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);
4128 /* do not use PLAYING mask for fading out from main screen */
4129 game_status = GAME_MODE_MAIN;
4134 if (!game.restart_level)
4135 CloseDoor(DOOR_CLOSE_1);
4138 if (level_editor_test_game)
4139 FadeSkipNextFadeIn();
4141 FadeSetEnterScreen();
4143 if (level_editor_test_game)
4144 fading = fading_none;
4146 fading = menu.destination;
4150 FadeOut(REDRAW_FIELD);
4153 FadeOut(REDRAW_FIELD);
4157 game_status = GAME_MODE_PLAYING;
4160 /* !!! FIX THIS (START) !!! */
4161 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
4163 InitGameEngine_EM();
4165 /* blit playfield from scroll buffer to normal back buffer for fading in */
4166 BlitScreenToBitmap_EM(backbuffer);
4173 /* after drawing the level, correct some elements */
4174 if (game.timegate_time_left == 0)
4175 CloseAllOpenTimegates();
4177 /* blit playfield from scroll buffer to normal back buffer for fading in */
4178 if (setup.soft_scrolling)
4179 BlitBitmap(fieldbuffer, backbuffer, FX, FY, SXSIZE, SYSIZE, SX, SY);
4181 redraw_mask |= REDRAW_FROM_BACKBUFFER;
4183 /* !!! FIX THIS (END) !!! */
4186 FadeIn(REDRAW_FIELD);
4189 FadeIn(REDRAW_FIELD);
4194 if (!game.restart_level)
4196 /* copy default game door content to main double buffer */
4197 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
4198 DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
4201 SetPanelBackground();
4202 SetDrawBackgroundMask(REDRAW_DOOR_1);
4204 UpdateGameDoorValues();
4205 DrawGameDoorValues();
4207 if (!game.restart_level)
4211 game_gadget[SOUND_CTRL_ID_MUSIC]->checked = setup.sound_music;
4212 game_gadget[SOUND_CTRL_ID_LOOPS]->checked = setup.sound_loops;
4213 game_gadget[SOUND_CTRL_ID_SIMPLE]->checked = setup.sound_simple;
4217 /* copy actual game door content to door double buffer for OpenDoor() */
4218 BlitBitmap(drawto, bitmap_db_door,
4219 DX, DY, DXSIZE, DYSIZE, DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1);
4221 OpenDoor(DOOR_OPEN_ALL);
4223 PlaySound(SND_GAME_STARTING);
4225 if (setup.sound_music)
4228 KeyboardAutoRepeatOffUnlessAutoplay();
4232 for (i = 0; i < MAX_PLAYERS; i++)
4233 printf("Player %d %sactive.\n",
4234 i + 1, (stored_player[i].active ? "" : "not "));
4245 game.restart_level = FALSE;
4248 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y)
4250 /* this is used for non-R'n'D game engines to update certain engine values */
4252 /* needed to determine if sounds are played within the visible screen area */
4253 scroll_x = actual_scroll_x;
4254 scroll_y = actual_scroll_y;
4257 void InitMovDir(int x, int y)
4259 int i, element = Feld[x][y];
4260 static int xy[4][2] =
4267 static int direction[3][4] =
4269 { MV_RIGHT, MV_UP, MV_LEFT, MV_DOWN },
4270 { MV_LEFT, MV_DOWN, MV_RIGHT, MV_UP },
4271 { MV_LEFT, MV_RIGHT, MV_UP, MV_DOWN }
4280 Feld[x][y] = EL_BUG;
4281 MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
4284 case EL_SPACESHIP_RIGHT:
4285 case EL_SPACESHIP_UP:
4286 case EL_SPACESHIP_LEFT:
4287 case EL_SPACESHIP_DOWN:
4288 Feld[x][y] = EL_SPACESHIP;
4289 MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
4292 case EL_BD_BUTTERFLY_RIGHT:
4293 case EL_BD_BUTTERFLY_UP:
4294 case EL_BD_BUTTERFLY_LEFT:
4295 case EL_BD_BUTTERFLY_DOWN:
4296 Feld[x][y] = EL_BD_BUTTERFLY;
4297 MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
4300 case EL_BD_FIREFLY_RIGHT:
4301 case EL_BD_FIREFLY_UP:
4302 case EL_BD_FIREFLY_LEFT:
4303 case EL_BD_FIREFLY_DOWN:
4304 Feld[x][y] = EL_BD_FIREFLY;
4305 MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
4308 case EL_PACMAN_RIGHT:
4310 case EL_PACMAN_LEFT:
4311 case EL_PACMAN_DOWN:
4312 Feld[x][y] = EL_PACMAN;
4313 MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
4316 case EL_YAMYAM_LEFT:
4317 case EL_YAMYAM_RIGHT:
4319 case EL_YAMYAM_DOWN:
4320 Feld[x][y] = EL_YAMYAM;
4321 MovDir[x][y] = direction[2][element - EL_YAMYAM_LEFT];
4324 case EL_SP_SNIKSNAK:
4325 MovDir[x][y] = MV_UP;
4328 case EL_SP_ELECTRON:
4329 MovDir[x][y] = MV_LEFT;
4336 Feld[x][y] = EL_MOLE;
4337 MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
4341 if (IS_CUSTOM_ELEMENT(element))
4343 struct ElementInfo *ei = &element_info[element];
4344 int move_direction_initial = ei->move_direction_initial;
4345 int move_pattern = ei->move_pattern;
4347 if (move_direction_initial == MV_START_PREVIOUS)
4349 if (MovDir[x][y] != MV_NONE)
4352 move_direction_initial = MV_START_AUTOMATIC;
4355 if (move_direction_initial == MV_START_RANDOM)
4356 MovDir[x][y] = 1 << RND(4);
4357 else if (move_direction_initial & MV_ANY_DIRECTION)
4358 MovDir[x][y] = move_direction_initial;
4359 else if (move_pattern == MV_ALL_DIRECTIONS ||
4360 move_pattern == MV_TURNING_LEFT ||
4361 move_pattern == MV_TURNING_RIGHT ||
4362 move_pattern == MV_TURNING_LEFT_RIGHT ||
4363 move_pattern == MV_TURNING_RIGHT_LEFT ||
4364 move_pattern == MV_TURNING_RANDOM)
4365 MovDir[x][y] = 1 << RND(4);
4366 else if (move_pattern == MV_HORIZONTAL)
4367 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
4368 else if (move_pattern == MV_VERTICAL)
4369 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
4370 else if (move_pattern & MV_ANY_DIRECTION)
4371 MovDir[x][y] = element_info[element].move_pattern;
4372 else if (move_pattern == MV_ALONG_LEFT_SIDE ||
4373 move_pattern == MV_ALONG_RIGHT_SIDE)
4375 /* use random direction as default start direction */
4376 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
4377 MovDir[x][y] = 1 << RND(4);
4379 for (i = 0; i < NUM_DIRECTIONS; i++)
4381 int x1 = x + xy[i][0];
4382 int y1 = y + xy[i][1];
4384 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4386 if (move_pattern == MV_ALONG_RIGHT_SIDE)
4387 MovDir[x][y] = direction[0][i];
4389 MovDir[x][y] = direction[1][i];
4398 MovDir[x][y] = 1 << RND(4);
4400 if (element != EL_BUG &&
4401 element != EL_SPACESHIP &&
4402 element != EL_BD_BUTTERFLY &&
4403 element != EL_BD_FIREFLY)
4406 for (i = 0; i < NUM_DIRECTIONS; i++)
4408 int x1 = x + xy[i][0];
4409 int y1 = y + xy[i][1];
4411 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4413 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
4415 MovDir[x][y] = direction[0][i];
4418 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
4419 element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4421 MovDir[x][y] = direction[1][i];
4430 GfxDir[x][y] = MovDir[x][y];
4433 void InitAmoebaNr(int x, int y)
4436 int group_nr = AmoebeNachbarNr(x, y);
4440 for (i = 1; i < MAX_NUM_AMOEBA; i++)
4442 if (AmoebaCnt[i] == 0)
4450 AmoebaNr[x][y] = group_nr;
4451 AmoebaCnt[group_nr]++;
4452 AmoebaCnt2[group_nr]++;
4455 static void PlayerWins(struct PlayerInfo *player)
4457 player->LevelSolved = TRUE;
4458 player->GameOver = TRUE;
4460 player->score_final = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
4461 level.native_em_level->lev->score : player->score);
4463 player->LevelSolved_CountingTime = (level.time == 0 ? TimePlayed : TimeLeft);
4464 player->LevelSolved_CountingScore = player->score_final;
4469 static int time, time_final;
4470 static int score, score_final;
4471 static int game_over_delay_1 = 0;
4472 static int game_over_delay_2 = 0;
4473 int game_over_delay_value_1 = 50;
4474 int game_over_delay_value_2 = 50;
4476 if (!local_player->LevelSolved_GameWon)
4480 /* do not start end game actions before the player stops moving (to exit) */
4481 if (local_player->MovPos)
4484 local_player->LevelSolved_GameWon = TRUE;
4485 local_player->LevelSolved_SaveTape = tape.recording;
4486 local_player->LevelSolved_SaveScore = !tape.playing;
4488 if (tape.auto_play) /* tape might already be stopped here */
4489 tape.auto_play_level_solved = TRUE;
4495 game_over_delay_1 = game_over_delay_value_1;
4496 game_over_delay_2 = game_over_delay_value_2;
4498 time = time_final = (level.time == 0 ? TimePlayed : TimeLeft);
4499 score = score_final = local_player->score_final;
4504 score_final += TimeLeft * level.score[SC_TIME_BONUS];
4506 else if (level.time == 0 && TimePlayed < 999)
4509 score_final += (999 - TimePlayed) * level.score[SC_TIME_BONUS];
4512 local_player->score_final = score_final;
4514 if (level_editor_test_game)
4517 score = score_final;
4520 local_player->LevelSolved_CountingTime = time;
4521 local_player->LevelSolved_CountingScore = score;
4523 game_panel_controls[GAME_PANEL_TIME].value = time;
4524 game_panel_controls[GAME_PANEL_SCORE].value = score;
4526 DisplayGameControlValues();
4528 DrawGameValue_Time(time);
4529 DrawGameValue_Score(score);
4533 if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
4535 if (ExitX >= 0 && ExitY >= 0) /* local player has left the level */
4537 /* close exit door after last player */
4538 if ((AllPlayersGone &&
4539 (Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
4540 Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN ||
4541 Feld[ExitX][ExitY] == EL_STEEL_EXIT_OPEN)) ||
4542 Feld[ExitX][ExitY] == EL_EM_EXIT_OPEN ||
4543 Feld[ExitX][ExitY] == EL_EM_STEEL_EXIT_OPEN)
4545 int element = Feld[ExitX][ExitY];
4548 if (element == EL_EM_EXIT_OPEN ||
4549 element == EL_EM_STEEL_EXIT_OPEN)
4556 Feld[ExitX][ExitY] =
4557 (element == EL_EXIT_OPEN ? EL_EXIT_CLOSING :
4558 element == EL_EM_EXIT_OPEN ? EL_EM_EXIT_CLOSING :
4559 element == EL_SP_EXIT_OPEN ? EL_SP_EXIT_CLOSING:
4560 element == EL_STEEL_EXIT_OPEN ? EL_STEEL_EXIT_CLOSING:
4561 EL_EM_STEEL_EXIT_CLOSING);
4563 PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING);
4567 /* player disappears */
4568 DrawLevelField(ExitX, ExitY);
4571 for (i = 0; i < MAX_PLAYERS; i++)
4573 struct PlayerInfo *player = &stored_player[i];
4575 if (player->present)
4577 RemovePlayer(player);
4579 /* player disappears */
4580 DrawLevelField(player->jx, player->jy);
4585 PlaySound(SND_GAME_WINNING);
4588 if (game_over_delay_1 > 0)
4590 game_over_delay_1--;
4595 if (time != time_final)
4597 int time_to_go = ABS(time_final - time);
4598 int time_count_dir = (time < time_final ? +1 : -1);
4599 int time_count_steps = (time_to_go > 100 && time_to_go % 10 == 0 ? 10 : 1);
4601 time += time_count_steps * time_count_dir;
4602 score += time_count_steps * level.score[SC_TIME_BONUS];
4605 local_player->LevelSolved_CountingTime = time;
4606 local_player->LevelSolved_CountingScore = score;
4608 game_panel_controls[GAME_PANEL_TIME].value = time;
4609 game_panel_controls[GAME_PANEL_SCORE].value = score;
4611 DisplayGameControlValues();
4613 DrawGameValue_Time(time);
4614 DrawGameValue_Score(score);
4617 if (time == time_final)
4618 StopSound(SND_GAME_LEVELTIME_BONUS);
4619 else if (setup.sound_loops)
4620 PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4622 PlaySound(SND_GAME_LEVELTIME_BONUS);
4627 local_player->LevelSolved_PanelOff = TRUE;
4629 if (game_over_delay_2 > 0)
4631 game_over_delay_2--;
4644 boolean raise_level = FALSE;
4646 local_player->LevelSolved_GameEnd = TRUE;
4648 CloseDoor(DOOR_CLOSE_1);
4650 if (local_player->LevelSolved_SaveTape)
4657 SaveTapeChecked(tape.level_nr); /* ask to save tape */
4659 SaveTape(tape.level_nr); /* ask to save tape */
4663 if (level_editor_test_game)
4665 game_status = GAME_MODE_MAIN;
4668 DrawAndFadeInMainMenu(REDRAW_FIELD);
4676 if (!local_player->LevelSolved_SaveScore)
4679 FadeOut(REDRAW_FIELD);
4682 game_status = GAME_MODE_MAIN;
4684 DrawAndFadeInMainMenu(REDRAW_FIELD);
4689 if (level_nr == leveldir_current->handicap_level)
4691 leveldir_current->handicap_level++;
4692 SaveLevelSetup_SeriesInfo();
4695 if (level_nr < leveldir_current->last_level)
4696 raise_level = TRUE; /* advance to next level */
4698 if ((hi_pos = NewHiScore()) >= 0)
4700 game_status = GAME_MODE_SCORES;
4702 DrawHallOfFame(hi_pos);
4713 FadeOut(REDRAW_FIELD);
4716 game_status = GAME_MODE_MAIN;
4724 DrawAndFadeInMainMenu(REDRAW_FIELD);
4733 LoadScore(level_nr);
4735 if (strEqual(setup.player_name, EMPTY_PLAYER_NAME) ||
4736 local_player->score_final < highscore[MAX_SCORE_ENTRIES - 1].Score)
4739 for (k = 0; k < MAX_SCORE_ENTRIES; k++)
4741 if (local_player->score_final > highscore[k].Score)
4743 /* player has made it to the hall of fame */
4745 if (k < MAX_SCORE_ENTRIES - 1)
4747 int m = MAX_SCORE_ENTRIES - 1;
4750 for (l = k; l < MAX_SCORE_ENTRIES; l++)
4751 if (strEqual(setup.player_name, highscore[l].Name))
4753 if (m == k) /* player's new highscore overwrites his old one */
4757 for (l = m; l > k; l--)
4759 strcpy(highscore[l].Name, highscore[l - 1].Name);
4760 highscore[l].Score = highscore[l - 1].Score;
4767 strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
4768 highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
4769 highscore[k].Score = local_player->score_final;
4775 else if (!strncmp(setup.player_name, highscore[k].Name,
4776 MAX_PLAYER_NAME_LEN))
4777 break; /* player already there with a higher score */
4783 SaveScore(level_nr);
4788 inline static int getElementMoveStepsizeExt(int x, int y, int direction)
4790 int element = Feld[x][y];
4791 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
4792 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
4793 int horiz_move = (dx != 0);
4794 int sign = (horiz_move ? dx : dy);
4795 int step = sign * element_info[element].move_stepsize;
4797 /* special values for move stepsize for spring and things on conveyor belt */
4800 if (CAN_FALL(element) &&
4801 y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
4802 step = sign * MOVE_STEPSIZE_NORMAL / 2;
4803 else if (element == EL_SPRING)
4804 step = sign * MOVE_STEPSIZE_NORMAL * 2;
4810 inline static int getElementMoveStepsize(int x, int y)
4812 return getElementMoveStepsizeExt(x, y, MovDir[x][y]);
4815 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
4817 if (player->GfxAction != action || player->GfxDir != dir)
4820 printf("Player frame reset! (%d => %d, %d => %d)\n",
4821 player->GfxAction, action, player->GfxDir, dir);
4824 player->GfxAction = action;
4825 player->GfxDir = dir;
4827 player->StepFrame = 0;
4831 #if USE_GFX_RESET_GFX_ANIMATION
4832 static void ResetGfxFrame(int x, int y, boolean redraw)
4834 int element = Feld[x][y];
4835 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
4836 int last_gfx_frame = GfxFrame[x][y];
4838 if (graphic_info[graphic].anim_global_sync)
4839 GfxFrame[x][y] = FrameCounter;
4840 else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
4841 GfxFrame[x][y] = CustomValue[x][y];
4842 else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
4843 GfxFrame[x][y] = element_info[element].collect_score;
4844 else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
4845 GfxFrame[x][y] = ChangeDelay[x][y];
4847 if (redraw && GfxFrame[x][y] != last_gfx_frame)
4848 DrawLevelGraphicAnimation(x, y, graphic);
4852 static void ResetGfxAnimation(int x, int y)
4854 GfxAction[x][y] = ACTION_DEFAULT;
4855 GfxDir[x][y] = MovDir[x][y];
4858 #if USE_GFX_RESET_GFX_ANIMATION
4859 ResetGfxFrame(x, y, FALSE);
4863 static void ResetRandomAnimationValue(int x, int y)
4865 GfxRandom[x][y] = INIT_GFX_RANDOM();
4868 void InitMovingField(int x, int y, int direction)
4870 int element = Feld[x][y];
4871 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
4872 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
4875 boolean is_moving_before, is_moving_after;
4877 boolean continues_moving = (WasJustMoving[x][y] && direction == MovDir[x][y]);
4880 /* check if element was/is moving or being moved before/after mode change */
4883 is_moving_before = (WasJustMoving[x][y] != 0);
4885 /* (!!! this does not work -- WasJustMoving is NOT a boolean value !!!) */
4886 is_moving_before = WasJustMoving[x][y];
4889 is_moving_before = (getElementMoveStepsizeExt(x, y, MovDir[x][y]) != 0);
4891 is_moving_after = (getElementMoveStepsizeExt(x, y, direction) != 0);
4893 /* reset animation only for moving elements which change direction of moving
4894 or which just started or stopped moving
4895 (else CEs with property "can move" / "not moving" are reset each frame) */
4896 #if USE_GFX_RESET_ONLY_WHEN_MOVING
4898 if (is_moving_before != is_moving_after ||
4899 direction != MovDir[x][y])
4900 ResetGfxAnimation(x, y);
4902 if ((is_moving_before || is_moving_after) && !continues_moving)
4903 ResetGfxAnimation(x, y);
4906 if (!continues_moving)
4907 ResetGfxAnimation(x, y);
4910 MovDir[x][y] = direction;
4911 GfxDir[x][y] = direction;
4913 #if USE_GFX_RESET_ONLY_WHEN_MOVING
4914 GfxAction[x][y] = (!is_moving_after ? ACTION_WAITING :
4915 direction == MV_DOWN && CAN_FALL(element) ?
4916 ACTION_FALLING : ACTION_MOVING);
4918 GfxAction[x][y] = (direction == MV_DOWN && CAN_FALL(element) ?
4919 ACTION_FALLING : ACTION_MOVING);
4922 /* this is needed for CEs with property "can move" / "not moving" */
4924 if (is_moving_after)
4926 if (Feld[newx][newy] == EL_EMPTY)
4927 Feld[newx][newy] = EL_BLOCKED;
4929 MovDir[newx][newy] = MovDir[x][y];
4931 #if USE_NEW_CUSTOM_VALUE
4932 CustomValue[newx][newy] = CustomValue[x][y];
4935 GfxFrame[newx][newy] = GfxFrame[x][y];
4936 GfxRandom[newx][newy] = GfxRandom[x][y];
4937 GfxAction[newx][newy] = GfxAction[x][y];
4938 GfxDir[newx][newy] = GfxDir[x][y];
4942 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
4944 int direction = MovDir[x][y];
4945 int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0);
4946 int newy = y + (direction & MV_UP ? -1 : direction & MV_DOWN ? +1 : 0);
4952 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
4954 int oldx = x, oldy = y;
4955 int direction = MovDir[x][y];
4957 if (direction == MV_LEFT)
4959 else if (direction == MV_RIGHT)
4961 else if (direction == MV_UP)
4963 else if (direction == MV_DOWN)
4966 *comes_from_x = oldx;
4967 *comes_from_y = oldy;
4970 int MovingOrBlocked2Element(int x, int y)
4972 int element = Feld[x][y];
4974 if (element == EL_BLOCKED)
4978 Blocked2Moving(x, y, &oldx, &oldy);
4979 return Feld[oldx][oldy];
4985 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
4987 /* like MovingOrBlocked2Element(), but if element is moving
4988 and (x,y) is the field the moving element is just leaving,
4989 return EL_BLOCKED instead of the element value */
4990 int element = Feld[x][y];
4992 if (IS_MOVING(x, y))
4994 if (element == EL_BLOCKED)
4998 Blocked2Moving(x, y, &oldx, &oldy);
4999 return Feld[oldx][oldy];
5008 static void RemoveField(int x, int y)
5010 Feld[x][y] = EL_EMPTY;
5016 #if USE_NEW_CUSTOM_VALUE
5017 CustomValue[x][y] = 0;
5021 ChangeDelay[x][y] = 0;
5022 ChangePage[x][y] = -1;
5023 Pushed[x][y] = FALSE;
5026 ExplodeField[x][y] = EX_TYPE_NONE;
5029 GfxElement[x][y] = EL_UNDEFINED;
5030 GfxAction[x][y] = ACTION_DEFAULT;
5031 GfxDir[x][y] = MV_NONE;
5034 void RemoveMovingField(int x, int y)
5036 int oldx = x, oldy = y, newx = x, newy = y;
5037 int element = Feld[x][y];
5038 int next_element = EL_UNDEFINED;
5040 if (element != EL_BLOCKED && !IS_MOVING(x, y))
5043 if (IS_MOVING(x, y))
5045 Moving2Blocked(x, y, &newx, &newy);
5047 if (Feld[newx][newy] != EL_BLOCKED)
5049 /* element is moving, but target field is not free (blocked), but
5050 already occupied by something different (example: acid pool);
5051 in this case, only remove the moving field, but not the target */
5053 RemoveField(oldx, oldy);
5055 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5057 DrawLevelField(oldx, oldy);
5062 else if (element == EL_BLOCKED)
5064 Blocked2Moving(x, y, &oldx, &oldy);
5065 if (!IS_MOVING(oldx, oldy))
5069 if (element == EL_BLOCKED &&
5070 (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
5071 Feld[oldx][oldy] == EL_QUICKSAND_FAST_EMPTYING ||
5072 Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
5073 Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
5074 Feld[oldx][oldy] == EL_DC_MAGIC_WALL_EMPTYING ||
5075 Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
5076 next_element = get_next_element(Feld[oldx][oldy]);
5078 RemoveField(oldx, oldy);
5079 RemoveField(newx, newy);
5081 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5083 if (next_element != EL_UNDEFINED)
5084 Feld[oldx][oldy] = next_element;
5086 DrawLevelField(oldx, oldy);
5087 DrawLevelField(newx, newy);
5090 void DrawDynamite(int x, int y)
5092 int sx = SCREENX(x), sy = SCREENY(y);
5093 int graphic = el2img(Feld[x][y]);
5096 if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
5099 if (IS_WALKABLE_INSIDE(Back[x][y]))
5103 DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
5104 else if (Store[x][y])
5105 DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
5107 frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5109 if (Back[x][y] || Store[x][y])
5110 DrawGraphicThruMask(sx, sy, graphic, frame);
5112 DrawGraphic(sx, sy, graphic, frame);
5115 void CheckDynamite(int x, int y)
5117 if (MovDelay[x][y] != 0) /* dynamite is still waiting to explode */
5121 if (MovDelay[x][y] != 0)
5124 PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5130 StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5135 static void setMinimalPlayerBoundaries(int *sx1, int *sy1, int *sx2, int *sy2)
5137 boolean num_checked_players = 0;
5140 for (i = 0; i < MAX_PLAYERS; i++)
5142 if (stored_player[i].active)
5144 int sx = stored_player[i].jx;
5145 int sy = stored_player[i].jy;
5147 if (num_checked_players == 0)
5154 *sx1 = MIN(*sx1, sx);
5155 *sy1 = MIN(*sy1, sy);
5156 *sx2 = MAX(*sx2, sx);
5157 *sy2 = MAX(*sy2, sy);
5160 num_checked_players++;
5165 static boolean checkIfAllPlayersFitToScreen_RND()
5167 int sx1 = 0, sy1 = 0, sx2 = 0, sy2 = 0;
5169 setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5171 return (sx2 - sx1 < SCR_FIELDX &&
5172 sy2 - sy1 < SCR_FIELDY);
5175 static void setScreenCenteredToAllPlayers(int *sx, int *sy)
5177 int sx1 = scroll_x, sy1 = scroll_y, sx2 = scroll_x, sy2 = scroll_y;
5179 setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5181 *sx = (sx1 + sx2) / 2;
5182 *sy = (sy1 + sy2) / 2;
5185 void DrawRelocateScreen(int old_x, int old_y, int x, int y, int move_dir,
5186 boolean center_screen, boolean quick_relocation)
5188 boolean ffwd_delay = (tape.playing && tape.fast_forward);
5189 boolean no_delay = (tape.warp_forward);
5190 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5191 int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5193 if (quick_relocation)
5195 int offset = game.scroll_delay_value;
5197 if (!IN_VIS_FIELD(SCREENX(x), SCREENY(y)) || center_screen)
5199 if (!level.shifted_relocation || center_screen)
5201 /* quick relocation (without scrolling), with centering of screen */
5203 scroll_x = (x < SBX_Left + MIDPOSX ? SBX_Left :
5204 x > SBX_Right + MIDPOSX ? SBX_Right :
5207 scroll_y = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
5208 y > SBY_Lower + MIDPOSY ? SBY_Lower :
5213 /* quick relocation (without scrolling), but do not center screen */
5215 int center_scroll_x = (old_x < SBX_Left + MIDPOSX ? SBX_Left :
5216 old_x > SBX_Right + MIDPOSX ? SBX_Right :
5219 int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5220 old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5223 int offset_x = x + (scroll_x - center_scroll_x);
5224 int offset_y = y + (scroll_y - center_scroll_y);
5226 scroll_x = (offset_x < SBX_Left + MIDPOSX ? SBX_Left :
5227 offset_x > SBX_Right + MIDPOSX ? SBX_Right :
5228 offset_x - MIDPOSX);
5230 scroll_y = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5231 offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5232 offset_y - MIDPOSY);
5237 /* quick relocation (without scrolling), inside visible screen area */
5239 if ((move_dir == MV_LEFT && scroll_x > x - MIDPOSX + offset) ||
5240 (move_dir == MV_RIGHT && scroll_x < x - MIDPOSX - offset))
5241 scroll_x = x - MIDPOSX + (scroll_x < x - MIDPOSX ? -offset : +offset);
5243 if ((move_dir == MV_UP && scroll_y > y - MIDPOSY + offset) ||
5244 (move_dir == MV_DOWN && scroll_y < y - MIDPOSY - offset))
5245 scroll_y = y - MIDPOSY + (scroll_y < y - MIDPOSY ? -offset : +offset);
5247 /* don't scroll over playfield boundaries */
5248 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
5249 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
5251 /* don't scroll over playfield boundaries */
5252 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
5253 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
5256 RedrawPlayfield(TRUE, 0,0,0,0);
5261 int scroll_xx, scroll_yy;
5263 if (!level.shifted_relocation || center_screen)
5265 /* visible relocation (with scrolling), with centering of screen */
5267 scroll_xx = (x < SBX_Left + MIDPOSX ? SBX_Left :
5268 x > SBX_Right + MIDPOSX ? SBX_Right :
5271 scroll_yy = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
5272 y > SBY_Lower + MIDPOSY ? SBY_Lower :
5277 /* visible relocation (with scrolling), but do not center screen */
5279 int center_scroll_x = (old_x < SBX_Left + MIDPOSX ? SBX_Left :
5280 old_x > SBX_Right + MIDPOSX ? SBX_Right :
5283 int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5284 old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5287 int offset_x = x + (scroll_x - center_scroll_x);
5288 int offset_y = y + (scroll_y - center_scroll_y);
5290 scroll_xx = (offset_x < SBX_Left + MIDPOSX ? SBX_Left :
5291 offset_x > SBX_Right + MIDPOSX ? SBX_Right :
5292 offset_x - MIDPOSX);
5294 scroll_yy = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5295 offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5296 offset_y - MIDPOSY);
5301 /* visible relocation (with scrolling), with centering of screen */
5303 int scroll_xx = (x < SBX_Left + MIDPOSX ? SBX_Left :
5304 x > SBX_Right + MIDPOSX ? SBX_Right :
5307 int scroll_yy = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
5308 y > SBY_Lower + MIDPOSY ? SBY_Lower :
5312 ScrollScreen(NULL, SCROLL_GO_ON); /* scroll last frame to full tile */
5314 while (scroll_x != scroll_xx || scroll_y != scroll_yy)
5317 int fx = FX, fy = FY;
5319 dx = (scroll_xx < scroll_x ? +1 : scroll_xx > scroll_x ? -1 : 0);
5320 dy = (scroll_yy < scroll_y ? +1 : scroll_yy > scroll_y ? -1 : 0);
5322 if (dx == 0 && dy == 0) /* no scrolling needed at all */
5328 fx += dx * TILEX / 2;
5329 fy += dy * TILEY / 2;
5331 ScrollLevel(dx, dy);
5334 /* scroll in two steps of half tile size to make things smoother */
5335 BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
5337 Delay(wait_delay_value);
5339 /* scroll second step to align at full tile size */
5341 Delay(wait_delay_value);
5346 Delay(wait_delay_value);
5350 void RelocatePlayer(int jx, int jy, int el_player_raw)
5352 int el_player = GET_PLAYER_ELEMENT(el_player_raw);
5353 int player_nr = GET_PLAYER_NR(el_player);
5354 struct PlayerInfo *player = &stored_player[player_nr];
5355 boolean ffwd_delay = (tape.playing && tape.fast_forward);
5356 boolean no_delay = (tape.warp_forward);
5357 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5358 int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5359 int old_jx = player->jx;
5360 int old_jy = player->jy;
5361 int old_element = Feld[old_jx][old_jy];
5362 int element = Feld[jx][jy];
5363 boolean player_relocated = (old_jx != jx || old_jy != jy);
5365 int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
5366 int move_dir_vert = (jy < old_jy ? MV_UP : jy > old_jy ? MV_DOWN : 0);
5367 int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
5368 int enter_side_vert = MV_DIR_OPPOSITE(move_dir_vert);
5369 int leave_side_horiz = move_dir_horiz;
5370 int leave_side_vert = move_dir_vert;
5371 int enter_side = enter_side_horiz | enter_side_vert;
5372 int leave_side = leave_side_horiz | leave_side_vert;
5374 if (player->GameOver) /* do not reanimate dead player */
5377 if (!player_relocated) /* no need to relocate the player */
5380 if (IS_PLAYER(jx, jy)) /* player already placed at new position */
5382 RemoveField(jx, jy); /* temporarily remove newly placed player */
5383 DrawLevelField(jx, jy);
5386 if (player->present)
5388 while (player->MovPos)
5390 ScrollPlayer(player, SCROLL_GO_ON);
5391 ScrollScreen(NULL, SCROLL_GO_ON);
5393 AdvanceFrameAndPlayerCounters(player->index_nr);
5398 Delay(wait_delay_value);
5401 DrawPlayer(player); /* needed here only to cleanup last field */
5402 DrawLevelField(player->jx, player->jy); /* remove player graphic */
5404 player->is_moving = FALSE;
5407 if (IS_CUSTOM_ELEMENT(old_element))
5408 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
5410 player->index_bit, leave_side);
5412 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
5414 player->index_bit, leave_side);
5416 Feld[jx][jy] = el_player;
5417 InitPlayerField(jx, jy, el_player, TRUE);
5419 if (!ELEM_IS_PLAYER(element)) /* player may be set on walkable element */
5421 Feld[jx][jy] = element;
5422 InitField(jx, jy, FALSE);
5425 /* only visually relocate centered player */
5426 DrawRelocateScreen(old_jx, old_jy, player->jx, player->jy, player->MovDir,
5427 FALSE, level.instant_relocation);
5429 TestIfPlayerTouchesBadThing(jx, jy);
5430 TestIfPlayerTouchesCustomElement(jx, jy);
5432 if (IS_CUSTOM_ELEMENT(element))
5433 CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
5434 player->index_bit, enter_side);
5436 CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
5437 player->index_bit, enter_side);
5440 void Explode(int ex, int ey, int phase, int mode)
5446 /* !!! eliminate this variable !!! */
5447 int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
5449 if (game.explosions_delayed)
5451 ExplodeField[ex][ey] = mode;
5455 if (phase == EX_PHASE_START) /* initialize 'Store[][]' field */
5457 int center_element = Feld[ex][ey];
5458 int artwork_element, explosion_element; /* set these values later */
5461 /* --- This is only really needed (and now handled) in "Impact()". --- */
5462 /* do not explode moving elements that left the explode field in time */
5463 if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
5464 center_element == EL_EMPTY &&
5465 (mode == EX_TYPE_NORMAL || mode == EX_TYPE_CENTER))
5470 /* !!! at this place, the center element may be EL_BLOCKED !!! */
5471 if (mode == EX_TYPE_NORMAL ||
5472 mode == EX_TYPE_CENTER ||
5473 mode == EX_TYPE_CROSS)
5474 PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
5477 /* remove things displayed in background while burning dynamite */
5478 if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
5481 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
5483 /* put moving element to center field (and let it explode there) */
5484 center_element = MovingOrBlocked2Element(ex, ey);
5485 RemoveMovingField(ex, ey);
5486 Feld[ex][ey] = center_element;
5489 /* now "center_element" is finally determined -- set related values now */
5490 artwork_element = center_element; /* for custom player artwork */
5491 explosion_element = center_element; /* for custom player artwork */
5493 if (IS_PLAYER(ex, ey))
5495 int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
5497 artwork_element = stored_player[player_nr].artwork_element;
5499 if (level.use_explosion_element[player_nr])
5501 explosion_element = level.explosion_element[player_nr];
5502 artwork_element = explosion_element;
5507 if (mode == EX_TYPE_NORMAL ||
5508 mode == EX_TYPE_CENTER ||
5509 mode == EX_TYPE_CROSS)
5510 PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
5513 last_phase = element_info[explosion_element].explosion_delay + 1;
5515 for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
5517 int xx = x - ex + 1;
5518 int yy = y - ey + 1;
5521 if (!IN_LEV_FIELD(x, y) ||
5522 (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
5523 (mode == EX_TYPE_CROSS && (x != ex && y != ey)))
5526 element = Feld[x][y];
5528 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
5530 element = MovingOrBlocked2Element(x, y);
5532 if (!IS_EXPLOSION_PROOF(element))
5533 RemoveMovingField(x, y);
5536 /* indestructible elements can only explode in center (but not flames) */
5537 if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
5538 mode == EX_TYPE_BORDER)) ||
5539 element == EL_FLAMES)
5542 /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
5543 behaviour, for example when touching a yamyam that explodes to rocks
5544 with active deadly shield, a rock is created under the player !!! */
5545 /* (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8) */
5547 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
5548 (game.engine_version < VERSION_IDENT(3,1,0,0) ||
5549 (x == ex && y == ey && mode != EX_TYPE_BORDER)))
5551 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
5554 if (IS_ACTIVE_BOMB(element))
5556 /* re-activate things under the bomb like gate or penguin */
5557 Feld[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
5564 /* save walkable background elements while explosion on same tile */
5565 if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
5566 (x != ex || y != ey || mode == EX_TYPE_BORDER))
5567 Back[x][y] = element;
5569 /* ignite explodable elements reached by other explosion */
5570 if (element == EL_EXPLOSION)
5571 element = Store2[x][y];
5573 if (AmoebaNr[x][y] &&
5574 (element == EL_AMOEBA_FULL ||
5575 element == EL_BD_AMOEBA ||
5576 element == EL_AMOEBA_GROWING))
5578 AmoebaCnt[AmoebaNr[x][y]]--;
5579 AmoebaCnt2[AmoebaNr[x][y]]--;
5584 if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
5586 int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
5588 Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
5590 if (PLAYERINFO(ex, ey)->use_murphy)
5591 Store[x][y] = EL_EMPTY;
5594 /* !!! check this case -- currently needed for rnd_rado_negundo_v,
5595 !!! levels 015 018 019 020 021 022 023 026 027 028 !!! */
5596 else if (ELEM_IS_PLAYER(center_element))
5597 Store[x][y] = EL_EMPTY;
5598 else if (center_element == EL_YAMYAM)
5599 Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
5600 else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
5601 Store[x][y] = element_info[center_element].content.e[xx][yy];
5603 /* needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
5604 (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
5605 otherwise) -- FIX THIS !!! */
5606 else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
5607 Store[x][y] = element_info[element].content.e[1][1];
5609 else if (!CAN_EXPLODE(element))
5610 Store[x][y] = element_info[element].content.e[1][1];
5613 Store[x][y] = EL_EMPTY;
5615 if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
5616 center_element == EL_AMOEBA_TO_DIAMOND)
5617 Store2[x][y] = element;
5619 Feld[x][y] = EL_EXPLOSION;
5620 GfxElement[x][y] = artwork_element;
5622 ExplodePhase[x][y] = 1;
5623 ExplodeDelay[x][y] = last_phase;
5628 if (center_element == EL_YAMYAM)
5629 game.yamyam_content_nr =
5630 (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
5642 GfxFrame[x][y] = 0; /* restart explosion animation */
5644 last_phase = ExplodeDelay[x][y];
5646 ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
5650 /* activate this even in non-DEBUG version until cause for crash in
5651 getGraphicAnimationFrame() (see below) is found and eliminated */
5657 /* this can happen if the player leaves an explosion just in time */
5658 if (GfxElement[x][y] == EL_UNDEFINED)
5659 GfxElement[x][y] = EL_EMPTY;
5661 if (GfxElement[x][y] == EL_UNDEFINED)
5664 printf("Explode(): x = %d, y = %d: GfxElement == EL_UNDEFINED\n", x, y);
5665 printf("Explode(): This should never happen!\n");
5668 GfxElement[x][y] = EL_EMPTY;
5674 border_element = Store2[x][y];
5675 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5676 border_element = StorePlayer[x][y];
5678 if (phase == element_info[border_element].ignition_delay ||
5679 phase == last_phase)
5681 boolean border_explosion = FALSE;
5683 if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
5684 !PLAYER_EXPLOSION_PROTECTED(x, y))
5686 KillPlayerUnlessExplosionProtected(x, y);
5687 border_explosion = TRUE;
5689 else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
5691 Feld[x][y] = Store2[x][y];
5694 border_explosion = TRUE;
5696 else if (border_element == EL_AMOEBA_TO_DIAMOND)
5698 AmoebeUmwandeln(x, y);
5700 border_explosion = TRUE;
5703 /* if an element just explodes due to another explosion (chain-reaction),
5704 do not immediately end the new explosion when it was the last frame of
5705 the explosion (as it would be done in the following "if"-statement!) */
5706 if (border_explosion && phase == last_phase)
5710 if (phase == last_phase)
5714 element = Feld[x][y] = Store[x][y];
5715 Store[x][y] = Store2[x][y] = 0;
5716 GfxElement[x][y] = EL_UNDEFINED;
5718 /* player can escape from explosions and might therefore be still alive */
5719 if (element >= EL_PLAYER_IS_EXPLODING_1 &&
5720 element <= EL_PLAYER_IS_EXPLODING_4)
5722 int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
5723 int explosion_element = EL_PLAYER_1 + player_nr;
5724 int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
5725 int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
5727 if (level.use_explosion_element[player_nr])
5728 explosion_element = level.explosion_element[player_nr];
5730 Feld[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
5731 element_info[explosion_element].content.e[xx][yy]);
5734 /* restore probably existing indestructible background element */
5735 if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
5736 element = Feld[x][y] = Back[x][y];
5739 MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
5740 GfxDir[x][y] = MV_NONE;
5741 ChangeDelay[x][y] = 0;
5742 ChangePage[x][y] = -1;
5744 #if USE_NEW_CUSTOM_VALUE
5745 CustomValue[x][y] = 0;
5748 InitField_WithBug2(x, y, FALSE);
5750 DrawLevelField(x, y);
5752 TestIfElementTouchesCustomElement(x, y);
5754 if (GFX_CRUMBLED(element))
5755 DrawLevelFieldCrumbledSandNeighbours(x, y);
5757 if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
5758 StorePlayer[x][y] = 0;
5760 if (ELEM_IS_PLAYER(element))
5761 RelocatePlayer(x, y, element);
5763 else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
5765 int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
5766 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5769 DrawLevelFieldCrumbledSand(x, y);
5771 if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
5773 DrawLevelElement(x, y, Back[x][y]);
5774 DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
5776 else if (IS_WALKABLE_UNDER(Back[x][y]))
5778 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5779 DrawLevelElementThruMask(x, y, Back[x][y]);
5781 else if (!IS_WALKABLE_INSIDE(Back[x][y]))
5782 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5786 void DynaExplode(int ex, int ey)
5789 int dynabomb_element = Feld[ex][ey];
5790 int dynabomb_size = 1;
5791 boolean dynabomb_xl = FALSE;
5792 struct PlayerInfo *player;
5793 static int xy[4][2] =
5801 if (IS_ACTIVE_BOMB(dynabomb_element))
5803 player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
5804 dynabomb_size = player->dynabomb_size;
5805 dynabomb_xl = player->dynabomb_xl;
5806 player->dynabombs_left++;
5809 Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
5811 for (i = 0; i < NUM_DIRECTIONS; i++)
5813 for (j = 1; j <= dynabomb_size; j++)
5815 int x = ex + j * xy[i][0];
5816 int y = ey + j * xy[i][1];
5819 if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
5822 element = Feld[x][y];
5824 /* do not restart explosions of fields with active bombs */
5825 if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
5828 Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
5830 if (element != EL_EMPTY && element != EL_EXPLOSION &&
5831 !IS_DIGGABLE(element) && !dynabomb_xl)
5837 void Bang(int x, int y)
5839 int element = MovingOrBlocked2Element(x, y);
5840 int explosion_type = EX_TYPE_NORMAL;
5842 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5844 struct PlayerInfo *player = PLAYERINFO(x, y);
5846 element = Feld[x][y] = (player->use_murphy ? EL_SP_MURPHY :
5847 player->element_nr);
5849 if (level.use_explosion_element[player->index_nr])
5851 int explosion_element = level.explosion_element[player->index_nr];
5853 if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
5854 explosion_type = EX_TYPE_CROSS;
5855 else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
5856 explosion_type = EX_TYPE_CENTER;
5864 case EL_BD_BUTTERFLY:
5867 case EL_DARK_YAMYAM:
5871 RaiseScoreElement(element);
5874 case EL_DYNABOMB_PLAYER_1_ACTIVE:
5875 case EL_DYNABOMB_PLAYER_2_ACTIVE:
5876 case EL_DYNABOMB_PLAYER_3_ACTIVE:
5877 case EL_DYNABOMB_PLAYER_4_ACTIVE:
5878 case EL_DYNABOMB_INCREASE_NUMBER:
5879 case EL_DYNABOMB_INCREASE_SIZE:
5880 case EL_DYNABOMB_INCREASE_POWER:
5881 explosion_type = EX_TYPE_DYNA;
5884 case EL_DC_LANDMINE:
5886 case EL_EM_EXIT_OPEN:
5887 case EL_EM_STEEL_EXIT_OPEN:
5889 explosion_type = EX_TYPE_CENTER;
5894 case EL_LAMP_ACTIVE:
5895 case EL_AMOEBA_TO_DIAMOND:
5896 if (!IS_PLAYER(x, y)) /* penguin and player may be at same field */
5897 explosion_type = EX_TYPE_CENTER;
5901 if (element_info[element].explosion_type == EXPLODES_CROSS)
5902 explosion_type = EX_TYPE_CROSS;
5903 else if (element_info[element].explosion_type == EXPLODES_1X1)
5904 explosion_type = EX_TYPE_CENTER;
5908 if (explosion_type == EX_TYPE_DYNA)
5911 Explode(x, y, EX_PHASE_START, explosion_type);
5913 CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
5916 void SplashAcid(int x, int y)
5918 if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
5919 (!IN_LEV_FIELD(x - 1, y - 2) ||
5920 !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
5921 Feld[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
5923 if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
5924 (!IN_LEV_FIELD(x + 1, y - 2) ||
5925 !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
5926 Feld[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
5928 PlayLevelSound(x, y, SND_ACID_SPLASHING);
5931 static void InitBeltMovement()
5933 static int belt_base_element[4] =
5935 EL_CONVEYOR_BELT_1_LEFT,
5936 EL_CONVEYOR_BELT_2_LEFT,
5937 EL_CONVEYOR_BELT_3_LEFT,
5938 EL_CONVEYOR_BELT_4_LEFT
5940 static int belt_base_active_element[4] =
5942 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
5943 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
5944 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
5945 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
5950 /* set frame order for belt animation graphic according to belt direction */
5951 for (i = 0; i < NUM_BELTS; i++)
5955 for (j = 0; j < NUM_BELT_PARTS; j++)
5957 int element = belt_base_active_element[belt_nr] + j;
5958 int graphic_1 = el2img(element);
5959 int graphic_2 = el2panelimg(element);
5961 if (game.belt_dir[i] == MV_LEFT)
5963 graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
5964 graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
5968 graphic_info[graphic_1].anim_mode |= ANIM_REVERSE;
5969 graphic_info[graphic_2].anim_mode |= ANIM_REVERSE;
5974 SCAN_PLAYFIELD(x, y)
5976 int element = Feld[x][y];
5978 for (i = 0; i < NUM_BELTS; i++)
5980 if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
5982 int e_belt_nr = getBeltNrFromBeltElement(element);
5985 if (e_belt_nr == belt_nr)
5987 int belt_part = Feld[x][y] - belt_base_element[belt_nr];
5989 Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
5996 static void ToggleBeltSwitch(int x, int y)
5998 static int belt_base_element[4] =
6000 EL_CONVEYOR_BELT_1_LEFT,
6001 EL_CONVEYOR_BELT_2_LEFT,
6002 EL_CONVEYOR_BELT_3_LEFT,
6003 EL_CONVEYOR_BELT_4_LEFT
6005 static int belt_base_active_element[4] =
6007 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
6008 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
6009 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
6010 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
6012 static int belt_base_switch_element[4] =
6014 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
6015 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
6016 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
6017 EL_CONVEYOR_BELT_4_SWITCH_LEFT
6019 static int belt_move_dir[4] =
6027 int element = Feld[x][y];
6028 int belt_nr = getBeltNrFromBeltSwitchElement(element);
6029 int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
6030 int belt_dir = belt_move_dir[belt_dir_nr];
6033 if (!IS_BELT_SWITCH(element))
6036 game.belt_dir_nr[belt_nr] = belt_dir_nr;
6037 game.belt_dir[belt_nr] = belt_dir;
6039 if (belt_dir_nr == 3)
6042 /* set frame order for belt animation graphic according to belt direction */
6043 for (i = 0; i < NUM_BELT_PARTS; i++)
6045 int element = belt_base_active_element[belt_nr] + i;
6046 int graphic_1 = el2img(element);
6047 int graphic_2 = el2panelimg(element);
6049 if (belt_dir == MV_LEFT)
6051 graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
6052 graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
6056 graphic_info[graphic_1].anim_mode |= ANIM_REVERSE;
6057 graphic_info[graphic_2].anim_mode |= ANIM_REVERSE;
6061 SCAN_PLAYFIELD(xx, yy)
6063 int element = Feld[xx][yy];
6065 if (IS_BELT_SWITCH(element))
6067 int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
6069 if (e_belt_nr == belt_nr)
6071 Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
6072 DrawLevelField(xx, yy);
6075 else if (IS_BELT(element) && belt_dir != MV_NONE)
6077 int e_belt_nr = getBeltNrFromBeltElement(element);
6079 if (e_belt_nr == belt_nr)
6081 int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
6083 Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
6084 DrawLevelField(xx, yy);
6087 else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
6089 int e_belt_nr = getBeltNrFromBeltActiveElement(element);
6091 if (e_belt_nr == belt_nr)
6093 int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
6095 Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
6096 DrawLevelField(xx, yy);
6102 static void ToggleSwitchgateSwitch(int x, int y)
6106 game.switchgate_pos = !game.switchgate_pos;
6108 SCAN_PLAYFIELD(xx, yy)
6110 int element = Feld[xx][yy];
6112 #if !USE_BOTH_SWITCHGATE_SWITCHES
6113 if (element == EL_SWITCHGATE_SWITCH_UP ||
6114 element == EL_SWITCHGATE_SWITCH_DOWN)
6116 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
6117 DrawLevelField(xx, yy);
6119 else if (element == EL_DC_SWITCHGATE_SWITCH_UP ||
6120 element == EL_DC_SWITCHGATE_SWITCH_DOWN)
6122 Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
6123 DrawLevelField(xx, yy);
6126 if (element == EL_SWITCHGATE_SWITCH_UP)
6128 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_DOWN;
6129 DrawLevelField(xx, yy);
6131 else if (element == EL_SWITCHGATE_SWITCH_DOWN)
6133 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP;
6134 DrawLevelField(xx, yy);
6136 else if (element == EL_DC_SWITCHGATE_SWITCH_UP)
6138 Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_DOWN;
6139 DrawLevelField(xx, yy);
6141 else if (element == EL_DC_SWITCHGATE_SWITCH_DOWN)
6143 Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP;
6144 DrawLevelField(xx, yy);
6147 else if (element == EL_SWITCHGATE_OPEN ||
6148 element == EL_SWITCHGATE_OPENING)
6150 Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
6152 PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
6154 else if (element == EL_SWITCHGATE_CLOSED ||
6155 element == EL_SWITCHGATE_CLOSING)
6157 Feld[xx][yy] = EL_SWITCHGATE_OPENING;
6159 PlayLevelSoundAction(xx, yy, ACTION_OPENING);
6164 static int getInvisibleActiveFromInvisibleElement(int element)
6166 return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
6167 element == EL_INVISIBLE_WALL ? EL_INVISIBLE_WALL_ACTIVE :
6168 element == EL_INVISIBLE_SAND ? EL_INVISIBLE_SAND_ACTIVE :
6172 static int getInvisibleFromInvisibleActiveElement(int element)
6174 return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
6175 element == EL_INVISIBLE_WALL_ACTIVE ? EL_INVISIBLE_WALL :
6176 element == EL_INVISIBLE_SAND_ACTIVE ? EL_INVISIBLE_SAND :
6180 static void RedrawAllLightSwitchesAndInvisibleElements()
6184 SCAN_PLAYFIELD(x, y)
6186 int element = Feld[x][y];
6188 if (element == EL_LIGHT_SWITCH &&
6189 game.light_time_left > 0)
6191 Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
6192 DrawLevelField(x, y);
6194 else if (element == EL_LIGHT_SWITCH_ACTIVE &&
6195 game.light_time_left == 0)
6197 Feld[x][y] = EL_LIGHT_SWITCH;
6198 DrawLevelField(x, y);
6200 else if (element == EL_EMC_DRIPPER &&
6201 game.light_time_left > 0)
6203 Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
6204 DrawLevelField(x, y);
6206 else if (element == EL_EMC_DRIPPER_ACTIVE &&
6207 game.light_time_left == 0)
6209 Feld[x][y] = EL_EMC_DRIPPER;
6210 DrawLevelField(x, y);
6212 else if (element == EL_INVISIBLE_STEELWALL ||
6213 element == EL_INVISIBLE_WALL ||
6214 element == EL_INVISIBLE_SAND)
6216 if (game.light_time_left > 0)
6217 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
6219 DrawLevelField(x, y);
6221 /* uncrumble neighbour fields, if needed */
6222 if (element == EL_INVISIBLE_SAND)
6223 DrawLevelFieldCrumbledSandNeighbours(x, y);
6225 else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6226 element == EL_INVISIBLE_WALL_ACTIVE ||
6227 element == EL_INVISIBLE_SAND_ACTIVE)
6229 if (game.light_time_left == 0)
6230 Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
6232 DrawLevelField(x, y);
6234 /* re-crumble neighbour fields, if needed */
6235 if (element == EL_INVISIBLE_SAND)
6236 DrawLevelFieldCrumbledSandNeighbours(x, y);
6241 static void RedrawAllInvisibleElementsForLenses()
6245 SCAN_PLAYFIELD(x, y)
6247 int element = Feld[x][y];
6249 if (element == EL_EMC_DRIPPER &&
6250 game.lenses_time_left > 0)
6252 Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
6253 DrawLevelField(x, y);
6255 else if (element == EL_EMC_DRIPPER_ACTIVE &&
6256 game.lenses_time_left == 0)
6258 Feld[x][y] = EL_EMC_DRIPPER;
6259 DrawLevelField(x, y);
6261 else if (element == EL_INVISIBLE_STEELWALL ||
6262 element == EL_INVISIBLE_WALL ||
6263 element == EL_INVISIBLE_SAND)
6265 if (game.lenses_time_left > 0)
6266 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
6268 DrawLevelField(x, y);
6270 /* uncrumble neighbour fields, if needed */
6271 if (element == EL_INVISIBLE_SAND)
6272 DrawLevelFieldCrumbledSandNeighbours(x, y);
6274 else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6275 element == EL_INVISIBLE_WALL_ACTIVE ||
6276 element == EL_INVISIBLE_SAND_ACTIVE)
6278 if (game.lenses_time_left == 0)
6279 Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
6281 DrawLevelField(x, y);
6283 /* re-crumble neighbour fields, if needed */
6284 if (element == EL_INVISIBLE_SAND)
6285 DrawLevelFieldCrumbledSandNeighbours(x, y);
6290 static void RedrawAllInvisibleElementsForMagnifier()
6294 SCAN_PLAYFIELD(x, y)
6296 int element = Feld[x][y];
6298 if (element == EL_EMC_FAKE_GRASS &&
6299 game.magnify_time_left > 0)
6301 Feld[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
6302 DrawLevelField(x, y);
6304 else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
6305 game.magnify_time_left == 0)
6307 Feld[x][y] = EL_EMC_FAKE_GRASS;
6308 DrawLevelField(x, y);
6310 else if (IS_GATE_GRAY(element) &&
6311 game.magnify_time_left > 0)
6313 Feld[x][y] = (IS_RND_GATE_GRAY(element) ?
6314 element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
6315 IS_EM_GATE_GRAY(element) ?
6316 element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
6317 IS_EMC_GATE_GRAY(element) ?
6318 element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
6320 DrawLevelField(x, y);
6322 else if (IS_GATE_GRAY_ACTIVE(element) &&
6323 game.magnify_time_left == 0)
6325 Feld[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
6326 element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
6327 IS_EM_GATE_GRAY_ACTIVE(element) ?
6328 element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
6329 IS_EMC_GATE_GRAY_ACTIVE(element) ?
6330 element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
6332 DrawLevelField(x, y);
6337 static void ToggleLightSwitch(int x, int y)
6339 int element = Feld[x][y];
6341 game.light_time_left =
6342 (element == EL_LIGHT_SWITCH ?
6343 level.time_light * FRAMES_PER_SECOND : 0);
6345 RedrawAllLightSwitchesAndInvisibleElements();
6348 static void ActivateTimegateSwitch(int x, int y)
6352 game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
6354 SCAN_PLAYFIELD(xx, yy)
6356 int element = Feld[xx][yy];
6358 if (element == EL_TIMEGATE_CLOSED ||
6359 element == EL_TIMEGATE_CLOSING)
6361 Feld[xx][yy] = EL_TIMEGATE_OPENING;
6362 PlayLevelSound(xx, yy, SND_CLASS_TIMEGATE_OPENING);
6366 else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
6368 Feld[xx][yy] = EL_TIMEGATE_SWITCH;
6369 DrawLevelField(xx, yy);
6376 Feld[x][y] = (Feld[x][y] == EL_TIMEGATE_SWITCH ? EL_TIMEGATE_SWITCH_ACTIVE :
6377 EL_DC_TIMEGATE_SWITCH_ACTIVE);
6379 Feld[x][y] = EL_TIMEGATE_SWITCH_ACTIVE;
6383 void Impact(int x, int y)
6385 boolean last_line = (y == lev_fieldy - 1);
6386 boolean object_hit = FALSE;
6387 boolean impact = (last_line || object_hit);
6388 int element = Feld[x][y];
6389 int smashed = EL_STEELWALL;
6391 if (!last_line) /* check if element below was hit */
6393 if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
6396 object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
6397 MovDir[x][y + 1] != MV_DOWN ||
6398 MovPos[x][y + 1] <= TILEY / 2));
6400 /* do not smash moving elements that left the smashed field in time */
6401 if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
6402 ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
6405 #if USE_QUICKSAND_IMPACT_BUGFIX
6406 if (Feld[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
6408 RemoveMovingField(x, y + 1);
6409 Feld[x][y + 1] = EL_QUICKSAND_EMPTY;
6410 Feld[x][y + 2] = EL_ROCK;
6411 DrawLevelField(x, y + 2);
6416 if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTYING && object_hit == FALSE)
6418 RemoveMovingField(x, y + 1);
6419 Feld[x][y + 1] = EL_QUICKSAND_FAST_EMPTY;
6420 Feld[x][y + 2] = EL_ROCK;
6421 DrawLevelField(x, y + 2);
6428 smashed = MovingOrBlocked2Element(x, y + 1);
6430 impact = (last_line || object_hit);
6433 if (!last_line && smashed == EL_ACID) /* element falls into acid */
6435 SplashAcid(x, y + 1);
6439 /* !!! not sufficient for all cases -- see EL_PEARL below !!! */
6440 /* only reset graphic animation if graphic really changes after impact */
6442 el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
6444 ResetGfxAnimation(x, y);
6445 DrawLevelField(x, y);
6448 if (impact && CAN_EXPLODE_IMPACT(element))
6453 else if (impact && element == EL_PEARL &&
6454 smashed != EL_DC_MAGIC_WALL && smashed != EL_DC_MAGIC_WALL_ACTIVE)
6456 ResetGfxAnimation(x, y);
6458 Feld[x][y] = EL_PEARL_BREAKING;
6459 PlayLevelSound(x, y, SND_PEARL_BREAKING);
6462 else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
6464 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6469 if (impact && element == EL_AMOEBA_DROP)
6471 if (object_hit && IS_PLAYER(x, y + 1))
6472 KillPlayerUnlessEnemyProtected(x, y + 1);
6473 else if (object_hit && smashed == EL_PENGUIN)
6477 Feld[x][y] = EL_AMOEBA_GROWING;
6478 Store[x][y] = EL_AMOEBA_WET;
6480 ResetRandomAnimationValue(x, y);
6485 if (object_hit) /* check which object was hit */
6487 if ((CAN_PASS_MAGIC_WALL(element) &&
6488 (smashed == EL_MAGIC_WALL ||
6489 smashed == EL_BD_MAGIC_WALL)) ||
6490 (CAN_PASS_DC_MAGIC_WALL(element) &&
6491 smashed == EL_DC_MAGIC_WALL))
6494 int activated_magic_wall =
6495 (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
6496 smashed == EL_BD_MAGIC_WALL ? EL_BD_MAGIC_WALL_ACTIVE :
6497 EL_DC_MAGIC_WALL_ACTIVE);
6499 /* activate magic wall / mill */
6500 SCAN_PLAYFIELD(xx, yy)
6502 if (Feld[xx][yy] == smashed)
6503 Feld[xx][yy] = activated_magic_wall;
6506 game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
6507 game.magic_wall_active = TRUE;
6509 PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
6510 SND_MAGIC_WALL_ACTIVATING :
6511 smashed == EL_BD_MAGIC_WALL ?
6512 SND_BD_MAGIC_WALL_ACTIVATING :
6513 SND_DC_MAGIC_WALL_ACTIVATING));
6516 if (IS_PLAYER(x, y + 1))
6518 if (CAN_SMASH_PLAYER(element))
6520 KillPlayerUnlessEnemyProtected(x, y + 1);
6524 else if (smashed == EL_PENGUIN)
6526 if (CAN_SMASH_PLAYER(element))
6532 else if (element == EL_BD_DIAMOND)
6534 if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
6540 else if (((element == EL_SP_INFOTRON ||
6541 element == EL_SP_ZONK) &&
6542 (smashed == EL_SP_SNIKSNAK ||
6543 smashed == EL_SP_ELECTRON ||
6544 smashed == EL_SP_DISK_ORANGE)) ||
6545 (element == EL_SP_INFOTRON &&
6546 smashed == EL_SP_DISK_YELLOW))
6551 else if (CAN_SMASH_EVERYTHING(element))
6553 if (IS_CLASSIC_ENEMY(smashed) ||
6554 CAN_EXPLODE_SMASHED(smashed))
6559 else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
6561 if (smashed == EL_LAMP ||
6562 smashed == EL_LAMP_ACTIVE)
6567 else if (smashed == EL_NUT)
6569 Feld[x][y + 1] = EL_NUT_BREAKING;
6570 PlayLevelSound(x, y, SND_NUT_BREAKING);
6571 RaiseScoreElement(EL_NUT);
6574 else if (smashed == EL_PEARL)
6576 ResetGfxAnimation(x, y);
6578 Feld[x][y + 1] = EL_PEARL_BREAKING;
6579 PlayLevelSound(x, y, SND_PEARL_BREAKING);
6582 else if (smashed == EL_DIAMOND)
6584 Feld[x][y + 1] = EL_DIAMOND_BREAKING;
6585 PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
6588 else if (IS_BELT_SWITCH(smashed))
6590 ToggleBeltSwitch(x, y + 1);
6592 else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
6593 smashed == EL_SWITCHGATE_SWITCH_DOWN ||
6594 smashed == EL_DC_SWITCHGATE_SWITCH_UP ||
6595 smashed == EL_DC_SWITCHGATE_SWITCH_DOWN)
6597 ToggleSwitchgateSwitch(x, y + 1);
6599 else if (smashed == EL_LIGHT_SWITCH ||
6600 smashed == EL_LIGHT_SWITCH_ACTIVE)
6602 ToggleLightSwitch(x, y + 1);
6607 TestIfElementSmashesCustomElement(x, y, MV_DOWN);
6610 CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6612 CheckElementChangeBySide(x, y + 1, smashed, element,
6613 CE_SWITCHED, CH_SIDE_TOP);
6614 CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
6620 CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6625 /* play sound of magic wall / mill */
6627 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
6628 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ||
6629 Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE))
6631 if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
6632 PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
6633 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
6634 PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
6635 else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
6636 PlayLevelSound(x, y, SND_DC_MAGIC_WALL_FILLING);
6641 /* play sound of object that hits the ground */
6642 if (last_line || object_hit)
6643 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6646 inline static void TurnRoundExt(int x, int y)
6658 { 0, 0 }, { 0, 0 }, { 0, 0 },
6663 int left, right, back;
6667 { MV_DOWN, MV_UP, MV_RIGHT },
6668 { MV_UP, MV_DOWN, MV_LEFT },
6670 { MV_LEFT, MV_RIGHT, MV_DOWN },
6674 { MV_RIGHT, MV_LEFT, MV_UP }
6677 int element = Feld[x][y];
6678 int move_pattern = element_info[element].move_pattern;
6680 int old_move_dir = MovDir[x][y];
6681 int left_dir = turn[old_move_dir].left;
6682 int right_dir = turn[old_move_dir].right;
6683 int back_dir = turn[old_move_dir].back;
6685 int left_dx = move_xy[left_dir].dx, left_dy = move_xy[left_dir].dy;
6686 int right_dx = move_xy[right_dir].dx, right_dy = move_xy[right_dir].dy;
6687 int move_dx = move_xy[old_move_dir].dx, move_dy = move_xy[old_move_dir].dy;
6688 int back_dx = move_xy[back_dir].dx, back_dy = move_xy[back_dir].dy;
6690 int left_x = x + left_dx, left_y = y + left_dy;
6691 int right_x = x + right_dx, right_y = y + right_dy;
6692 int move_x = x + move_dx, move_y = y + move_dy;
6696 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
6698 TestIfBadThingTouchesOtherBadThing(x, y);
6700 if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
6701 MovDir[x][y] = right_dir;
6702 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6703 MovDir[x][y] = left_dir;
6705 if (element == EL_BUG && MovDir[x][y] != old_move_dir)
6707 else if (element == EL_BD_BUTTERFLY) /* && MovDir[x][y] == left_dir) */
6710 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
6712 TestIfBadThingTouchesOtherBadThing(x, y);
6714 if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
6715 MovDir[x][y] = left_dir;
6716 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6717 MovDir[x][y] = right_dir;
6719 if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
6721 else if (element == EL_BD_FIREFLY) /* && MovDir[x][y] == right_dir) */
6724 else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
6726 TestIfBadThingTouchesOtherBadThing(x, y);
6728 if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
6729 MovDir[x][y] = left_dir;
6730 else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
6731 MovDir[x][y] = right_dir;
6733 if (MovDir[x][y] != old_move_dir)
6736 else if (element == EL_YAMYAM)
6738 boolean can_turn_left = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
6739 boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
6741 if (can_turn_left && can_turn_right)
6742 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6743 else if (can_turn_left)
6744 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6745 else if (can_turn_right)
6746 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6748 MovDir[x][y] = back_dir;
6750 MovDelay[x][y] = 16 + 16 * RND(3);
6752 else if (element == EL_DARK_YAMYAM)
6754 boolean can_turn_left = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6756 boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6759 if (can_turn_left && can_turn_right)
6760 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6761 else if (can_turn_left)
6762 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6763 else if (can_turn_right)
6764 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6766 MovDir[x][y] = back_dir;
6768 MovDelay[x][y] = 16 + 16 * RND(3);
6770 else if (element == EL_PACMAN)
6772 boolean can_turn_left = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
6773 boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
6775 if (can_turn_left && can_turn_right)
6776 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6777 else if (can_turn_left)
6778 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6779 else if (can_turn_right)
6780 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6782 MovDir[x][y] = back_dir;
6784 MovDelay[x][y] = 6 + RND(40);
6786 else if (element == EL_PIG)
6788 boolean can_turn_left = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
6789 boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
6790 boolean can_move_on = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
6791 boolean should_turn_left, should_turn_right, should_move_on;
6793 int rnd = RND(rnd_value);
6795 should_turn_left = (can_turn_left &&
6797 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
6798 y + back_dy + left_dy)));
6799 should_turn_right = (can_turn_right &&
6801 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
6802 y + back_dy + right_dy)));
6803 should_move_on = (can_move_on &&
6806 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
6807 y + move_dy + left_dy) ||
6808 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
6809 y + move_dy + right_dy)));
6811 if (should_turn_left || should_turn_right || should_move_on)
6813 if (should_turn_left && should_turn_right && should_move_on)
6814 MovDir[x][y] = (rnd < rnd_value / 3 ? left_dir :
6815 rnd < 2 * rnd_value / 3 ? right_dir :
6817 else if (should_turn_left && should_turn_right)
6818 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6819 else if (should_turn_left && should_move_on)
6820 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
6821 else if (should_turn_right && should_move_on)
6822 MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
6823 else if (should_turn_left)
6824 MovDir[x][y] = left_dir;
6825 else if (should_turn_right)
6826 MovDir[x][y] = right_dir;
6827 else if (should_move_on)
6828 MovDir[x][y] = old_move_dir;
6830 else if (can_move_on && rnd > rnd_value / 8)
6831 MovDir[x][y] = old_move_dir;
6832 else if (can_turn_left && can_turn_right)
6833 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6834 else if (can_turn_left && rnd > rnd_value / 8)
6835 MovDir[x][y] = left_dir;
6836 else if (can_turn_right && rnd > rnd_value/8)
6837 MovDir[x][y] = right_dir;
6839 MovDir[x][y] = back_dir;
6841 xx = x + move_xy[MovDir[x][y]].dx;
6842 yy = y + move_xy[MovDir[x][y]].dy;
6844 if (!IN_LEV_FIELD(xx, yy) ||
6845 (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy])))
6846 MovDir[x][y] = old_move_dir;
6850 else if (element == EL_DRAGON)
6852 boolean can_turn_left = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
6853 boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
6854 boolean can_move_on = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
6856 int rnd = RND(rnd_value);
6858 if (can_move_on && rnd > rnd_value / 8)
6859 MovDir[x][y] = old_move_dir;
6860 else if (can_turn_left && can_turn_right)
6861 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6862 else if (can_turn_left && rnd > rnd_value / 8)
6863 MovDir[x][y] = left_dir;
6864 else if (can_turn_right && rnd > rnd_value / 8)
6865 MovDir[x][y] = right_dir;
6867 MovDir[x][y] = back_dir;
6869 xx = x + move_xy[MovDir[x][y]].dx;
6870 yy = y + move_xy[MovDir[x][y]].dy;
6872 if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
6873 MovDir[x][y] = old_move_dir;
6877 else if (element == EL_MOLE)
6879 boolean can_move_on =
6880 (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
6881 IS_AMOEBOID(Feld[move_x][move_y]) ||
6882 Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
6885 boolean can_turn_left =
6886 (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
6887 IS_AMOEBOID(Feld[left_x][left_y])));
6889 boolean can_turn_right =
6890 (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
6891 IS_AMOEBOID(Feld[right_x][right_y])));
6893 if (can_turn_left && can_turn_right)
6894 MovDir[x][y] = (RND(2) ? left_dir : right_dir);
6895 else if (can_turn_left)
6896 MovDir[x][y] = left_dir;
6898 MovDir[x][y] = right_dir;
6901 if (MovDir[x][y] != old_move_dir)
6904 else if (element == EL_BALLOON)
6906 MovDir[x][y] = game.wind_direction;
6909 else if (element == EL_SPRING)
6911 #if USE_NEW_SPRING_BUMPER
6912 if (MovDir[x][y] & MV_HORIZONTAL)
6914 if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
6915 !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
6917 Feld[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
6918 ResetGfxAnimation(move_x, move_y);
6919 DrawLevelField(move_x, move_y);
6921 MovDir[x][y] = back_dir;
6923 else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
6924 SPRING_CAN_ENTER_FIELD(element, x, y + 1))
6925 MovDir[x][y] = MV_NONE;
6928 if (MovDir[x][y] & MV_HORIZONTAL &&
6929 (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
6930 SPRING_CAN_ENTER_FIELD(element, x, y + 1)))
6931 MovDir[x][y] = MV_NONE;
6936 else if (element == EL_ROBOT ||
6937 element == EL_SATELLITE ||
6938 element == EL_PENGUIN ||
6939 element == EL_EMC_ANDROID)
6941 int attr_x = -1, attr_y = -1;
6952 for (i = 0; i < MAX_PLAYERS; i++)
6954 struct PlayerInfo *player = &stored_player[i];
6955 int jx = player->jx, jy = player->jy;
6957 if (!player->active)
6961 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
6969 if (element == EL_ROBOT && ZX >= 0 && ZY >= 0 &&
6970 (Feld[ZX][ZY] == EL_ROBOT_WHEEL_ACTIVE ||
6971 game.engine_version < VERSION_IDENT(3,1,0,0)))
6977 if (element == EL_PENGUIN)
6980 static int xy[4][2] =
6988 for (i = 0; i < NUM_DIRECTIONS; i++)
6990 int ex = x + xy[i][0];
6991 int ey = y + xy[i][1];
6993 if (IN_LEV_FIELD(ex, ey) && (Feld[ex][ey] == EL_EXIT_OPEN ||
6994 Feld[ex][ey] == EL_EM_EXIT_OPEN ||
6995 Feld[ex][ey] == EL_STEEL_EXIT_OPEN ||
6996 Feld[ex][ey] == EL_EM_STEEL_EXIT_OPEN))
7005 MovDir[x][y] = MV_NONE;
7007 MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
7008 else if (attr_x > x)
7009 MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
7011 MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
7012 else if (attr_y > y)
7013 MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
7015 if (element == EL_ROBOT)
7019 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7020 MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
7021 Moving2Blocked(x, y, &newx, &newy);
7023 if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
7024 MovDelay[x][y] = 8 + 8 * !RND(3);
7026 MovDelay[x][y] = 16;
7028 else if (element == EL_PENGUIN)
7034 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7036 boolean first_horiz = RND(2);
7037 int new_move_dir = MovDir[x][y];
7040 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7041 Moving2Blocked(x, y, &newx, &newy);
7043 if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
7047 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7048 Moving2Blocked(x, y, &newx, &newy);
7050 if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
7053 MovDir[x][y] = old_move_dir;
7057 else if (element == EL_SATELLITE)
7063 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7065 boolean first_horiz = RND(2);
7066 int new_move_dir = MovDir[x][y];
7069 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7070 Moving2Blocked(x, y, &newx, &newy);
7072 if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
7076 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7077 Moving2Blocked(x, y, &newx, &newy);
7079 if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
7082 MovDir[x][y] = old_move_dir;
7086 else if (element == EL_EMC_ANDROID)
7088 static int check_pos[16] =
7090 -1, /* 0 => (invalid) */
7091 7, /* 1 => MV_LEFT */
7092 3, /* 2 => MV_RIGHT */
7093 -1, /* 3 => (invalid) */
7095 0, /* 5 => MV_LEFT | MV_UP */
7096 2, /* 6 => MV_RIGHT | MV_UP */
7097 -1, /* 7 => (invalid) */
7098 5, /* 8 => MV_DOWN */
7099 6, /* 9 => MV_LEFT | MV_DOWN */
7100 4, /* 10 => MV_RIGHT | MV_DOWN */
7101 -1, /* 11 => (invalid) */
7102 -1, /* 12 => (invalid) */
7103 -1, /* 13 => (invalid) */
7104 -1, /* 14 => (invalid) */
7105 -1, /* 15 => (invalid) */
7113 { -1, -1, MV_LEFT | MV_UP },
7115 { +1, -1, MV_RIGHT | MV_UP },
7116 { +1, 0, MV_RIGHT },
7117 { +1, +1, MV_RIGHT | MV_DOWN },
7119 { -1, +1, MV_LEFT | MV_DOWN },
7122 int start_pos, check_order;
7123 boolean can_clone = FALSE;
7126 /* check if there is any free field around current position */
7127 for (i = 0; i < 8; i++)
7129 int newx = x + check_xy[i].dx;
7130 int newy = y + check_xy[i].dy;
7132 if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7140 if (can_clone) /* randomly find an element to clone */
7144 start_pos = check_pos[RND(8)];
7145 check_order = (RND(2) ? -1 : +1);
7147 for (i = 0; i < 8; i++)
7149 int pos_raw = start_pos + i * check_order;
7150 int pos = (pos_raw + 8) % 8;
7151 int newx = x + check_xy[pos].dx;
7152 int newy = y + check_xy[pos].dy;
7154 if (ANDROID_CAN_CLONE_FIELD(newx, newy))
7156 element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
7157 element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
7159 Store[x][y] = Feld[newx][newy];
7168 if (can_clone) /* randomly find a direction to move */
7172 start_pos = check_pos[RND(8)];
7173 check_order = (RND(2) ? -1 : +1);
7175 for (i = 0; i < 8; i++)
7177 int pos_raw = start_pos + i * check_order;
7178 int pos = (pos_raw + 8) % 8;
7179 int newx = x + check_xy[pos].dx;
7180 int newy = y + check_xy[pos].dy;
7181 int new_move_dir = check_xy[pos].dir;
7183 if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7185 MovDir[x][y] = new_move_dir;
7186 MovDelay[x][y] = level.android_clone_time * 8 + 1;
7195 if (can_clone) /* cloning and moving successful */
7198 /* cannot clone -- try to move towards player */
7200 start_pos = check_pos[MovDir[x][y] & 0x0f];
7201 check_order = (RND(2) ? -1 : +1);
7203 for (i = 0; i < 3; i++)
7205 /* first check start_pos, then previous/next or (next/previous) pos */
7206 int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
7207 int pos = (pos_raw + 8) % 8;
7208 int newx = x + check_xy[pos].dx;
7209 int newy = y + check_xy[pos].dy;
7210 int new_move_dir = check_xy[pos].dir;
7212 if (IS_PLAYER(newx, newy))
7215 if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7217 MovDir[x][y] = new_move_dir;
7218 MovDelay[x][y] = level.android_move_time * 8 + 1;
7225 else if (move_pattern == MV_TURNING_LEFT ||
7226 move_pattern == MV_TURNING_RIGHT ||
7227 move_pattern == MV_TURNING_LEFT_RIGHT ||
7228 move_pattern == MV_TURNING_RIGHT_LEFT ||
7229 move_pattern == MV_TURNING_RANDOM ||
7230 move_pattern == MV_ALL_DIRECTIONS)
7232 boolean can_turn_left =
7233 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
7234 boolean can_turn_right =
7235 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
7237 if (element_info[element].move_stepsize == 0) /* "not moving" */
7240 if (move_pattern == MV_TURNING_LEFT)
7241 MovDir[x][y] = left_dir;
7242 else if (move_pattern == MV_TURNING_RIGHT)
7243 MovDir[x][y] = right_dir;
7244 else if (move_pattern == MV_TURNING_LEFT_RIGHT)
7245 MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
7246 else if (move_pattern == MV_TURNING_RIGHT_LEFT)
7247 MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
7248 else if (move_pattern == MV_TURNING_RANDOM)
7249 MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
7250 can_turn_right && !can_turn_left ? right_dir :
7251 RND(2) ? left_dir : right_dir);
7252 else if (can_turn_left && can_turn_right)
7253 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7254 else if (can_turn_left)
7255 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7256 else if (can_turn_right)
7257 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7259 MovDir[x][y] = back_dir;
7261 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7263 else if (move_pattern == MV_HORIZONTAL ||
7264 move_pattern == MV_VERTICAL)
7266 if (move_pattern & old_move_dir)
7267 MovDir[x][y] = back_dir;
7268 else if (move_pattern == MV_HORIZONTAL)
7269 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
7270 else if (move_pattern == MV_VERTICAL)
7271 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
7273 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7275 else if (move_pattern & MV_ANY_DIRECTION)
7277 MovDir[x][y] = move_pattern;
7278 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7280 else if (move_pattern & MV_WIND_DIRECTION)
7282 MovDir[x][y] = game.wind_direction;
7283 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7285 else if (move_pattern == MV_ALONG_LEFT_SIDE)
7287 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
7288 MovDir[x][y] = left_dir;
7289 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7290 MovDir[x][y] = right_dir;
7292 if (MovDir[x][y] != old_move_dir)
7293 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7295 else if (move_pattern == MV_ALONG_RIGHT_SIDE)
7297 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
7298 MovDir[x][y] = right_dir;
7299 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7300 MovDir[x][y] = left_dir;
7302 if (MovDir[x][y] != old_move_dir)
7303 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7305 else if (move_pattern == MV_TOWARDS_PLAYER ||
7306 move_pattern == MV_AWAY_FROM_PLAYER)
7308 int attr_x = -1, attr_y = -1;
7310 boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
7321 for (i = 0; i < MAX_PLAYERS; i++)
7323 struct PlayerInfo *player = &stored_player[i];
7324 int jx = player->jx, jy = player->jy;
7326 if (!player->active)
7330 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7338 MovDir[x][y] = MV_NONE;
7340 MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
7341 else if (attr_x > x)
7342 MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
7344 MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
7345 else if (attr_y > y)
7346 MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
7348 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7350 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7352 boolean first_horiz = RND(2);
7353 int new_move_dir = MovDir[x][y];
7355 if (element_info[element].move_stepsize == 0) /* "not moving" */
7357 first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
7358 MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7364 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7365 Moving2Blocked(x, y, &newx, &newy);
7367 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7371 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7372 Moving2Blocked(x, y, &newx, &newy);
7374 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7377 MovDir[x][y] = old_move_dir;
7380 else if (move_pattern == MV_WHEN_PUSHED ||
7381 move_pattern == MV_WHEN_DROPPED)
7383 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7384 MovDir[x][y] = MV_NONE;
7388 else if (move_pattern & MV_MAZE_RUNNER_STYLE)
7390 static int test_xy[7][2] =
7400 static int test_dir[7] =
7410 boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
7411 int move_preference = -1000000; /* start with very low preference */
7412 int new_move_dir = MV_NONE;
7413 int start_test = RND(4);
7416 for (i = 0; i < NUM_DIRECTIONS; i++)
7418 int move_dir = test_dir[start_test + i];
7419 int move_dir_preference;
7421 xx = x + test_xy[start_test + i][0];
7422 yy = y + test_xy[start_test + i][1];
7424 if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
7425 (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
7427 new_move_dir = move_dir;
7432 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
7435 move_dir_preference = -1 * RunnerVisit[xx][yy];
7436 if (hunter_mode && PlayerVisit[xx][yy] > 0)
7437 move_dir_preference = PlayerVisit[xx][yy];
7439 if (move_dir_preference > move_preference)
7441 /* prefer field that has not been visited for the longest time */
7442 move_preference = move_dir_preference;
7443 new_move_dir = move_dir;
7445 else if (move_dir_preference == move_preference &&
7446 move_dir == old_move_dir)
7448 /* prefer last direction when all directions are preferred equally */
7449 move_preference = move_dir_preference;
7450 new_move_dir = move_dir;
7454 MovDir[x][y] = new_move_dir;
7455 if (old_move_dir != new_move_dir)
7456 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7460 static void TurnRound(int x, int y)
7462 int direction = MovDir[x][y];
7466 GfxDir[x][y] = MovDir[x][y];
7468 if (direction != MovDir[x][y])
7472 GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
7474 ResetGfxFrame(x, y, FALSE);
7477 static boolean JustBeingPushed(int x, int y)
7481 for (i = 0; i < MAX_PLAYERS; i++)
7483 struct PlayerInfo *player = &stored_player[i];
7485 if (player->active && player->is_pushing && player->MovPos)
7487 int next_jx = player->jx + (player->jx - player->last_jx);
7488 int next_jy = player->jy + (player->jy - player->last_jy);
7490 if (x == next_jx && y == next_jy)
7498 void StartMoving(int x, int y)
7500 boolean started_moving = FALSE; /* some elements can fall _and_ move */
7501 int element = Feld[x][y];
7506 if (MovDelay[x][y] == 0)
7507 GfxAction[x][y] = ACTION_DEFAULT;
7509 if (CAN_FALL(element) && y < lev_fieldy - 1)
7511 if ((x > 0 && IS_PLAYER(x - 1, y)) ||
7512 (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
7513 if (JustBeingPushed(x, y))
7516 if (element == EL_QUICKSAND_FULL)
7518 if (IS_FREE(x, y + 1))
7520 InitMovingField(x, y, MV_DOWN);
7521 started_moving = TRUE;
7523 Feld[x][y] = EL_QUICKSAND_EMPTYING;
7524 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7525 if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7526 Store[x][y] = EL_ROCK;
7528 Store[x][y] = EL_ROCK;
7531 PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7533 else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7535 if (!MovDelay[x][y])
7536 MovDelay[x][y] = TILEY + 1;
7545 Feld[x][y] = EL_QUICKSAND_EMPTY;
7546 Feld[x][y + 1] = EL_QUICKSAND_FULL;
7547 Store[x][y + 1] = Store[x][y];
7550 PlayLevelSoundAction(x, y, ACTION_FILLING);
7553 else if (element == EL_QUICKSAND_FAST_FULL)
7555 if (IS_FREE(x, y + 1))
7557 InitMovingField(x, y, MV_DOWN);
7558 started_moving = TRUE;
7560 Feld[x][y] = EL_QUICKSAND_FAST_EMPTYING;
7561 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7562 if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7563 Store[x][y] = EL_ROCK;
7565 Store[x][y] = EL_ROCK;
7568 PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7570 else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7572 if (!MovDelay[x][y])
7573 MovDelay[x][y] = TILEY + 1;
7582 Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
7583 Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7584 Store[x][y + 1] = Store[x][y];
7587 PlayLevelSoundAction(x, y, ACTION_FILLING);
7590 else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7591 Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7593 InitMovingField(x, y, MV_DOWN);
7594 started_moving = TRUE;
7596 Feld[x][y] = EL_QUICKSAND_FILLING;
7597 Store[x][y] = element;
7599 PlayLevelSoundAction(x, y, ACTION_FILLING);
7601 else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7602 Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7604 InitMovingField(x, y, MV_DOWN);
7605 started_moving = TRUE;
7607 Feld[x][y] = EL_QUICKSAND_FAST_FILLING;
7608 Store[x][y] = element;
7610 PlayLevelSoundAction(x, y, ACTION_FILLING);
7612 else if (element == EL_MAGIC_WALL_FULL)
7614 if (IS_FREE(x, y + 1))
7616 InitMovingField(x, y, MV_DOWN);
7617 started_moving = TRUE;
7619 Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
7620 Store[x][y] = EL_CHANGED(Store[x][y]);
7622 else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
7624 if (!MovDelay[x][y])
7625 MovDelay[x][y] = TILEY/4 + 1;
7634 Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
7635 Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
7636 Store[x][y + 1] = EL_CHANGED(Store[x][y]);
7640 else if (element == EL_BD_MAGIC_WALL_FULL)
7642 if (IS_FREE(x, y + 1))
7644 InitMovingField(x, y, MV_DOWN);
7645 started_moving = TRUE;
7647 Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
7648 Store[x][y] = EL_CHANGED_BD(Store[x][y]);
7650 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
7652 if (!MovDelay[x][y])
7653 MovDelay[x][y] = TILEY/4 + 1;
7662 Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
7663 Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
7664 Store[x][y + 1] = EL_CHANGED_BD(Store[x][y]);
7668 else if (element == EL_DC_MAGIC_WALL_FULL)
7670 if (IS_FREE(x, y + 1))
7672 InitMovingField(x, y, MV_DOWN);
7673 started_moving = TRUE;
7675 Feld[x][y] = EL_DC_MAGIC_WALL_EMPTYING;
7676 Store[x][y] = EL_CHANGED_DC(Store[x][y]);
7678 else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
7680 if (!MovDelay[x][y])
7681 MovDelay[x][y] = TILEY/4 + 1;
7690 Feld[x][y] = EL_DC_MAGIC_WALL_ACTIVE;
7691 Feld[x][y + 1] = EL_DC_MAGIC_WALL_FULL;
7692 Store[x][y + 1] = EL_CHANGED_DC(Store[x][y]);
7696 else if ((CAN_PASS_MAGIC_WALL(element) &&
7697 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
7698 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)) ||
7699 (CAN_PASS_DC_MAGIC_WALL(element) &&
7700 (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)))
7703 InitMovingField(x, y, MV_DOWN);
7704 started_moving = TRUE;
7707 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
7708 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ? EL_BD_MAGIC_WALL_FILLING :
7709 EL_DC_MAGIC_WALL_FILLING);
7710 Store[x][y] = element;
7712 else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
7714 SplashAcid(x, y + 1);
7716 InitMovingField(x, y, MV_DOWN);
7717 started_moving = TRUE;
7719 Store[x][y] = EL_ACID;
7722 #if USE_FIX_IMPACT_COLLISION
7723 (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7724 CheckImpact[x][y] && !IS_FREE(x, y + 1)) ||
7726 (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7727 CheckCollision[x][y] && !IS_FREE(x, y + 1)) ||
7729 (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
7730 CAN_FALL(element) && WasJustFalling[x][y] &&
7731 (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
7733 (game.engine_version < VERSION_IDENT(2,2,0,7) &&
7734 CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
7735 (Feld[x][y + 1] == EL_BLOCKED)))
7737 /* this is needed for a special case not covered by calling "Impact()"
7738 from "ContinueMoving()": if an element moves to a tile directly below
7739 another element which was just falling on that tile (which was empty
7740 in the previous frame), the falling element above would just stop
7741 instead of smashing the element below (in previous version, the above
7742 element was just checked for "moving" instead of "falling", resulting
7743 in incorrect smashes caused by horizontal movement of the above
7744 element; also, the case of the player being the element to smash was
7745 simply not covered here... :-/ ) */
7747 CheckCollision[x][y] = 0;
7748 CheckImpact[x][y] = 0;
7752 else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
7754 if (MovDir[x][y] == MV_NONE)
7756 InitMovingField(x, y, MV_DOWN);
7757 started_moving = TRUE;
7760 else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
7762 if (WasJustFalling[x][y]) /* prevent animation from being restarted */
7763 MovDir[x][y] = MV_DOWN;
7765 InitMovingField(x, y, MV_DOWN);
7766 started_moving = TRUE;
7768 else if (element == EL_AMOEBA_DROP)
7770 Feld[x][y] = EL_AMOEBA_GROWING;
7771 Store[x][y] = EL_AMOEBA_WET;
7773 else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
7774 (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
7775 !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
7776 element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
7778 boolean can_fall_left = (x > 0 && IS_FREE(x - 1, y) &&
7779 (IS_FREE(x - 1, y + 1) ||
7780 Feld[x - 1][y + 1] == EL_ACID));
7781 boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
7782 (IS_FREE(x + 1, y + 1) ||
7783 Feld[x + 1][y + 1] == EL_ACID));
7784 boolean can_fall_any = (can_fall_left || can_fall_right);
7785 boolean can_fall_both = (can_fall_left && can_fall_right);
7786 int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
7788 #if USE_NEW_ALL_SLIPPERY
7789 if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
7791 if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
7792 can_fall_right = FALSE;
7793 else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
7794 can_fall_left = FALSE;
7795 else if (slippery_type == SLIPPERY_ONLY_LEFT)
7796 can_fall_right = FALSE;
7797 else if (slippery_type == SLIPPERY_ONLY_RIGHT)
7798 can_fall_left = FALSE;
7800 can_fall_any = (can_fall_left || can_fall_right);
7801 can_fall_both = FALSE;
7804 if (can_fall_any && IS_CUSTOM_ELEMENT(Feld[x][y + 1]))
7806 if (slippery_type == SLIPPERY_ONLY_LEFT)
7807 can_fall_right = FALSE;
7808 else if (slippery_type == SLIPPERY_ONLY_RIGHT)
7809 can_fall_left = FALSE;
7810 else if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
7811 can_fall_right = FALSE;
7812 else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
7813 can_fall_left = FALSE;
7815 can_fall_any = (can_fall_left || can_fall_right);
7816 can_fall_both = (can_fall_left && can_fall_right);
7820 #if USE_NEW_ALL_SLIPPERY
7822 #if USE_NEW_SP_SLIPPERY
7823 /* !!! better use the same properties as for custom elements here !!! */
7824 else if (game.engine_version >= VERSION_IDENT(3,1,1,0) &&
7825 can_fall_both && IS_SP_ELEMENT(Feld[x][y + 1]))
7827 can_fall_right = FALSE; /* slip down on left side */
7828 can_fall_both = FALSE;
7833 #if USE_NEW_ALL_SLIPPERY
7836 if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
7837 can_fall_right = FALSE; /* slip down on left side */
7839 can_fall_left = !(can_fall_right = RND(2));
7841 can_fall_both = FALSE;
7846 if (game.emulation == EMU_BOULDERDASH ||
7847 element == EL_BD_ROCK || element == EL_BD_DIAMOND)
7848 can_fall_right = FALSE; /* slip down on left side */
7850 can_fall_left = !(can_fall_right = RND(2));
7852 can_fall_both = FALSE;
7858 /* if not determined otherwise, prefer left side for slipping down */
7859 InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
7860 started_moving = TRUE;
7864 else if (IS_BELT_ACTIVE(Feld[x][y + 1]) && !CAN_MOVE(element))
7866 else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
7869 boolean left_is_free = (x > 0 && IS_FREE(x - 1, y));
7870 boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
7871 int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
7872 int belt_dir = game.belt_dir[belt_nr];
7874 if ((belt_dir == MV_LEFT && left_is_free) ||
7875 (belt_dir == MV_RIGHT && right_is_free))
7877 int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
7879 InitMovingField(x, y, belt_dir);
7880 started_moving = TRUE;
7882 Pushed[x][y] = TRUE;
7883 Pushed[nextx][y] = TRUE;
7885 GfxAction[x][y] = ACTION_DEFAULT;
7889 MovDir[x][y] = 0; /* if element was moving, stop it */
7894 /* not "else if" because of elements that can fall and move (EL_SPRING) */
7896 if (CAN_MOVE(element) && !started_moving && MovDir[x][y] != MV_NONE)
7898 if (CAN_MOVE(element) && !started_moving)
7901 int move_pattern = element_info[element].move_pattern;
7906 if (MovDir[x][y] == MV_NONE)
7908 printf("StartMoving(): %d,%d: element %d ['%s'] not moving\n",
7909 x, y, element, element_info[element].token_name);
7910 printf("StartMoving(): This should never happen!\n");
7915 Moving2Blocked(x, y, &newx, &newy);
7917 if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
7920 if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7921 CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7923 WasJustMoving[x][y] = 0;
7924 CheckCollision[x][y] = 0;
7926 TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
7928 if (Feld[x][y] != element) /* element has changed */
7932 if (!MovDelay[x][y]) /* start new movement phase */
7934 /* all objects that can change their move direction after each step
7935 (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
7937 if (element != EL_YAMYAM &&
7938 element != EL_DARK_YAMYAM &&
7939 element != EL_PACMAN &&
7940 !(move_pattern & MV_ANY_DIRECTION) &&
7941 move_pattern != MV_TURNING_LEFT &&
7942 move_pattern != MV_TURNING_RIGHT &&
7943 move_pattern != MV_TURNING_LEFT_RIGHT &&
7944 move_pattern != MV_TURNING_RIGHT_LEFT &&
7945 move_pattern != MV_TURNING_RANDOM)
7949 if (MovDelay[x][y] && (element == EL_BUG ||
7950 element == EL_SPACESHIP ||
7951 element == EL_SP_SNIKSNAK ||
7952 element == EL_SP_ELECTRON ||
7953 element == EL_MOLE))
7954 DrawLevelField(x, y);
7958 if (MovDelay[x][y]) /* wait some time before next movement */
7962 if (element == EL_ROBOT ||
7963 element == EL_YAMYAM ||
7964 element == EL_DARK_YAMYAM)
7966 DrawLevelElementAnimationIfNeeded(x, y, element);
7967 PlayLevelSoundAction(x, y, ACTION_WAITING);
7969 else if (element == EL_SP_ELECTRON)
7970 DrawLevelElementAnimationIfNeeded(x, y, element);
7971 else if (element == EL_DRAGON)
7974 int dir = MovDir[x][y];
7975 int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
7976 int dy = (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
7977 int graphic = (dir == MV_LEFT ? IMG_FLAMES_1_LEFT :
7978 dir == MV_RIGHT ? IMG_FLAMES_1_RIGHT :
7979 dir == MV_UP ? IMG_FLAMES_1_UP :
7980 dir == MV_DOWN ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
7981 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
7983 GfxAction[x][y] = ACTION_ATTACKING;
7985 if (IS_PLAYER(x, y))
7986 DrawPlayerField(x, y);
7988 DrawLevelField(x, y);
7990 PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
7992 for (i = 1; i <= 3; i++)
7994 int xx = x + i * dx;
7995 int yy = y + i * dy;
7996 int sx = SCREENX(xx);
7997 int sy = SCREENY(yy);
7998 int flame_graphic = graphic + (i - 1);
8000 if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
8005 int flamed = MovingOrBlocked2Element(xx, yy);
8009 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
8011 else if (IS_MOVING(xx, yy) || IS_BLOCKED(xx, yy))
8012 RemoveMovingField(xx, yy);
8014 RemoveField(xx, yy);
8016 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
8019 RemoveMovingField(xx, yy);
8022 ChangeDelay[xx][yy] = 0;
8024 Feld[xx][yy] = EL_FLAMES;
8026 if (IN_SCR_FIELD(sx, sy))
8028 DrawLevelFieldCrumbledSand(xx, yy);
8029 DrawGraphic(sx, sy, flame_graphic, frame);
8034 if (Feld[xx][yy] == EL_FLAMES)
8035 Feld[xx][yy] = EL_EMPTY;
8036 DrawLevelField(xx, yy);
8041 if (MovDelay[x][y]) /* element still has to wait some time */
8043 PlayLevelSoundAction(x, y, ACTION_WAITING);
8049 /* now make next step */
8051 Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
8053 if (DONT_COLLIDE_WITH(element) &&
8054 IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
8055 !PLAYER_ENEMY_PROTECTED(newx, newy))
8057 TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
8062 else if (CAN_MOVE_INTO_ACID(element) &&
8063 IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID &&
8064 !IS_MV_DIAGONAL(MovDir[x][y]) &&
8065 (MovDir[x][y] == MV_DOWN ||
8066 game.engine_version >= VERSION_IDENT(3,1,0,0)))
8068 SplashAcid(newx, newy);
8069 Store[x][y] = EL_ACID;
8071 else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
8073 if (Feld[newx][newy] == EL_EXIT_OPEN ||
8074 Feld[newx][newy] == EL_EM_EXIT_OPEN ||
8075 Feld[newx][newy] == EL_STEEL_EXIT_OPEN ||
8076 Feld[newx][newy] == EL_EM_STEEL_EXIT_OPEN)
8079 DrawLevelField(x, y);
8081 PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
8082 if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
8083 DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
8085 local_player->friends_still_needed--;
8086 if (!local_player->friends_still_needed &&
8087 !local_player->GameOver && AllPlayersGone)
8088 PlayerWins(local_player);
8092 else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
8094 if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MP_MOVING)
8095 DrawLevelField(newx, newy);
8097 GfxDir[x][y] = MovDir[x][y] = MV_NONE;
8099 else if (!IS_FREE(newx, newy))
8101 GfxAction[x][y] = ACTION_WAITING;
8103 if (IS_PLAYER(x, y))
8104 DrawPlayerField(x, y);
8106 DrawLevelField(x, y);
8111 else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
8113 if (IS_FOOD_PIG(Feld[newx][newy]))
8115 if (IS_MOVING(newx, newy))
8116 RemoveMovingField(newx, newy);
8119 Feld[newx][newy] = EL_EMPTY;
8120 DrawLevelField(newx, newy);
8123 PlayLevelSound(x, y, SND_PIG_DIGGING);
8125 else if (!IS_FREE(newx, newy))
8127 if (IS_PLAYER(x, y))
8128 DrawPlayerField(x, y);
8130 DrawLevelField(x, y);
8135 else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
8137 if (Store[x][y] != EL_EMPTY)
8139 boolean can_clone = FALSE;
8142 /* check if element to clone is still there */
8143 for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
8145 if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == Store[x][y])
8153 /* cannot clone or target field not free anymore -- do not clone */
8154 if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8155 Store[x][y] = EL_EMPTY;
8158 if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8160 if (IS_MV_DIAGONAL(MovDir[x][y]))
8162 int diagonal_move_dir = MovDir[x][y];
8163 int stored = Store[x][y];
8164 int change_delay = 8;
8167 /* android is moving diagonally */
8169 CreateField(x, y, EL_DIAGONAL_SHRINKING);
8171 Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
8172 GfxElement[x][y] = EL_EMC_ANDROID;
8173 GfxAction[x][y] = ACTION_SHRINKING;
8174 GfxDir[x][y] = diagonal_move_dir;
8175 ChangeDelay[x][y] = change_delay;
8177 graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
8180 DrawLevelGraphicAnimation(x, y, graphic);
8181 PlayLevelSoundAction(x, y, ACTION_SHRINKING);
8183 if (Feld[newx][newy] == EL_ACID)
8185 SplashAcid(newx, newy);
8190 CreateField(newx, newy, EL_DIAGONAL_GROWING);
8192 Store[newx][newy] = EL_EMC_ANDROID;
8193 GfxElement[newx][newy] = EL_EMC_ANDROID;
8194 GfxAction[newx][newy] = ACTION_GROWING;
8195 GfxDir[newx][newy] = diagonal_move_dir;
8196 ChangeDelay[newx][newy] = change_delay;
8198 graphic = el_act_dir2img(GfxElement[newx][newy],
8199 GfxAction[newx][newy], GfxDir[newx][newy]);
8201 DrawLevelGraphicAnimation(newx, newy, graphic);
8202 PlayLevelSoundAction(newx, newy, ACTION_GROWING);
8208 Feld[newx][newy] = EL_EMPTY;
8209 DrawLevelField(newx, newy);
8211 PlayLevelSoundAction(x, y, ACTION_DIGGING);
8214 else if (!IS_FREE(newx, newy))
8217 if (IS_PLAYER(x, y))
8218 DrawPlayerField(x, y);
8220 DrawLevelField(x, y);
8226 else if (IS_CUSTOM_ELEMENT(element) &&
8227 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
8229 int new_element = Feld[newx][newy];
8231 if (!IS_FREE(newx, newy))
8233 int action = (IS_DIGGABLE(new_element) ? ACTION_DIGGING :
8234 IS_COLLECTIBLE(new_element) ? ACTION_COLLECTING :
8237 /* no element can dig solid indestructible elements */
8238 if (IS_INDESTRUCTIBLE(new_element) &&
8239 !IS_DIGGABLE(new_element) &&
8240 !IS_COLLECTIBLE(new_element))
8243 if (AmoebaNr[newx][newy] &&
8244 (new_element == EL_AMOEBA_FULL ||
8245 new_element == EL_BD_AMOEBA ||
8246 new_element == EL_AMOEBA_GROWING))
8248 AmoebaCnt[AmoebaNr[newx][newy]]--;
8249 AmoebaCnt2[AmoebaNr[newx][newy]]--;
8252 if (IS_MOVING(newx, newy))
8253 RemoveMovingField(newx, newy);
8256 RemoveField(newx, newy);
8257 DrawLevelField(newx, newy);
8260 /* if digged element was about to explode, prevent the explosion */
8261 ExplodeField[newx][newy] = EX_TYPE_NONE;
8263 PlayLevelSoundAction(x, y, action);
8266 Store[newx][newy] = EL_EMPTY;
8268 /* this makes it possible to leave the removed element again */
8269 if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
8270 Store[newx][newy] = new_element;
8272 if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
8274 int move_leave_element = element_info[element].move_leave_element;
8276 /* this makes it possible to leave the removed element again */
8277 Store[newx][newy] = (move_leave_element == EL_TRIGGER_ELEMENT ?
8278 new_element : move_leave_element);
8282 if (move_pattern & MV_MAZE_RUNNER_STYLE)
8284 RunnerVisit[x][y] = FrameCounter;
8285 PlayerVisit[x][y] /= 8; /* expire player visit path */
8288 else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
8290 if (!IS_FREE(newx, newy))
8292 if (IS_PLAYER(x, y))
8293 DrawPlayerField(x, y);
8295 DrawLevelField(x, y);
8301 boolean wanna_flame = !RND(10);
8302 int dx = newx - x, dy = newy - y;
8303 int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
8304 int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
8305 int element1 = (IN_LEV_FIELD(newx1, newy1) ?
8306 MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
8307 int element2 = (IN_LEV_FIELD(newx2, newy2) ?
8308 MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
8311 IS_CLASSIC_ENEMY(element1) ||
8312 IS_CLASSIC_ENEMY(element2)) &&
8313 element1 != EL_DRAGON && element2 != EL_DRAGON &&
8314 element1 != EL_FLAMES && element2 != EL_FLAMES)
8316 ResetGfxAnimation(x, y);
8317 GfxAction[x][y] = ACTION_ATTACKING;
8319 if (IS_PLAYER(x, y))
8320 DrawPlayerField(x, y);
8322 DrawLevelField(x, y);
8324 PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
8326 MovDelay[x][y] = 50;
8330 RemoveField(newx, newy);
8332 Feld[newx][newy] = EL_FLAMES;
8333 if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
8336 RemoveField(newx1, newy1);
8338 Feld[newx1][newy1] = EL_FLAMES;
8340 if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
8343 RemoveField(newx2, newy2);
8345 Feld[newx2][newy2] = EL_FLAMES;
8352 else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8353 Feld[newx][newy] == EL_DIAMOND)
8355 if (IS_MOVING(newx, newy))
8356 RemoveMovingField(newx, newy);
8359 Feld[newx][newy] = EL_EMPTY;
8360 DrawLevelField(newx, newy);
8363 PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
8365 else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8366 IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
8368 if (AmoebaNr[newx][newy])
8370 AmoebaCnt2[AmoebaNr[newx][newy]]--;
8371 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
8372 Feld[newx][newy] == EL_BD_AMOEBA)
8373 AmoebaCnt[AmoebaNr[newx][newy]]--;
8378 if (IS_MOVING(newx, newy) || IS_BLOCKED(newx, newy))
8380 RemoveMovingField(newx, newy);
8383 if (IS_MOVING(newx, newy))
8385 RemoveMovingField(newx, newy);
8390 Feld[newx][newy] = EL_EMPTY;
8391 DrawLevelField(newx, newy);
8394 PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
8396 else if ((element == EL_PACMAN || element == EL_MOLE)
8397 && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
8399 if (AmoebaNr[newx][newy])
8401 AmoebaCnt2[AmoebaNr[newx][newy]]--;
8402 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
8403 Feld[newx][newy] == EL_BD_AMOEBA)
8404 AmoebaCnt[AmoebaNr[newx][newy]]--;
8407 if (element == EL_MOLE)
8409 Feld[newx][newy] = EL_AMOEBA_SHRINKING;
8410 PlayLevelSound(x, y, SND_MOLE_DIGGING);
8412 ResetGfxAnimation(x, y);
8413 GfxAction[x][y] = ACTION_DIGGING;
8414 DrawLevelField(x, y);
8416 MovDelay[newx][newy] = 0; /* start amoeba shrinking delay */
8418 return; /* wait for shrinking amoeba */
8420 else /* element == EL_PACMAN */
8422 Feld[newx][newy] = EL_EMPTY;
8423 DrawLevelField(newx, newy);
8424 PlayLevelSound(x, y, SND_PACMAN_DIGGING);
8427 else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
8428 (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
8429 (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
8431 /* wait for shrinking amoeba to completely disappear */
8434 else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
8436 /* object was running against a wall */
8441 /* !!! NEW "CE_BLOCKED" STUFF !!! -- DOES NOT WORK YET... !!! */
8442 if (move_pattern & MV_ANY_DIRECTION &&
8443 move_pattern == MovDir[x][y])
8445 int blocking_element =
8446 (IN_LEV_FIELD(newx, newy) ? Feld[newx][newy] : BorderElement);
8448 CheckElementChangeBySide(x, y, element, blocking_element, CE_BLOCKED,
8451 element = Feld[x][y]; /* element might have changed */
8455 if (GFX_ELEMENT(element) != EL_SAND) /* !!! FIX THIS (crumble) !!! */
8456 DrawLevelElementAnimation(x, y, element);
8458 if (DONT_TOUCH(element))
8459 TestIfBadThingTouchesPlayer(x, y);
8464 InitMovingField(x, y, MovDir[x][y]);
8466 PlayLevelSoundAction(x, y, ACTION_MOVING);
8470 ContinueMoving(x, y);
8473 void ContinueMoving(int x, int y)
8475 int element = Feld[x][y];
8476 struct ElementInfo *ei = &element_info[element];
8477 int direction = MovDir[x][y];
8478 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
8479 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
8480 int newx = x + dx, newy = y + dy;
8481 int stored = Store[x][y];
8482 int stored_new = Store[newx][newy];
8483 boolean pushed_by_player = (Pushed[x][y] && IS_PLAYER(x, y));
8484 boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
8485 boolean last_line = (newy == lev_fieldy - 1);
8487 MovPos[x][y] += getElementMoveStepsize(x, y);
8489 if (pushed_by_player) /* special case: moving object pushed by player */
8490 MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
8492 if (ABS(MovPos[x][y]) < TILEX)
8495 int ee = Feld[x][y];
8496 int gg = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
8497 int ff = getGraphicAnimationFrame(gg, GfxFrame[x][y]);
8499 printf("::: %d.%d: moving %d ... [%d, %d, %d] [%d, %d, %d]\n",
8500 x, y, ABS(MovPos[x][y]),
8502 GfxAction[x][y], GfxDir[x][y], GfxFrame[x][y]);
8505 DrawLevelField(x, y);
8507 return; /* element is still moving */
8510 /* element reached destination field */
8512 Feld[x][y] = EL_EMPTY;
8513 Feld[newx][newy] = element;
8514 MovPos[x][y] = 0; /* force "not moving" for "crumbled sand" */
8516 if (Store[x][y] == EL_ACID) /* element is moving into acid pool */
8518 element = Feld[newx][newy] = EL_ACID;
8520 else if (element == EL_MOLE)
8522 Feld[x][y] = EL_SAND;
8524 DrawLevelFieldCrumbledSandNeighbours(x, y);
8526 else if (element == EL_QUICKSAND_FILLING)
8528 element = Feld[newx][newy] = get_next_element(element);
8529 Store[newx][newy] = Store[x][y];
8531 else if (element == EL_QUICKSAND_EMPTYING)
8533 Feld[x][y] = get_next_element(element);
8534 element = Feld[newx][newy] = Store[x][y];
8536 else if (element == EL_QUICKSAND_FAST_FILLING)
8538 element = Feld[newx][newy] = get_next_element(element);
8539 Store[newx][newy] = Store[x][y];
8541 else if (element == EL_QUICKSAND_FAST_EMPTYING)
8543 Feld[x][y] = get_next_element(element);
8544 element = Feld[newx][newy] = Store[x][y];
8546 else if (element == EL_MAGIC_WALL_FILLING)
8548 element = Feld[newx][newy] = get_next_element(element);
8549 if (!game.magic_wall_active)
8550 element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
8551 Store[newx][newy] = Store[x][y];
8553 else if (element == EL_MAGIC_WALL_EMPTYING)
8555 Feld[x][y] = get_next_element(element);
8556 if (!game.magic_wall_active)
8557 Feld[x][y] = EL_MAGIC_WALL_DEAD;
8558 element = Feld[newx][newy] = Store[x][y];
8560 #if USE_NEW_CUSTOM_VALUE
8561 InitField(newx, newy, FALSE);
8564 else if (element == EL_BD_MAGIC_WALL_FILLING)
8566 element = Feld[newx][newy] = get_next_element(element);
8567 if (!game.magic_wall_active)
8568 element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
8569 Store[newx][newy] = Store[x][y];
8571 else if (element == EL_BD_MAGIC_WALL_EMPTYING)
8573 Feld[x][y] = get_next_element(element);
8574 if (!game.magic_wall_active)
8575 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
8576 element = Feld[newx][newy] = Store[x][y];
8578 #if USE_NEW_CUSTOM_VALUE
8579 InitField(newx, newy, FALSE);
8582 else if (element == EL_DC_MAGIC_WALL_FILLING)
8584 element = Feld[newx][newy] = get_next_element(element);
8585 if (!game.magic_wall_active)
8586 element = Feld[newx][newy] = EL_DC_MAGIC_WALL_DEAD;
8587 Store[newx][newy] = Store[x][y];
8589 else if (element == EL_DC_MAGIC_WALL_EMPTYING)
8591 Feld[x][y] = get_next_element(element);
8592 if (!game.magic_wall_active)
8593 Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
8594 element = Feld[newx][newy] = Store[x][y];
8596 #if USE_NEW_CUSTOM_VALUE
8597 InitField(newx, newy, FALSE);
8600 else if (element == EL_AMOEBA_DROPPING)
8602 Feld[x][y] = get_next_element(element);
8603 element = Feld[newx][newy] = Store[x][y];
8605 else if (element == EL_SOKOBAN_OBJECT)
8608 Feld[x][y] = Back[x][y];
8610 if (Back[newx][newy])
8611 Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
8613 Back[x][y] = Back[newx][newy] = 0;
8616 Store[x][y] = EL_EMPTY;
8621 MovDelay[newx][newy] = 0;
8623 if (CAN_CHANGE_OR_HAS_ACTION(element))
8625 /* copy element change control values to new field */
8626 ChangeDelay[newx][newy] = ChangeDelay[x][y];
8627 ChangePage[newx][newy] = ChangePage[x][y];
8628 ChangeCount[newx][newy] = ChangeCount[x][y];
8629 ChangeEvent[newx][newy] = ChangeEvent[x][y];
8632 #if USE_NEW_CUSTOM_VALUE
8633 CustomValue[newx][newy] = CustomValue[x][y];
8636 ChangeDelay[x][y] = 0;
8637 ChangePage[x][y] = -1;
8638 ChangeCount[x][y] = 0;
8639 ChangeEvent[x][y] = -1;
8641 #if USE_NEW_CUSTOM_VALUE
8642 CustomValue[x][y] = 0;
8645 /* copy animation control values to new field */
8646 GfxFrame[newx][newy] = GfxFrame[x][y];
8647 GfxRandom[newx][newy] = GfxRandom[x][y]; /* keep same random value */
8648 GfxAction[newx][newy] = GfxAction[x][y]; /* keep action one frame */
8649 GfxDir[newx][newy] = GfxDir[x][y]; /* keep element direction */
8651 Pushed[x][y] = Pushed[newx][newy] = FALSE;
8653 /* some elements can leave other elements behind after moving */
8655 if (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)))
8659 if (IS_CUSTOM_ELEMENT(element) && ei->move_leave_element != EL_EMPTY &&
8660 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
8661 (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
8664 int move_leave_element = ei->move_leave_element;
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 == EL_ACID ? EL_EMPTY : stored);
8672 /* this makes it possible to leave the removed element again */
8673 if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
8674 move_leave_element = stored;
8677 /* this makes it possible to leave the removed element again */
8678 if (ei->move_leave_type == LEAVE_TYPE_LIMITED &&
8679 ei->move_leave_element == EL_TRIGGER_ELEMENT)
8680 move_leave_element = stored;
8683 Feld[x][y] = move_leave_element;
8685 if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
8686 MovDir[x][y] = direction;
8688 InitField(x, y, FALSE);
8690 if (GFX_CRUMBLED(Feld[x][y]))
8691 DrawLevelFieldCrumbledSandNeighbours(x, y);
8693 if (ELEM_IS_PLAYER(move_leave_element))
8694 RelocatePlayer(x, y, move_leave_element);
8697 /* do this after checking for left-behind element */
8698 ResetGfxAnimation(x, y); /* reset animation values for old field */
8700 if (!CAN_MOVE(element) ||
8701 (CAN_FALL(element) && direction == MV_DOWN &&
8702 (element == EL_SPRING ||
8703 element_info[element].move_pattern == MV_WHEN_PUSHED ||
8704 element_info[element].move_pattern == MV_WHEN_DROPPED)))
8705 GfxDir[x][y] = MovDir[newx][newy] = 0;
8707 DrawLevelField(x, y);
8708 DrawLevelField(newx, newy);
8710 Stop[newx][newy] = TRUE; /* ignore this element until the next frame */
8712 /* prevent pushed element from moving on in pushed direction */
8713 if (pushed_by_player && CAN_MOVE(element) &&
8714 element_info[element].move_pattern & MV_ANY_DIRECTION &&
8715 !(element_info[element].move_pattern & direction))
8716 TurnRound(newx, newy);
8718 /* prevent elements on conveyor belt from moving on in last direction */
8719 if (pushed_by_conveyor && CAN_FALL(element) &&
8720 direction & MV_HORIZONTAL)
8721 MovDir[newx][newy] = 0;
8723 if (!pushed_by_player)
8725 int nextx = newx + dx, nexty = newy + dy;
8726 boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
8728 WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
8730 if (CAN_FALL(element) && direction == MV_DOWN)
8731 WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
8733 if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
8734 CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
8736 #if USE_FIX_IMPACT_COLLISION
8737 if (CAN_FALL(element) && direction == MV_DOWN && check_collision_again)
8738 CheckImpact[newx][newy] = CHECK_DELAY_IMPACT;
8742 if (DONT_TOUCH(element)) /* object may be nasty to player or others */
8744 TestIfBadThingTouchesPlayer(newx, newy);
8745 TestIfBadThingTouchesFriend(newx, newy);
8747 if (!IS_CUSTOM_ELEMENT(element))
8748 TestIfBadThingTouchesOtherBadThing(newx, newy);
8750 else if (element == EL_PENGUIN)
8751 TestIfFriendTouchesBadThing(newx, newy);
8753 /* give the player one last chance (one more frame) to move away */
8754 if (CAN_FALL(element) && direction == MV_DOWN &&
8755 (last_line || (!IS_FREE(x, newy + 1) &&
8756 (!IS_PLAYER(x, newy + 1) ||
8757 game.engine_version < VERSION_IDENT(3,1,1,0)))))
8760 if (pushed_by_player && !game.use_change_when_pushing_bug)
8762 int push_side = MV_DIR_OPPOSITE(direction);
8763 struct PlayerInfo *player = PLAYERINFO(x, y);
8765 CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
8766 player->index_bit, push_side);
8767 CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
8768 player->index_bit, push_side);
8771 if (element == EL_EMC_ANDROID && pushed_by_player) /* make another move */
8772 MovDelay[newx][newy] = 1;
8774 CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
8776 TestIfElementTouchesCustomElement(x, y); /* empty or new element */
8779 if (ChangePage[newx][newy] != -1) /* delayed change */
8781 int page = ChangePage[newx][newy];
8782 struct ElementChangeInfo *change = &ei->change_page[page];
8784 ChangePage[newx][newy] = -1;
8786 if (change->can_change)
8788 if (ChangeElement(newx, newy, element, page))
8790 if (change->post_change_function)
8791 change->post_change_function(newx, newy);
8795 if (change->has_action)
8796 ExecuteCustomElementAction(newx, newy, element, page);
8800 TestIfElementHitsCustomElement(newx, newy, direction);
8801 TestIfPlayerTouchesCustomElement(newx, newy);
8802 TestIfElementTouchesCustomElement(newx, newy);
8804 if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
8805 IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
8806 CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
8807 MV_DIR_OPPOSITE(direction));
8810 int AmoebeNachbarNr(int ax, int ay)
8813 int element = Feld[ax][ay];
8815 static int xy[4][2] =
8823 for (i = 0; i < NUM_DIRECTIONS; i++)
8825 int x = ax + xy[i][0];
8826 int y = ay + xy[i][1];
8828 if (!IN_LEV_FIELD(x, y))
8831 if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
8832 group_nr = AmoebaNr[x][y];
8838 void AmoebenVereinigen(int ax, int ay)
8840 int i, x, y, xx, yy;
8841 int new_group_nr = AmoebaNr[ax][ay];
8842 static int xy[4][2] =
8850 if (new_group_nr == 0)
8853 for (i = 0; i < NUM_DIRECTIONS; i++)
8858 if (!IN_LEV_FIELD(x, y))
8861 if ((Feld[x][y] == EL_AMOEBA_FULL ||
8862 Feld[x][y] == EL_BD_AMOEBA ||
8863 Feld[x][y] == EL_AMOEBA_DEAD) &&
8864 AmoebaNr[x][y] != new_group_nr)
8866 int old_group_nr = AmoebaNr[x][y];
8868 if (old_group_nr == 0)
8871 AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
8872 AmoebaCnt[old_group_nr] = 0;
8873 AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
8874 AmoebaCnt2[old_group_nr] = 0;
8876 SCAN_PLAYFIELD(xx, yy)
8878 if (AmoebaNr[xx][yy] == old_group_nr)
8879 AmoebaNr[xx][yy] = new_group_nr;
8885 void AmoebeUmwandeln(int ax, int ay)
8889 if (Feld[ax][ay] == EL_AMOEBA_DEAD)
8891 int group_nr = AmoebaNr[ax][ay];
8896 printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
8897 printf("AmoebeUmwandeln(): This should never happen!\n");
8902 SCAN_PLAYFIELD(x, y)
8904 if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
8907 Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
8911 PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
8912 SND_AMOEBA_TURNING_TO_GEM :
8913 SND_AMOEBA_TURNING_TO_ROCK));
8918 static int xy[4][2] =
8926 for (i = 0; i < NUM_DIRECTIONS; i++)
8931 if (!IN_LEV_FIELD(x, y))
8934 if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
8936 PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
8937 SND_AMOEBA_TURNING_TO_GEM :
8938 SND_AMOEBA_TURNING_TO_ROCK));
8945 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
8948 int group_nr = AmoebaNr[ax][ay];
8949 boolean done = FALSE;
8954 printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
8955 printf("AmoebeUmwandelnBD(): This should never happen!\n");
8960 SCAN_PLAYFIELD(x, y)
8962 if (AmoebaNr[x][y] == group_nr &&
8963 (Feld[x][y] == EL_AMOEBA_DEAD ||
8964 Feld[x][y] == EL_BD_AMOEBA ||
8965 Feld[x][y] == EL_AMOEBA_GROWING))
8968 Feld[x][y] = new_element;
8969 InitField(x, y, FALSE);
8970 DrawLevelField(x, y);
8976 PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
8977 SND_BD_AMOEBA_TURNING_TO_ROCK :
8978 SND_BD_AMOEBA_TURNING_TO_GEM));
8981 void AmoebeWaechst(int x, int y)
8983 static unsigned long sound_delay = 0;
8984 static unsigned long sound_delay_value = 0;
8986 if (!MovDelay[x][y]) /* start new growing cycle */
8990 if (DelayReached(&sound_delay, sound_delay_value))
8992 PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
8993 sound_delay_value = 30;
8997 if (MovDelay[x][y]) /* wait some time before growing bigger */
9000 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9002 int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
9003 6 - MovDelay[x][y]);
9005 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
9008 if (!MovDelay[x][y])
9010 Feld[x][y] = Store[x][y];
9012 DrawLevelField(x, y);
9017 void AmoebaDisappearing(int x, int y)
9019 static unsigned long sound_delay = 0;
9020 static unsigned long sound_delay_value = 0;
9022 if (!MovDelay[x][y]) /* start new shrinking cycle */
9026 if (DelayReached(&sound_delay, sound_delay_value))
9027 sound_delay_value = 30;
9030 if (MovDelay[x][y]) /* wait some time before shrinking */
9033 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9035 int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
9036 6 - MovDelay[x][y]);
9038 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
9041 if (!MovDelay[x][y])
9043 Feld[x][y] = EL_EMPTY;
9044 DrawLevelField(x, y);
9046 /* don't let mole enter this field in this cycle;
9047 (give priority to objects falling to this field from above) */
9053 void AmoebeAbleger(int ax, int ay)
9056 int element = Feld[ax][ay];
9057 int graphic = el2img(element);
9058 int newax = ax, neway = ay;
9059 boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
9060 static int xy[4][2] =
9068 if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
9070 Feld[ax][ay] = EL_AMOEBA_DEAD;
9071 DrawLevelField(ax, ay);
9075 if (IS_ANIMATED(graphic))
9076 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9078 if (!MovDelay[ax][ay]) /* start making new amoeba field */
9079 MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
9081 if (MovDelay[ax][ay]) /* wait some time before making new amoeba */
9084 if (MovDelay[ax][ay])
9088 if (can_drop) /* EL_AMOEBA_WET or EL_EMC_DRIPPER */
9091 int x = ax + xy[start][0];
9092 int y = ay + xy[start][1];
9094 if (!IN_LEV_FIELD(x, y))
9097 if (IS_FREE(x, y) ||
9098 CAN_GROW_INTO(Feld[x][y]) ||
9099 Feld[x][y] == EL_QUICKSAND_EMPTY ||
9100 Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
9106 if (newax == ax && neway == ay)
9109 else /* normal or "filled" (BD style) amoeba */
9112 boolean waiting_for_player = FALSE;
9114 for (i = 0; i < NUM_DIRECTIONS; i++)
9116 int j = (start + i) % 4;
9117 int x = ax + xy[j][0];
9118 int y = ay + xy[j][1];
9120 if (!IN_LEV_FIELD(x, y))
9123 if (IS_FREE(x, y) ||
9124 CAN_GROW_INTO(Feld[x][y]) ||
9125 Feld[x][y] == EL_QUICKSAND_EMPTY ||
9126 Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
9132 else if (IS_PLAYER(x, y))
9133 waiting_for_player = TRUE;
9136 if (newax == ax && neway == ay) /* amoeba cannot grow */
9138 if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
9140 Feld[ax][ay] = EL_AMOEBA_DEAD;
9141 DrawLevelField(ax, ay);
9142 AmoebaCnt[AmoebaNr[ax][ay]]--;
9144 if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0) /* amoeba is completely dead */
9146 if (element == EL_AMOEBA_FULL)
9147 AmoebeUmwandeln(ax, ay);
9148 else if (element == EL_BD_AMOEBA)
9149 AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
9154 else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
9156 /* amoeba gets larger by growing in some direction */
9158 int new_group_nr = AmoebaNr[ax][ay];
9161 if (new_group_nr == 0)
9163 printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
9164 printf("AmoebeAbleger(): This should never happen!\n");
9169 AmoebaNr[newax][neway] = new_group_nr;
9170 AmoebaCnt[new_group_nr]++;
9171 AmoebaCnt2[new_group_nr]++;
9173 /* if amoeba touches other amoeba(s) after growing, unify them */
9174 AmoebenVereinigen(newax, neway);
9176 if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
9178 AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
9184 if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
9185 (neway == lev_fieldy - 1 && newax != ax))
9187 Feld[newax][neway] = EL_AMOEBA_GROWING; /* creation of new amoeba */
9188 Store[newax][neway] = element;
9190 else if (neway == ay || element == EL_EMC_DRIPPER)
9192 Feld[newax][neway] = EL_AMOEBA_DROP; /* drop left/right of amoeba */
9194 PlayLevelSoundAction(newax, neway, ACTION_GROWING);
9198 InitMovingField(ax, ay, MV_DOWN); /* drop dripping from amoeba */
9199 Feld[ax][ay] = EL_AMOEBA_DROPPING;
9200 Store[ax][ay] = EL_AMOEBA_DROP;
9201 ContinueMoving(ax, ay);
9205 DrawLevelField(newax, neway);
9208 void Life(int ax, int ay)
9212 int element = Feld[ax][ay];
9213 int graphic = el2img(element);
9214 int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
9216 boolean changed = FALSE;
9218 if (IS_ANIMATED(graphic))
9219 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9224 if (!MovDelay[ax][ay]) /* start new "game of life" cycle */
9225 MovDelay[ax][ay] = life_time;
9227 if (MovDelay[ax][ay]) /* wait some time before next cycle */
9230 if (MovDelay[ax][ay])
9234 for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
9236 int xx = ax+x1, yy = ay+y1;
9239 if (!IN_LEV_FIELD(xx, yy))
9242 for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
9244 int x = xx+x2, y = yy+y2;
9246 if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
9249 if (((Feld[x][y] == element ||
9250 (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
9252 (IS_FREE(x, y) && Stop[x][y]))
9256 if (xx == ax && yy == ay) /* field in the middle */
9258 if (nachbarn < life_parameter[0] ||
9259 nachbarn > life_parameter[1])
9261 Feld[xx][yy] = EL_EMPTY;
9263 DrawLevelField(xx, yy);
9264 Stop[xx][yy] = TRUE;
9268 else if (IS_FREE(xx, yy) || CAN_GROW_INTO(Feld[xx][yy]))
9269 { /* free border field */
9270 if (nachbarn >= life_parameter[2] &&
9271 nachbarn <= life_parameter[3])
9273 Feld[xx][yy] = element;
9274 MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
9276 DrawLevelField(xx, yy);
9277 Stop[xx][yy] = TRUE;
9284 PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
9285 SND_GAME_OF_LIFE_GROWING);
9288 static void InitRobotWheel(int x, int y)
9290 ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
9293 static void RunRobotWheel(int x, int y)
9295 PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
9298 static void StopRobotWheel(int x, int y)
9300 if (ZX == x && ZY == y)
9304 game.robot_wheel_active = FALSE;
9308 static void InitTimegateWheel(int x, int y)
9310 ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
9313 static void RunTimegateWheel(int x, int y)
9315 PlayLevelSound(x, y, SND_CLASS_TIMEGATE_SWITCH_ACTIVE);
9318 static void InitMagicBallDelay(int x, int y)
9321 ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
9323 ChangeDelay[x][y] = level.ball_time * FRAMES_PER_SECOND + 1;
9327 static void ActivateMagicBall(int bx, int by)
9331 if (level.ball_random)
9333 int pos_border = RND(8); /* select one of the eight border elements */
9334 int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
9335 int xx = pos_content % 3;
9336 int yy = pos_content / 3;
9341 if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
9342 CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9346 for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
9348 int xx = x - bx + 1;
9349 int yy = y - by + 1;
9351 if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
9352 CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9356 game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
9359 void CheckExit(int x, int y)
9361 if (local_player->gems_still_needed > 0 ||
9362 local_player->sokobanfields_still_needed > 0 ||
9363 local_player->lights_still_needed > 0)
9365 int element = Feld[x][y];
9366 int graphic = el2img(element);
9368 if (IS_ANIMATED(graphic))
9369 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9374 if (AllPlayersGone) /* do not re-open exit door closed after last player */
9377 Feld[x][y] = EL_EXIT_OPENING;
9379 PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
9382 void CheckExitEM(int x, int y)
9384 if (local_player->gems_still_needed > 0 ||
9385 local_player->sokobanfields_still_needed > 0 ||
9386 local_player->lights_still_needed > 0)
9388 int element = Feld[x][y];
9389 int graphic = el2img(element);
9391 if (IS_ANIMATED(graphic))
9392 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9397 if (AllPlayersGone) /* do not re-open exit door closed after last player */
9400 Feld[x][y] = EL_EM_EXIT_OPENING;
9402 PlayLevelSoundNearest(x, y, SND_CLASS_EM_EXIT_OPENING);
9405 void CheckExitSteel(int x, int y)
9407 if (local_player->gems_still_needed > 0 ||
9408 local_player->sokobanfields_still_needed > 0 ||
9409 local_player->lights_still_needed > 0)
9411 int element = Feld[x][y];
9412 int graphic = el2img(element);
9414 if (IS_ANIMATED(graphic))
9415 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9420 if (AllPlayersGone) /* do not re-open exit door closed after last player */
9423 Feld[x][y] = EL_STEEL_EXIT_OPENING;
9425 PlayLevelSoundNearest(x, y, SND_CLASS_STEEL_EXIT_OPENING);
9428 void CheckExitSteelEM(int x, int y)
9430 if (local_player->gems_still_needed > 0 ||
9431 local_player->sokobanfields_still_needed > 0 ||
9432 local_player->lights_still_needed > 0)
9434 int element = Feld[x][y];
9435 int graphic = el2img(element);
9437 if (IS_ANIMATED(graphic))
9438 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9443 if (AllPlayersGone) /* do not re-open exit door closed after last player */
9446 Feld[x][y] = EL_EM_STEEL_EXIT_OPENING;
9448 PlayLevelSoundNearest(x, y, SND_CLASS_EM_STEEL_EXIT_OPENING);
9451 void CheckExitSP(int x, int y)
9453 if (local_player->gems_still_needed > 0)
9455 int element = Feld[x][y];
9456 int graphic = el2img(element);
9458 if (IS_ANIMATED(graphic))
9459 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9464 if (AllPlayersGone) /* do not re-open exit door closed after last player */
9467 Feld[x][y] = EL_SP_EXIT_OPENING;
9469 PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
9472 static void CloseAllOpenTimegates()
9476 SCAN_PLAYFIELD(x, y)
9478 int element = Feld[x][y];
9480 if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
9482 Feld[x][y] = EL_TIMEGATE_CLOSING;
9484 PlayLevelSoundAction(x, y, ACTION_CLOSING);
9489 void DrawTwinkleOnField(int x, int y)
9491 if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
9494 if (Feld[x][y] == EL_BD_DIAMOND)
9497 if (MovDelay[x][y] == 0) /* next animation frame */
9498 MovDelay[x][y] = 11 * !GetSimpleRandom(500);
9500 if (MovDelay[x][y] != 0) /* wait some time before next frame */
9504 if (setup.direct_draw && MovDelay[x][y])
9505 SetDrawtoField(DRAW_BUFFERED);
9507 DrawLevelElementAnimation(x, y, Feld[x][y]);
9509 if (MovDelay[x][y] != 0)
9511 int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
9512 10 - MovDelay[x][y]);
9514 DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
9516 if (setup.direct_draw)
9520 dest_x = FX + SCREENX(x) * TILEX;
9521 dest_y = FY + SCREENY(y) * TILEY;
9523 BlitBitmap(drawto_field, window,
9524 dest_x, dest_y, TILEX, TILEY, dest_x, dest_y);
9525 SetDrawtoField(DRAW_DIRECT);
9531 void MauerWaechst(int x, int y)
9535 if (!MovDelay[x][y]) /* next animation frame */
9536 MovDelay[x][y] = 3 * delay;
9538 if (MovDelay[x][y]) /* wait some time before next frame */
9542 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9544 int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
9545 int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
9547 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
9550 if (!MovDelay[x][y])
9552 if (MovDir[x][y] == MV_LEFT)
9554 if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
9555 DrawLevelField(x - 1, y);
9557 else if (MovDir[x][y] == MV_RIGHT)
9559 if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
9560 DrawLevelField(x + 1, y);
9562 else if (MovDir[x][y] == MV_UP)
9564 if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
9565 DrawLevelField(x, y - 1);
9569 if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
9570 DrawLevelField(x, y + 1);
9573 Feld[x][y] = Store[x][y];
9575 GfxDir[x][y] = MovDir[x][y] = MV_NONE;
9576 DrawLevelField(x, y);
9581 void MauerAbleger(int ax, int ay)
9583 int element = Feld[ax][ay];
9584 int graphic = el2img(element);
9585 boolean oben_frei = FALSE, unten_frei = FALSE;
9586 boolean links_frei = FALSE, rechts_frei = FALSE;
9587 boolean oben_massiv = FALSE, unten_massiv = FALSE;
9588 boolean links_massiv = FALSE, rechts_massiv = FALSE;
9589 boolean new_wall = FALSE;
9591 if (IS_ANIMATED(graphic))
9592 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9594 if (!MovDelay[ax][ay]) /* start building new wall */
9595 MovDelay[ax][ay] = 6;
9597 if (MovDelay[ax][ay]) /* wait some time before building new wall */
9600 if (MovDelay[ax][ay])
9604 if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9606 if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9608 if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9610 if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9613 if (element == EL_EXPANDABLE_WALL_VERTICAL ||
9614 element == EL_EXPANDABLE_WALL_ANY)
9618 Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
9619 Store[ax][ay-1] = element;
9620 GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9621 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9622 DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9623 IMG_EXPANDABLE_WALL_GROWING_UP, 0);
9628 Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
9629 Store[ax][ay+1] = element;
9630 GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9631 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9632 DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9633 IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
9638 if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9639 element == EL_EXPANDABLE_WALL_ANY ||
9640 element == EL_EXPANDABLE_WALL ||
9641 element == EL_BD_EXPANDABLE_WALL)
9645 Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
9646 Store[ax-1][ay] = element;
9647 GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9648 if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9649 DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9650 IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
9656 Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
9657 Store[ax+1][ay] = element;
9658 GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9659 if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9660 DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9661 IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
9666 if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
9667 DrawLevelField(ax, ay);
9669 if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
9671 if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
9672 unten_massiv = TRUE;
9673 if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
9674 links_massiv = TRUE;
9675 if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
9676 rechts_massiv = TRUE;
9678 if (((oben_massiv && unten_massiv) ||
9679 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9680 element == EL_EXPANDABLE_WALL) &&
9681 ((links_massiv && rechts_massiv) ||
9682 element == EL_EXPANDABLE_WALL_VERTICAL))
9683 Feld[ax][ay] = EL_WALL;
9686 PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9689 void MauerAblegerStahl(int ax, int ay)
9691 int element = Feld[ax][ay];
9692 int graphic = el2img(element);
9693 boolean oben_frei = FALSE, unten_frei = FALSE;
9694 boolean links_frei = FALSE, rechts_frei = FALSE;
9695 boolean oben_massiv = FALSE, unten_massiv = FALSE;
9696 boolean links_massiv = FALSE, rechts_massiv = FALSE;
9697 boolean new_wall = FALSE;
9699 if (IS_ANIMATED(graphic))
9700 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9702 if (!MovDelay[ax][ay]) /* start building new wall */
9703 MovDelay[ax][ay] = 6;
9705 if (MovDelay[ax][ay]) /* wait some time before building new wall */
9708 if (MovDelay[ax][ay])
9712 if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9714 if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9716 if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9718 if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9721 if (element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
9722 element == EL_EXPANDABLE_STEELWALL_ANY)
9726 Feld[ax][ay-1] = EL_EXPANDABLE_STEELWALL_GROWING;
9727 Store[ax][ay-1] = element;
9728 GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9729 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9730 DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9731 IMG_EXPANDABLE_STEELWALL_GROWING_UP, 0);
9736 Feld[ax][ay+1] = EL_EXPANDABLE_STEELWALL_GROWING;
9737 Store[ax][ay+1] = element;
9738 GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9739 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9740 DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9741 IMG_EXPANDABLE_STEELWALL_GROWING_DOWN, 0);
9746 if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
9747 element == EL_EXPANDABLE_STEELWALL_ANY)
9751 Feld[ax-1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9752 Store[ax-1][ay] = element;
9753 GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9754 if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9755 DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9756 IMG_EXPANDABLE_STEELWALL_GROWING_LEFT, 0);
9762 Feld[ax+1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9763 Store[ax+1][ay] = element;
9764 GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9765 if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9766 DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9767 IMG_EXPANDABLE_STEELWALL_GROWING_RIGHT, 0);
9772 if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
9774 if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
9775 unten_massiv = TRUE;
9776 if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
9777 links_massiv = TRUE;
9778 if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
9779 rechts_massiv = TRUE;
9781 if (((oben_massiv && unten_massiv) ||
9782 element == EL_EXPANDABLE_STEELWALL_HORIZONTAL) &&
9783 ((links_massiv && rechts_massiv) ||
9784 element == EL_EXPANDABLE_STEELWALL_VERTICAL))
9785 Feld[ax][ay] = EL_WALL;
9788 PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9791 void CheckForDragon(int x, int y)
9794 boolean dragon_found = FALSE;
9795 static int xy[4][2] =
9803 for (i = 0; i < NUM_DIRECTIONS; i++)
9805 for (j = 0; j < 4; j++)
9807 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9809 if (IN_LEV_FIELD(xx, yy) &&
9810 (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
9812 if (Feld[xx][yy] == EL_DRAGON)
9813 dragon_found = TRUE;
9822 for (i = 0; i < NUM_DIRECTIONS; i++)
9824 for (j = 0; j < 3; j++)
9826 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9828 if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
9830 Feld[xx][yy] = EL_EMPTY;
9831 DrawLevelField(xx, yy);
9840 static void InitBuggyBase(int x, int y)
9842 int element = Feld[x][y];
9843 int activating_delay = FRAMES_PER_SECOND / 4;
9846 (element == EL_SP_BUGGY_BASE ?
9847 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
9848 element == EL_SP_BUGGY_BASE_ACTIVATING ?
9850 element == EL_SP_BUGGY_BASE_ACTIVE ?
9851 1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
9854 static void WarnBuggyBase(int x, int y)
9857 static int xy[4][2] =
9865 for (i = 0; i < NUM_DIRECTIONS; i++)
9867 int xx = x + xy[i][0];
9868 int yy = y + xy[i][1];
9870 if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
9872 PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
9879 static void InitTrap(int x, int y)
9881 ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
9884 static void ActivateTrap(int x, int y)
9886 PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
9889 static void ChangeActiveTrap(int x, int y)
9891 int graphic = IMG_TRAP_ACTIVE;
9893 /* if new animation frame was drawn, correct crumbled sand border */
9894 if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
9895 DrawLevelFieldCrumbledSand(x, y);
9898 static int getSpecialActionElement(int element, int number, int base_element)
9900 return (element != EL_EMPTY ? element :
9901 number != -1 ? base_element + number - 1 :
9905 static int getModifiedActionNumber(int value_old, int operator, int operand,
9906 int value_min, int value_max)
9908 int value_new = (operator == CA_MODE_SET ? operand :
9909 operator == CA_MODE_ADD ? value_old + operand :
9910 operator == CA_MODE_SUBTRACT ? value_old - operand :
9911 operator == CA_MODE_MULTIPLY ? value_old * operand :
9912 operator == CA_MODE_DIVIDE ? value_old / MAX(1, operand) :
9913 operator == CA_MODE_MODULO ? value_old % MAX(1, operand) :
9916 return (value_new < value_min ? value_min :
9917 value_new > value_max ? value_max :
9921 static void ExecuteCustomElementAction(int x, int y, int element, int page)
9923 struct ElementInfo *ei = &element_info[element];
9924 struct ElementChangeInfo *change = &ei->change_page[page];
9925 int target_element = change->target_element;
9926 int action_type = change->action_type;
9927 int action_mode = change->action_mode;
9928 int action_arg = change->action_arg;
9931 if (!change->has_action)
9934 /* ---------- determine action paramater values -------------------------- */
9936 int level_time_value =
9937 (level.time > 0 ? TimeLeft :
9940 int action_arg_element =
9941 (action_arg == CA_ARG_PLAYER_TRIGGER ? change->actual_trigger_player :
9942 action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
9943 action_arg == CA_ARG_ELEMENT_TARGET ? change->target_element :
9946 int action_arg_direction =
9947 (action_arg >= CA_ARG_DIRECTION_LEFT &&
9948 action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
9949 action_arg == CA_ARG_DIRECTION_TRIGGER ?
9950 change->actual_trigger_side :
9951 action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
9952 MV_DIR_OPPOSITE(change->actual_trigger_side) :
9955 int action_arg_number_min =
9956 (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
9959 int action_arg_number_max =
9960 (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
9961 action_type == CA_SET_LEVEL_GEMS ? 999 :
9962 action_type == CA_SET_LEVEL_TIME ? 9999 :
9963 action_type == CA_SET_LEVEL_SCORE ? 99999 :
9964 action_type == CA_SET_CE_VALUE ? 9999 :
9965 action_type == CA_SET_CE_SCORE ? 9999 :
9968 int action_arg_number_reset =
9969 (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize[0] :
9970 action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
9971 action_type == CA_SET_LEVEL_TIME ? level.time :
9972 action_type == CA_SET_LEVEL_SCORE ? 0 :
9973 #if USE_NEW_CUSTOM_VALUE
9974 action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
9976 action_type == CA_SET_CE_VALUE ? ei->custom_value_initial :
9978 action_type == CA_SET_CE_SCORE ? 0 :
9981 int action_arg_number =
9982 (action_arg <= CA_ARG_MAX ? action_arg :
9983 action_arg >= CA_ARG_SPEED_NOT_MOVING &&
9984 action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
9985 action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
9986 action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
9987 action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
9988 action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
9989 #if USE_NEW_CUSTOM_VALUE
9990 action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
9992 action_arg == CA_ARG_NUMBER_CE_VALUE ? ei->custom_value_initial :
9994 action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
9995 action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
9996 action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
9997 action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? local_player->gems_still_needed :
9998 action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? local_player->score :
9999 action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
10000 action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
10001 action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
10002 action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
10003 action_arg == CA_ARG_ELEMENT_NR_TARGET ? change->target_element :
10004 action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
10007 int action_arg_number_old =
10008 (action_type == CA_SET_LEVEL_GEMS ? local_player->gems_still_needed :
10009 action_type == CA_SET_LEVEL_TIME ? TimeLeft :
10010 action_type == CA_SET_LEVEL_SCORE ? local_player->score :
10011 action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
10012 action_type == CA_SET_CE_SCORE ? ei->collect_score :
10015 int action_arg_number_new =
10016 getModifiedActionNumber(action_arg_number_old,
10017 action_mode, action_arg_number,
10018 action_arg_number_min, action_arg_number_max);
10020 int trigger_player_bits =
10021 (change->actual_trigger_player >= EL_PLAYER_1 &&
10022 change->actual_trigger_player <= EL_PLAYER_4 ?
10023 (1 << (change->actual_trigger_player - EL_PLAYER_1)) :
10026 int action_arg_player_bits =
10027 (action_arg >= CA_ARG_PLAYER_1 &&
10028 action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
10029 action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
10032 /* ---------- execute action -------------------------------------------- */
10034 switch (action_type)
10041 /* ---------- level actions ------------------------------------------- */
10043 case CA_RESTART_LEVEL:
10045 game.restart_level = TRUE;
10050 case CA_SHOW_ENVELOPE:
10052 int element = getSpecialActionElement(action_arg_element,
10053 action_arg_number, EL_ENVELOPE_1);
10055 if (IS_ENVELOPE(element))
10056 local_player->show_envelope = element;
10061 case CA_SET_LEVEL_TIME:
10063 if (level.time > 0) /* only modify limited time value */
10065 TimeLeft = action_arg_number_new;
10068 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
10070 DisplayGameControlValues();
10072 DrawGameValue_Time(TimeLeft);
10075 if (!TimeLeft && setup.time_limit)
10076 for (i = 0; i < MAX_PLAYERS; i++)
10077 KillPlayer(&stored_player[i]);
10083 case CA_SET_LEVEL_SCORE:
10085 local_player->score = action_arg_number_new;
10088 game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
10090 DisplayGameControlValues();
10092 DrawGameValue_Score(local_player->score);
10098 case CA_SET_LEVEL_GEMS:
10100 local_player->gems_still_needed = action_arg_number_new;
10103 game_panel_controls[GAME_PANEL_GEMS].value = local_player->gems_still_needed;
10105 DisplayGameControlValues();
10107 DrawGameValue_Emeralds(local_player->gems_still_needed);
10113 #if !USE_PLAYER_GRAVITY
10114 case CA_SET_LEVEL_GRAVITY:
10116 game.gravity = (action_arg == CA_ARG_GRAVITY_OFF ? FALSE :
10117 action_arg == CA_ARG_GRAVITY_ON ? TRUE :
10118 action_arg == CA_ARG_GRAVITY_TOGGLE ? !game.gravity :
10124 case CA_SET_LEVEL_WIND:
10126 game.wind_direction = action_arg_direction;
10131 /* ---------- player actions ------------------------------------------ */
10133 case CA_MOVE_PLAYER:
10135 /* automatically move to the next field in specified direction */
10136 for (i = 0; i < MAX_PLAYERS; i++)
10137 if (trigger_player_bits & (1 << i))
10138 stored_player[i].programmed_action = action_arg_direction;
10143 case CA_EXIT_PLAYER:
10145 for (i = 0; i < MAX_PLAYERS; i++)
10146 if (action_arg_player_bits & (1 << i))
10147 PlayerWins(&stored_player[i]);
10152 case CA_KILL_PLAYER:
10154 for (i = 0; i < MAX_PLAYERS; i++)
10155 if (action_arg_player_bits & (1 << i))
10156 KillPlayer(&stored_player[i]);
10161 case CA_SET_PLAYER_KEYS:
10163 int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
10164 int element = getSpecialActionElement(action_arg_element,
10165 action_arg_number, EL_KEY_1);
10167 if (IS_KEY(element))
10169 for (i = 0; i < MAX_PLAYERS; i++)
10171 if (trigger_player_bits & (1 << i))
10173 stored_player[i].key[KEY_NR(element)] = key_state;
10175 DrawGameDoorValues();
10183 case CA_SET_PLAYER_SPEED:
10185 for (i = 0; i < MAX_PLAYERS; i++)
10187 if (trigger_player_bits & (1 << i))
10189 int move_stepsize = TILEX / stored_player[i].move_delay_value;
10191 if (action_arg == CA_ARG_SPEED_FASTER &&
10192 stored_player[i].cannot_move)
10194 action_arg_number = STEPSIZE_VERY_SLOW;
10196 else if (action_arg == CA_ARG_SPEED_SLOWER ||
10197 action_arg == CA_ARG_SPEED_FASTER)
10199 action_arg_number = 2;
10200 action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
10203 else if (action_arg == CA_ARG_NUMBER_RESET)
10205 action_arg_number = level.initial_player_stepsize[i];
10209 getModifiedActionNumber(move_stepsize,
10212 action_arg_number_min,
10213 action_arg_number_max);
10215 SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
10222 case CA_SET_PLAYER_SHIELD:
10224 for (i = 0; i < MAX_PLAYERS; i++)
10226 if (trigger_player_bits & (1 << i))
10228 if (action_arg == CA_ARG_SHIELD_OFF)
10230 stored_player[i].shield_normal_time_left = 0;
10231 stored_player[i].shield_deadly_time_left = 0;
10233 else if (action_arg == CA_ARG_SHIELD_NORMAL)
10235 stored_player[i].shield_normal_time_left = 999999;
10237 else if (action_arg == CA_ARG_SHIELD_DEADLY)
10239 stored_player[i].shield_normal_time_left = 999999;
10240 stored_player[i].shield_deadly_time_left = 999999;
10248 #if USE_PLAYER_GRAVITY
10249 case CA_SET_PLAYER_GRAVITY:
10251 for (i = 0; i < MAX_PLAYERS; i++)
10253 if (trigger_player_bits & (1 << i))
10255 stored_player[i].gravity =
10256 (action_arg == CA_ARG_GRAVITY_OFF ? FALSE :
10257 action_arg == CA_ARG_GRAVITY_ON ? TRUE :
10258 action_arg == CA_ARG_GRAVITY_TOGGLE ? !stored_player[i].gravity :
10259 stored_player[i].gravity);
10267 case CA_SET_PLAYER_ARTWORK:
10269 for (i = 0; i < MAX_PLAYERS; i++)
10271 if (trigger_player_bits & (1 << i))
10273 int artwork_element = action_arg_element;
10275 if (action_arg == CA_ARG_ELEMENT_RESET)
10277 (level.use_artwork_element[i] ? level.artwork_element[i] :
10278 stored_player[i].element_nr);
10280 #if USE_GFX_RESET_PLAYER_ARTWORK
10281 if (stored_player[i].artwork_element != artwork_element)
10282 stored_player[i].Frame = 0;
10285 stored_player[i].artwork_element = artwork_element;
10287 SetPlayerWaiting(&stored_player[i], FALSE);
10289 /* set number of special actions for bored and sleeping animation */
10290 stored_player[i].num_special_action_bored =
10291 get_num_special_action(artwork_element,
10292 ACTION_BORING_1, ACTION_BORING_LAST);
10293 stored_player[i].num_special_action_sleeping =
10294 get_num_special_action(artwork_element,
10295 ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
10302 /* ---------- CE actions ---------------------------------------------- */
10304 case CA_SET_CE_VALUE:
10306 #if USE_NEW_CUSTOM_VALUE
10307 int last_ce_value = CustomValue[x][y];
10309 CustomValue[x][y] = action_arg_number_new;
10311 if (CustomValue[x][y] != last_ce_value)
10313 CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_CHANGES);
10314 CheckTriggeredElementChange(x, y, element, CE_VALUE_CHANGES_OF_X);
10316 if (CustomValue[x][y] == 0)
10318 CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
10319 CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
10327 case CA_SET_CE_SCORE:
10329 #if USE_NEW_CUSTOM_VALUE
10330 int last_ce_score = ei->collect_score;
10332 ei->collect_score = action_arg_number_new;
10334 if (ei->collect_score != last_ce_score)
10336 CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
10337 CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
10339 if (ei->collect_score == 0)
10343 CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
10344 CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
10347 This is a very special case that seems to be a mixture between
10348 CheckElementChange() and CheckTriggeredElementChange(): while
10349 the first one only affects single elements that are triggered
10350 directly, the second one affects multiple elements in the playfield
10351 that are triggered indirectly by another element. This is a third
10352 case: Changing the CE score always affects multiple identical CEs,
10353 so every affected CE must be checked, not only the single CE for
10354 which the CE score was changed in the first place (as every instance
10355 of that CE shares the same CE score, and therefore also can change)!
10357 SCAN_PLAYFIELD(xx, yy)
10359 if (Feld[xx][yy] == element)
10360 CheckElementChange(xx, yy, element, EL_UNDEFINED,
10361 CE_SCORE_GETS_ZERO);
10370 /* ---------- engine actions ------------------------------------------ */
10372 case CA_SET_ENGINE_SCAN_MODE:
10374 InitPlayfieldScanMode(action_arg);
10384 static void CreateFieldExt(int x, int y, int element, boolean is_change)
10386 int old_element = Feld[x][y];
10387 int new_element = GetElementFromGroupElement(element);
10388 int previous_move_direction = MovDir[x][y];
10389 #if USE_NEW_CUSTOM_VALUE
10390 int last_ce_value = CustomValue[x][y];
10392 boolean player_explosion_protected = PLAYER_EXPLOSION_PROTECTED(x, y);
10393 boolean new_element_is_player = ELEM_IS_PLAYER(new_element);
10394 boolean add_player_onto_element = (new_element_is_player &&
10395 #if USE_CODE_THAT_BREAKS_SNAKE_BITE
10396 /* this breaks SnakeBite when a snake is
10397 halfway through a door that closes */
10398 /* NOW FIXED AT LEVEL INIT IN files.c */
10399 new_element != EL_SOKOBAN_FIELD_PLAYER &&
10401 IS_WALKABLE(old_element));
10404 /* check if element under the player changes from accessible to unaccessible
10405 (needed for special case of dropping element which then changes) */
10406 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
10407 IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
10415 if (!add_player_onto_element)
10417 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
10418 RemoveMovingField(x, y);
10422 Feld[x][y] = new_element;
10424 #if !USE_GFX_RESET_GFX_ANIMATION
10425 ResetGfxAnimation(x, y);
10426 ResetRandomAnimationValue(x, y);
10429 if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
10430 MovDir[x][y] = previous_move_direction;
10432 #if USE_NEW_CUSTOM_VALUE
10433 if (element_info[new_element].use_last_ce_value)
10434 CustomValue[x][y] = last_ce_value;
10437 InitField_WithBug1(x, y, FALSE);
10439 new_element = Feld[x][y]; /* element may have changed */
10441 #if USE_GFX_RESET_GFX_ANIMATION
10442 ResetGfxAnimation(x, y);
10443 ResetRandomAnimationValue(x, y);
10446 DrawLevelField(x, y);
10448 if (GFX_CRUMBLED(new_element))
10449 DrawLevelFieldCrumbledSandNeighbours(x, y);
10453 /* check if element under the player changes from accessible to unaccessible
10454 (needed for special case of dropping element which then changes) */
10455 /* (must be checked after creating new element for walkable group elements) */
10456 #if USE_FIX_KILLED_BY_NON_WALKABLE
10457 if (IS_PLAYER(x, y) && !player_explosion_protected &&
10458 IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
10465 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
10466 IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
10475 /* "ChangeCount" not set yet to allow "entered by player" change one time */
10476 if (new_element_is_player)
10477 RelocatePlayer(x, y, new_element);
10480 ChangeCount[x][y]++; /* count number of changes in the same frame */
10482 TestIfBadThingTouchesPlayer(x, y);
10483 TestIfPlayerTouchesCustomElement(x, y);
10484 TestIfElementTouchesCustomElement(x, y);
10487 static void CreateField(int x, int y, int element)
10489 CreateFieldExt(x, y, element, FALSE);
10492 static void CreateElementFromChange(int x, int y, int element)
10494 element = GET_VALID_RUNTIME_ELEMENT(element);
10496 #if USE_STOP_CHANGED_ELEMENTS
10497 if (game.engine_version >= VERSION_IDENT(3,2,0,7))
10499 int old_element = Feld[x][y];
10501 /* prevent changed element from moving in same engine frame
10502 unless both old and new element can either fall or move */
10503 if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
10504 (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
10509 CreateFieldExt(x, y, element, TRUE);
10512 static boolean ChangeElement(int x, int y, int element, int page)
10514 struct ElementInfo *ei = &element_info[element];
10515 struct ElementChangeInfo *change = &ei->change_page[page];
10516 int ce_value = CustomValue[x][y];
10517 int ce_score = ei->collect_score;
10518 int target_element;
10519 int old_element = Feld[x][y];
10521 /* always use default change event to prevent running into a loop */
10522 if (ChangeEvent[x][y] == -1)
10523 ChangeEvent[x][y] = CE_DELAY;
10525 if (ChangeEvent[x][y] == CE_DELAY)
10527 /* reset actual trigger element, trigger player and action element */
10528 change->actual_trigger_element = EL_EMPTY;
10529 change->actual_trigger_player = EL_PLAYER_1;
10530 change->actual_trigger_side = CH_SIDE_NONE;
10531 change->actual_trigger_ce_value = 0;
10532 change->actual_trigger_ce_score = 0;
10535 /* do not change elements more than a specified maximum number of changes */
10536 if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
10539 ChangeCount[x][y]++; /* count number of changes in the same frame */
10541 if (change->explode)
10548 if (change->use_target_content)
10550 boolean complete_replace = TRUE;
10551 boolean can_replace[3][3];
10554 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10557 boolean is_walkable;
10558 boolean is_diggable;
10559 boolean is_collectible;
10560 boolean is_removable;
10561 boolean is_destructible;
10562 int ex = x + xx - 1;
10563 int ey = y + yy - 1;
10564 int content_element = change->target_content.e[xx][yy];
10567 can_replace[xx][yy] = TRUE;
10569 if (ex == x && ey == y) /* do not check changing element itself */
10572 if (content_element == EL_EMPTY_SPACE)
10574 can_replace[xx][yy] = FALSE; /* do not replace border with space */
10579 if (!IN_LEV_FIELD(ex, ey))
10581 can_replace[xx][yy] = FALSE;
10582 complete_replace = FALSE;
10589 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10590 e = MovingOrBlocked2Element(ex, ey);
10592 is_empty = (IS_FREE(ex, ey) ||
10593 (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
10595 is_walkable = (is_empty || IS_WALKABLE(e));
10596 is_diggable = (is_empty || IS_DIGGABLE(e));
10597 is_collectible = (is_empty || IS_COLLECTIBLE(e));
10598 is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
10599 is_removable = (is_diggable || is_collectible);
10601 can_replace[xx][yy] =
10602 (((change->replace_when == CP_WHEN_EMPTY && is_empty) ||
10603 (change->replace_when == CP_WHEN_WALKABLE && is_walkable) ||
10604 (change->replace_when == CP_WHEN_DIGGABLE && is_diggable) ||
10605 (change->replace_when == CP_WHEN_COLLECTIBLE && is_collectible) ||
10606 (change->replace_when == CP_WHEN_REMOVABLE && is_removable) ||
10607 (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
10608 !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
10610 if (!can_replace[xx][yy])
10611 complete_replace = FALSE;
10614 if (!change->only_if_complete || complete_replace)
10616 boolean something_has_changed = FALSE;
10618 if (change->only_if_complete && change->use_random_replace &&
10619 RND(100) < change->random_percentage)
10622 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10624 int ex = x + xx - 1;
10625 int ey = y + yy - 1;
10626 int content_element;
10628 if (can_replace[xx][yy] && (!change->use_random_replace ||
10629 RND(100) < change->random_percentage))
10631 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10632 RemoveMovingField(ex, ey);
10634 ChangeEvent[ex][ey] = ChangeEvent[x][y];
10636 content_element = change->target_content.e[xx][yy];
10637 target_element = GET_TARGET_ELEMENT(element, content_element, change,
10638 ce_value, ce_score);
10640 CreateElementFromChange(ex, ey, target_element);
10642 something_has_changed = TRUE;
10644 /* for symmetry reasons, freeze newly created border elements */
10645 if (ex != x || ey != y)
10646 Stop[ex][ey] = TRUE; /* no more moving in this frame */
10650 if (something_has_changed)
10652 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10653 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10659 target_element = GET_TARGET_ELEMENT(element, change->target_element, change,
10660 ce_value, ce_score);
10662 if (element == EL_DIAGONAL_GROWING ||
10663 element == EL_DIAGONAL_SHRINKING)
10665 target_element = Store[x][y];
10667 Store[x][y] = EL_EMPTY;
10670 CreateElementFromChange(x, y, target_element);
10672 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10673 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10676 /* this uses direct change before indirect change */
10677 CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
10682 #if USE_NEW_DELAYED_ACTION
10684 static void HandleElementChange(int x, int y, int page)
10686 int element = MovingOrBlocked2Element(x, y);
10687 struct ElementInfo *ei = &element_info[element];
10688 struct ElementChangeInfo *change = &ei->change_page[page];
10691 if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
10692 !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
10695 printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
10696 x, y, element, element_info[element].token_name);
10697 printf("HandleElementChange(): This should never happen!\n");
10702 /* this can happen with classic bombs on walkable, changing elements */
10703 if (!CAN_CHANGE_OR_HAS_ACTION(element))
10706 if (!CAN_CHANGE(Back[x][y])) /* prevent permanent repetition */
10707 ChangeDelay[x][y] = 0;
10713 if (ChangeDelay[x][y] == 0) /* initialize element change */
10715 ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
10717 if (change->can_change)
10720 /* !!! not clear why graphic animation should be reset at all here !!! */
10721 /* !!! UPDATE: but is needed for correct Snake Bite tail animation !!! */
10722 #if USE_GFX_RESET_WHEN_NOT_MOVING
10723 /* when a custom element is about to change (for example by change delay),
10724 do not reset graphic animation when the custom element is moving */
10725 if (!IS_MOVING(x, y))
10728 ResetGfxAnimation(x, y);
10729 ResetRandomAnimationValue(x, y);
10733 if (change->pre_change_function)
10734 change->pre_change_function(x, y);
10738 ChangeDelay[x][y]--;
10740 if (ChangeDelay[x][y] != 0) /* continue element change */
10742 if (change->can_change)
10744 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10746 if (IS_ANIMATED(graphic))
10747 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10749 if (change->change_function)
10750 change->change_function(x, y);
10753 else /* finish element change */
10755 if (ChangePage[x][y] != -1) /* remember page from delayed change */
10757 page = ChangePage[x][y];
10758 ChangePage[x][y] = -1;
10760 change = &ei->change_page[page];
10763 if (IS_MOVING(x, y)) /* never change a running system ;-) */
10765 ChangeDelay[x][y] = 1; /* try change after next move step */
10766 ChangePage[x][y] = page; /* remember page to use for change */
10771 if (change->can_change)
10773 if (ChangeElement(x, y, element, page))
10775 if (change->post_change_function)
10776 change->post_change_function(x, y);
10780 if (change->has_action)
10781 ExecuteCustomElementAction(x, y, element, page);
10787 static void HandleElementChange(int x, int y, int page)
10789 int element = MovingOrBlocked2Element(x, y);
10790 struct ElementInfo *ei = &element_info[element];
10791 struct ElementChangeInfo *change = &ei->change_page[page];
10794 if (!CAN_CHANGE(element) && !CAN_CHANGE(Back[x][y]))
10797 printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
10798 x, y, element, element_info[element].token_name);
10799 printf("HandleElementChange(): This should never happen!\n");
10804 /* this can happen with classic bombs on walkable, changing elements */
10805 if (!CAN_CHANGE(element))
10808 if (!CAN_CHANGE(Back[x][y])) /* prevent permanent repetition */
10809 ChangeDelay[x][y] = 0;
10815 if (ChangeDelay[x][y] == 0) /* initialize element change */
10817 ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
10819 ResetGfxAnimation(x, y);
10820 ResetRandomAnimationValue(x, y);
10822 if (change->pre_change_function)
10823 change->pre_change_function(x, y);
10826 ChangeDelay[x][y]--;
10828 if (ChangeDelay[x][y] != 0) /* continue element change */
10830 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10832 if (IS_ANIMATED(graphic))
10833 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10835 if (change->change_function)
10836 change->change_function(x, y);
10838 else /* finish element change */
10840 if (ChangePage[x][y] != -1) /* remember page from delayed change */
10842 page = ChangePage[x][y];
10843 ChangePage[x][y] = -1;
10845 change = &ei->change_page[page];
10848 if (IS_MOVING(x, y)) /* never change a running system ;-) */
10850 ChangeDelay[x][y] = 1; /* try change after next move step */
10851 ChangePage[x][y] = page; /* remember page to use for change */
10856 if (ChangeElement(x, y, element, page))
10858 if (change->post_change_function)
10859 change->post_change_function(x, y);
10866 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
10867 int trigger_element,
10869 int trigger_player,
10873 boolean change_done_any = FALSE;
10874 int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
10877 if (!(trigger_events[trigger_element][trigger_event]))
10881 printf("::: CheckTriggeredElementChangeExt %d ... [%d, %d, %d, '%s']\n",
10882 trigger_event, recursion_loop_depth, recursion_loop_detected,
10883 recursion_loop_element, EL_NAME(recursion_loop_element));
10886 RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
10888 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
10890 int element = EL_CUSTOM_START + i;
10891 boolean change_done = FALSE;
10894 if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
10895 !HAS_ANY_CHANGE_EVENT(element, trigger_event))
10898 for (p = 0; p < element_info[element].num_change_pages; p++)
10900 struct ElementChangeInfo *change = &element_info[element].change_page[p];
10902 if (change->can_change_or_has_action &&
10903 change->has_event[trigger_event] &&
10904 change->trigger_side & trigger_side &&
10905 change->trigger_player & trigger_player &&
10906 change->trigger_page & trigger_page_bits &&
10907 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
10909 change->actual_trigger_element = trigger_element;
10910 change->actual_trigger_player = EL_PLAYER_1 + log_2(trigger_player);
10911 change->actual_trigger_side = trigger_side;
10912 change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
10913 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10915 if ((change->can_change && !change_done) || change->has_action)
10919 SCAN_PLAYFIELD(x, y)
10921 if (Feld[x][y] == element)
10923 if (change->can_change && !change_done)
10925 ChangeDelay[x][y] = 1;
10926 ChangeEvent[x][y] = trigger_event;
10928 HandleElementChange(x, y, p);
10930 #if USE_NEW_DELAYED_ACTION
10931 else if (change->has_action)
10933 ExecuteCustomElementAction(x, y, element, p);
10934 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10937 if (change->has_action)
10939 ExecuteCustomElementAction(x, y, element, p);
10940 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10946 if (change->can_change)
10948 change_done = TRUE;
10949 change_done_any = TRUE;
10956 RECURSION_LOOP_DETECTION_END();
10958 return change_done_any;
10961 static boolean CheckElementChangeExt(int x, int y,
10963 int trigger_element,
10965 int trigger_player,
10968 boolean change_done = FALSE;
10971 if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
10972 !HAS_ANY_CHANGE_EVENT(element, trigger_event))
10975 if (Feld[x][y] == EL_BLOCKED)
10977 Blocked2Moving(x, y, &x, &y);
10978 element = Feld[x][y];
10982 /* check if element has already changed */
10983 if (Feld[x][y] != element)
10986 /* check if element has already changed or is about to change after moving */
10987 if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
10988 Feld[x][y] != element) ||
10990 (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
10991 (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
10992 ChangePage[x][y] != -1)))
10997 printf("::: CheckElementChangeExt %d ... [%d, %d, %d, '%s']\n",
10998 trigger_event, recursion_loop_depth, recursion_loop_detected,
10999 recursion_loop_element, EL_NAME(recursion_loop_element));
11002 RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
11004 for (p = 0; p < element_info[element].num_change_pages; p++)
11006 struct ElementChangeInfo *change = &element_info[element].change_page[p];
11008 /* check trigger element for all events where the element that is checked
11009 for changing interacts with a directly adjacent element -- this is
11010 different to element changes that affect other elements to change on the
11011 whole playfield (which is handeld by CheckTriggeredElementChangeExt()) */
11012 boolean check_trigger_element =
11013 (trigger_event == CE_TOUCHING_X ||
11014 trigger_event == CE_HITTING_X ||
11015 trigger_event == CE_HIT_BY_X ||
11017 /* this one was forgotten until 3.2.3 */
11018 trigger_event == CE_DIGGING_X);
11021 if (change->can_change_or_has_action &&
11022 change->has_event[trigger_event] &&
11023 change->trigger_side & trigger_side &&
11024 change->trigger_player & trigger_player &&
11025 (!check_trigger_element ||
11026 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
11028 change->actual_trigger_element = trigger_element;
11029 change->actual_trigger_player = EL_PLAYER_1 + log_2(trigger_player);
11030 change->actual_trigger_side = trigger_side;
11031 change->actual_trigger_ce_value = CustomValue[x][y];
11032 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11034 /* special case: trigger element not at (x,y) position for some events */
11035 if (check_trigger_element)
11047 { 0, 0 }, { 0, 0 }, { 0, 0 },
11051 int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
11052 int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
11054 change->actual_trigger_ce_value = CustomValue[xx][yy];
11055 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11058 if (change->can_change && !change_done)
11060 ChangeDelay[x][y] = 1;
11061 ChangeEvent[x][y] = trigger_event;
11063 HandleElementChange(x, y, p);
11065 change_done = TRUE;
11067 #if USE_NEW_DELAYED_ACTION
11068 else if (change->has_action)
11070 ExecuteCustomElementAction(x, y, element, p);
11071 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11074 if (change->has_action)
11076 ExecuteCustomElementAction(x, y, element, p);
11077 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11083 RECURSION_LOOP_DETECTION_END();
11085 return change_done;
11088 static void PlayPlayerSound(struct PlayerInfo *player)
11090 int jx = player->jx, jy = player->jy;
11091 int sound_element = player->artwork_element;
11092 int last_action = player->last_action_waiting;
11093 int action = player->action_waiting;
11095 if (player->is_waiting)
11097 if (action != last_action)
11098 PlayLevelSoundElementAction(jx, jy, sound_element, action);
11100 PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
11104 if (action != last_action)
11105 StopSound(element_info[sound_element].sound[last_action]);
11107 if (last_action == ACTION_SLEEPING)
11108 PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
11112 static void PlayAllPlayersSound()
11116 for (i = 0; i < MAX_PLAYERS; i++)
11117 if (stored_player[i].active)
11118 PlayPlayerSound(&stored_player[i]);
11121 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
11123 boolean last_waiting = player->is_waiting;
11124 int move_dir = player->MovDir;
11126 player->dir_waiting = move_dir;
11127 player->last_action_waiting = player->action_waiting;
11131 if (!last_waiting) /* not waiting -> waiting */
11133 player->is_waiting = TRUE;
11135 player->frame_counter_bored =
11137 game.player_boring_delay_fixed +
11138 GetSimpleRandom(game.player_boring_delay_random);
11139 player->frame_counter_sleeping =
11141 game.player_sleeping_delay_fixed +
11142 GetSimpleRandom(game.player_sleeping_delay_random);
11144 InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
11147 if (game.player_sleeping_delay_fixed +
11148 game.player_sleeping_delay_random > 0 &&
11149 player->anim_delay_counter == 0 &&
11150 player->post_delay_counter == 0 &&
11151 FrameCounter >= player->frame_counter_sleeping)
11152 player->is_sleeping = TRUE;
11153 else if (game.player_boring_delay_fixed +
11154 game.player_boring_delay_random > 0 &&
11155 FrameCounter >= player->frame_counter_bored)
11156 player->is_bored = TRUE;
11158 player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
11159 player->is_bored ? ACTION_BORING :
11162 if (player->is_sleeping && player->use_murphy)
11164 /* special case for sleeping Murphy when leaning against non-free tile */
11166 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_LEFT;
11170 else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
11171 (Feld[player->jx + 1][player->jy] != EL_EMPTY &&
11172 !IS_MOVING(player->jx + 1, player->jy)))
11173 move_dir = MV_RIGHT;
11175 player->is_sleeping = FALSE;
11177 player->dir_waiting = move_dir;
11180 if (player->is_sleeping)
11182 if (player->num_special_action_sleeping > 0)
11184 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11186 int last_special_action = player->special_action_sleeping;
11187 int num_special_action = player->num_special_action_sleeping;
11188 int special_action =
11189 (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
11190 last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
11191 last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
11192 last_special_action + 1 : ACTION_SLEEPING);
11193 int special_graphic =
11194 el_act_dir2img(player->artwork_element, special_action, move_dir);
11196 player->anim_delay_counter =
11197 graphic_info[special_graphic].anim_delay_fixed +
11198 GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
11199 player->post_delay_counter =
11200 graphic_info[special_graphic].post_delay_fixed +
11201 GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
11203 player->special_action_sleeping = special_action;
11206 if (player->anim_delay_counter > 0)
11208 player->action_waiting = player->special_action_sleeping;
11209 player->anim_delay_counter--;
11211 else if (player->post_delay_counter > 0)
11213 player->post_delay_counter--;
11217 else if (player->is_bored)
11219 if (player->num_special_action_bored > 0)
11221 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11223 int special_action =
11224 ACTION_BORING_1 + GetSimpleRandom(player->num_special_action_bored);
11225 int special_graphic =
11226 el_act_dir2img(player->artwork_element, special_action, move_dir);
11228 player->anim_delay_counter =
11229 graphic_info[special_graphic].anim_delay_fixed +
11230 GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
11231 player->post_delay_counter =
11232 graphic_info[special_graphic].post_delay_fixed +
11233 GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
11235 player->special_action_bored = special_action;
11238 if (player->anim_delay_counter > 0)
11240 player->action_waiting = player->special_action_bored;
11241 player->anim_delay_counter--;
11243 else if (player->post_delay_counter > 0)
11245 player->post_delay_counter--;
11250 else if (last_waiting) /* waiting -> not waiting */
11252 player->is_waiting = FALSE;
11253 player->is_bored = FALSE;
11254 player->is_sleeping = FALSE;
11256 player->frame_counter_bored = -1;
11257 player->frame_counter_sleeping = -1;
11259 player->anim_delay_counter = 0;
11260 player->post_delay_counter = 0;
11262 player->dir_waiting = player->MovDir;
11263 player->action_waiting = ACTION_DEFAULT;
11265 player->special_action_bored = ACTION_DEFAULT;
11266 player->special_action_sleeping = ACTION_DEFAULT;
11270 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
11272 boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
11273 int left = player_action & JOY_LEFT;
11274 int right = player_action & JOY_RIGHT;
11275 int up = player_action & JOY_UP;
11276 int down = player_action & JOY_DOWN;
11277 int button1 = player_action & JOY_BUTTON_1;
11278 int button2 = player_action & JOY_BUTTON_2;
11279 int dx = (left ? -1 : right ? 1 : 0);
11280 int dy = (up ? -1 : down ? 1 : 0);
11282 if (!player->active || tape.pausing)
11288 snapped = SnapField(player, dx, dy);
11292 dropped = DropElement(player);
11294 moved = MovePlayer(player, dx, dy);
11297 if (tape.single_step && tape.recording && !tape.pausing)
11299 if (button1 || (dropped && !moved))
11301 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
11302 SnapField(player, 0, 0); /* stop snapping */
11306 SetPlayerWaiting(player, FALSE);
11308 return player_action;
11312 /* no actions for this player (no input at player's configured device) */
11314 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
11315 SnapField(player, 0, 0);
11316 CheckGravityMovementWhenNotMoving(player);
11318 if (player->MovPos == 0)
11319 SetPlayerWaiting(player, TRUE);
11321 if (player->MovPos == 0) /* needed for tape.playing */
11322 player->is_moving = FALSE;
11324 player->is_dropping = FALSE;
11325 player->is_dropping_pressed = FALSE;
11326 player->drop_pressed_delay = 0;
11332 static void CheckLevelTime()
11336 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11338 if (level.native_em_level->lev->home == 0) /* all players at home */
11340 PlayerWins(local_player);
11342 AllPlayersGone = TRUE;
11344 level.native_em_level->lev->home = -1;
11347 if (level.native_em_level->ply[0]->alive == 0 &&
11348 level.native_em_level->ply[1]->alive == 0 &&
11349 level.native_em_level->ply[2]->alive == 0 &&
11350 level.native_em_level->ply[3]->alive == 0) /* all dead */
11351 AllPlayersGone = TRUE;
11354 if (TimeFrames >= FRAMES_PER_SECOND)
11359 for (i = 0; i < MAX_PLAYERS; i++)
11361 struct PlayerInfo *player = &stored_player[i];
11363 if (SHIELD_ON(player))
11365 player->shield_normal_time_left--;
11367 if (player->shield_deadly_time_left > 0)
11368 player->shield_deadly_time_left--;
11372 if (!local_player->LevelSolved && !level.use_step_counter)
11380 if (TimeLeft <= 10 && setup.time_limit)
11381 PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
11384 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
11386 DisplayGameControlValues();
11388 DrawGameValue_Time(TimeLeft);
11391 if (!TimeLeft && setup.time_limit)
11393 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11394 level.native_em_level->lev->killed_out_of_time = TRUE;
11396 for (i = 0; i < MAX_PLAYERS; i++)
11397 KillPlayer(&stored_player[i]);
11401 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
11403 game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
11405 DisplayGameControlValues();
11408 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
11409 DrawGameValue_Time(TimePlayed);
11412 level.native_em_level->lev->time =
11413 (level.time == 0 ? TimePlayed : TimeLeft);
11416 if (tape.recording || tape.playing)
11417 DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
11420 UpdateGameDoorValues();
11421 DrawGameDoorValues();
11424 void AdvanceFrameAndPlayerCounters(int player_nr)
11428 /* advance frame counters (global frame counter and time frame counter) */
11432 /* advance player counters (counters for move delay, move animation etc.) */
11433 for (i = 0; i < MAX_PLAYERS; i++)
11435 boolean advance_player_counters = (player_nr == -1 || player_nr == i);
11436 int move_delay_value = stored_player[i].move_delay_value;
11437 int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
11439 if (!advance_player_counters) /* not all players may be affected */
11442 #if USE_NEW_PLAYER_ANIM
11443 if (move_frames == 0) /* less than one move per game frame */
11445 int stepsize = TILEX / move_delay_value;
11446 int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
11447 int count = (stored_player[i].is_moving ?
11448 ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
11450 if (count % delay == 0)
11455 stored_player[i].Frame += move_frames;
11457 if (stored_player[i].MovPos != 0)
11458 stored_player[i].StepFrame += move_frames;
11460 if (stored_player[i].move_delay > 0)
11461 stored_player[i].move_delay--;
11463 /* due to bugs in previous versions, counter must count up, not down */
11464 if (stored_player[i].push_delay != -1)
11465 stored_player[i].push_delay++;
11467 if (stored_player[i].drop_delay > 0)
11468 stored_player[i].drop_delay--;
11470 if (stored_player[i].is_dropping_pressed)
11471 stored_player[i].drop_pressed_delay++;
11475 void StartGameActions(boolean init_network_game, boolean record_tape,
11478 unsigned long new_random_seed = InitRND(random_seed);
11481 TapeStartRecording(new_random_seed);
11483 #if defined(NETWORK_AVALIABLE)
11484 if (init_network_game)
11486 SendToServer_StartPlaying();
11497 static unsigned long game_frame_delay = 0;
11498 unsigned long game_frame_delay_value;
11499 byte *recorded_player_action;
11500 byte summarized_player_action = 0;
11501 byte tape_action[MAX_PLAYERS];
11504 /* detect endless loops, caused by custom element programming */
11505 if (recursion_loop_detected && recursion_loop_depth == 0)
11507 char *message = getStringCat3("Internal Error ! Element ",
11508 EL_NAME(recursion_loop_element),
11509 " caused endless loop ! Quit the game ?");
11511 Error(ERR_WARN, "element '%s' caused endless loop in game engine",
11512 EL_NAME(recursion_loop_element));
11514 RequestQuitGameExt(FALSE, level_editor_test_game, message);
11516 recursion_loop_detected = FALSE; /* if game should be continued */
11523 if (game.restart_level)
11524 StartGameActions(options.network, setup.autorecord, NEW_RANDOMIZE);
11526 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11528 if (level.native_em_level->lev->home == 0) /* all players at home */
11530 PlayerWins(local_player);
11532 AllPlayersGone = TRUE;
11534 level.native_em_level->lev->home = -1;
11537 if (level.native_em_level->ply[0]->alive == 0 &&
11538 level.native_em_level->ply[1]->alive == 0 &&
11539 level.native_em_level->ply[2]->alive == 0 &&
11540 level.native_em_level->ply[3]->alive == 0) /* all dead */
11541 AllPlayersGone = TRUE;
11544 if (local_player->LevelSolved && !local_player->LevelSolved_GameEnd)
11547 if (AllPlayersGone && !TAPE_IS_STOPPED(tape))
11550 if (game_status != GAME_MODE_PLAYING) /* status might have changed */
11553 game_frame_delay_value =
11554 (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
11556 if (tape.playing && tape.warp_forward && !tape.pausing)
11557 game_frame_delay_value = 0;
11559 /* ---------- main game synchronization point ---------- */
11561 WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11563 if (network_playing && !network_player_action_received)
11565 /* try to get network player actions in time */
11567 #if defined(NETWORK_AVALIABLE)
11568 /* last chance to get network player actions without main loop delay */
11569 HandleNetworking();
11572 /* game was quit by network peer */
11573 if (game_status != GAME_MODE_PLAYING)
11576 if (!network_player_action_received)
11577 return; /* failed to get network player actions in time */
11579 /* do not yet reset "network_player_action_received" (for tape.pausing) */
11585 /* at this point we know that we really continue executing the game */
11587 network_player_action_received = FALSE;
11589 /* when playing tape, read previously recorded player input from tape data */
11590 recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
11593 /* TapePlayAction() may return NULL when toggling to "pause before death" */
11598 if (tape.set_centered_player)
11600 game.centered_player_nr_next = tape.centered_player_nr_next;
11601 game.set_centered_player = TRUE;
11604 for (i = 0; i < MAX_PLAYERS; i++)
11606 summarized_player_action |= stored_player[i].action;
11608 if (!network_playing)
11609 stored_player[i].effective_action = stored_player[i].action;
11612 #if defined(NETWORK_AVALIABLE)
11613 if (network_playing)
11614 SendToServer_MovePlayer(summarized_player_action);
11617 if (!options.network && !setup.team_mode)
11618 local_player->effective_action = summarized_player_action;
11620 if (setup.team_mode && setup.input_on_focus && game.centered_player_nr != -1)
11622 for (i = 0; i < MAX_PLAYERS; i++)
11623 stored_player[i].effective_action =
11624 (i == game.centered_player_nr ? summarized_player_action : 0);
11627 if (recorded_player_action != NULL)
11628 for (i = 0; i < MAX_PLAYERS; i++)
11629 stored_player[i].effective_action = recorded_player_action[i];
11631 for (i = 0; i < MAX_PLAYERS; i++)
11633 tape_action[i] = stored_player[i].effective_action;
11635 /* (this can only happen in the R'n'D game engine) */
11636 if (tape.recording && tape_action[i] && !tape.player_participates[i])
11637 tape.player_participates[i] = TRUE; /* player just appeared from CE */
11640 /* only record actions from input devices, but not programmed actions */
11641 if (tape.recording)
11642 TapeRecordAction(tape_action);
11644 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11646 GameActions_EM_Main();
11654 void GameActions_EM_Main()
11656 byte effective_action[MAX_PLAYERS];
11657 boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11660 for (i = 0; i < MAX_PLAYERS; i++)
11661 effective_action[i] = stored_player[i].effective_action;
11663 GameActions_EM(effective_action, warp_mode);
11667 AdvanceFrameAndPlayerCounters(-1); /* advance counters for all players */
11670 void GameActions_RND()
11672 int magic_wall_x = 0, magic_wall_y = 0;
11673 int i, x, y, element, graphic;
11675 InitPlayfieldScanModeVars();
11677 #if USE_ONE_MORE_CHANGE_PER_FRAME
11678 if (game.engine_version >= VERSION_IDENT(3,2,0,7))
11680 SCAN_PLAYFIELD(x, y)
11682 ChangeCount[x][y] = 0;
11683 ChangeEvent[x][y] = -1;
11688 if (game.set_centered_player)
11690 boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
11692 /* switching to "all players" only possible if all players fit to screen */
11693 if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
11695 game.centered_player_nr_next = game.centered_player_nr;
11696 game.set_centered_player = FALSE;
11699 /* do not switch focus to non-existing (or non-active) player */
11700 if (game.centered_player_nr_next >= 0 &&
11701 !stored_player[game.centered_player_nr_next].active)
11703 game.centered_player_nr_next = game.centered_player_nr;
11704 game.set_centered_player = FALSE;
11708 if (game.set_centered_player &&
11709 ScreenMovPos == 0) /* screen currently aligned at tile position */
11713 if (game.centered_player_nr_next == -1)
11715 setScreenCenteredToAllPlayers(&sx, &sy);
11719 sx = stored_player[game.centered_player_nr_next].jx;
11720 sy = stored_player[game.centered_player_nr_next].jy;
11723 game.centered_player_nr = game.centered_player_nr_next;
11724 game.set_centered_player = FALSE;
11726 DrawRelocateScreen(0, 0, sx, sy, MV_NONE, TRUE, setup.quick_switch);
11727 DrawGameDoorValues();
11730 for (i = 0; i < MAX_PLAYERS; i++)
11732 int actual_player_action = stored_player[i].effective_action;
11735 /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
11736 - rnd_equinox_tetrachloride 048
11737 - rnd_equinox_tetrachloride_ii 096
11738 - rnd_emanuel_schmieg 002
11739 - doctor_sloan_ww 001, 020
11741 if (stored_player[i].MovPos == 0)
11742 CheckGravityMovement(&stored_player[i]);
11745 /* overwrite programmed action with tape action */
11746 if (stored_player[i].programmed_action)
11747 actual_player_action = stored_player[i].programmed_action;
11749 PlayerActions(&stored_player[i], actual_player_action);
11751 ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
11754 ScrollScreen(NULL, SCROLL_GO_ON);
11756 /* for backwards compatibility, the following code emulates a fixed bug that
11757 occured when pushing elements (causing elements that just made their last
11758 pushing step to already (if possible) make their first falling step in the
11759 same game frame, which is bad); this code is also needed to use the famous
11760 "spring push bug" which is used in older levels and might be wanted to be
11761 used also in newer levels, but in this case the buggy pushing code is only
11762 affecting the "spring" element and no other elements */
11764 if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
11766 for (i = 0; i < MAX_PLAYERS; i++)
11768 struct PlayerInfo *player = &stored_player[i];
11769 int x = player->jx;
11770 int y = player->jy;
11772 if (player->active && player->is_pushing && player->is_moving &&
11774 (game.engine_version < VERSION_IDENT(2,2,0,7) ||
11775 Feld[x][y] == EL_SPRING))
11777 ContinueMoving(x, y);
11779 /* continue moving after pushing (this is actually a bug) */
11780 if (!IS_MOVING(x, y))
11781 Stop[x][y] = FALSE;
11787 debug_print_timestamp(0, "start main loop profiling");
11790 SCAN_PLAYFIELD(x, y)
11792 ChangeCount[x][y] = 0;
11793 ChangeEvent[x][y] = -1;
11795 /* this must be handled before main playfield loop */
11796 if (Feld[x][y] == EL_PLAYER_IS_LEAVING)
11799 if (MovDelay[x][y] <= 0)
11803 #if USE_NEW_SNAP_DELAY
11804 if (Feld[x][y] == EL_ELEMENT_SNAPPING)
11807 if (MovDelay[x][y] <= 0)
11810 DrawLevelField(x, y);
11812 TestIfElementTouchesCustomElement(x, y); /* for empty space */
11818 if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
11820 printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
11821 printf("GameActions(): This should never happen!\n");
11823 ChangePage[x][y] = -1;
11827 Stop[x][y] = FALSE;
11828 if (WasJustMoving[x][y] > 0)
11829 WasJustMoving[x][y]--;
11830 if (WasJustFalling[x][y] > 0)
11831 WasJustFalling[x][y]--;
11832 if (CheckCollision[x][y] > 0)
11833 CheckCollision[x][y]--;
11834 if (CheckImpact[x][y] > 0)
11835 CheckImpact[x][y]--;
11839 /* reset finished pushing action (not done in ContinueMoving() to allow
11840 continuous pushing animation for elements with zero push delay) */
11841 if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
11843 ResetGfxAnimation(x, y);
11844 DrawLevelField(x, y);
11848 if (IS_BLOCKED(x, y))
11852 Blocked2Moving(x, y, &oldx, &oldy);
11853 if (!IS_MOVING(oldx, oldy))
11855 printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
11856 printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
11857 printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
11858 printf("GameActions(): This should never happen!\n");
11865 debug_print_timestamp(0, "- time for pre-main loop:");
11868 #if 0 // -------------------- !!! TEST ONLY !!! --------------------
11869 SCAN_PLAYFIELD(x, y)
11871 element = Feld[x][y];
11872 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11877 int element2 = element;
11878 int graphic2 = graphic;
11880 int element2 = Feld[x][y];
11881 int graphic2 = el_act_dir2img(element2, GfxAction[x][y], GfxDir[x][y]);
11883 int last_gfx_frame = GfxFrame[x][y];
11885 if (graphic_info[graphic2].anim_global_sync)
11886 GfxFrame[x][y] = FrameCounter;
11887 else if (ANIM_MODE(graphic2) == ANIM_CE_VALUE)
11888 GfxFrame[x][y] = CustomValue[x][y];
11889 else if (ANIM_MODE(graphic2) == ANIM_CE_SCORE)
11890 GfxFrame[x][y] = element_info[element2].collect_score;
11891 else if (ANIM_MODE(graphic2) == ANIM_CE_DELAY)
11892 GfxFrame[x][y] = ChangeDelay[x][y];
11894 if (redraw && GfxFrame[x][y] != last_gfx_frame)
11895 DrawLevelGraphicAnimation(x, y, graphic2);
11898 ResetGfxFrame(x, y, TRUE);
11902 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
11903 IS_NEXT_FRAME(GfxFrame[x][y], graphic))
11904 ResetRandomAnimationValue(x, y);
11908 SetRandomAnimationValue(x, y);
11912 PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
11915 #endif // -------------------- !!! TEST ONLY !!! --------------------
11918 debug_print_timestamp(0, "- time for TEST loop: -->");
11921 SCAN_PLAYFIELD(x, y)
11923 element = Feld[x][y];
11924 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11926 ResetGfxFrame(x, y, TRUE);
11928 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
11929 IS_NEXT_FRAME(GfxFrame[x][y], graphic))
11930 ResetRandomAnimationValue(x, y);
11932 SetRandomAnimationValue(x, y);
11934 PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
11936 if (IS_INACTIVE(element))
11938 if (IS_ANIMATED(graphic))
11939 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11944 /* this may take place after moving, so 'element' may have changed */
11945 if (IS_CHANGING(x, y) &&
11946 (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
11948 int page = element_info[element].event_page_nr[CE_DELAY];
11951 HandleElementChange(x, y, page);
11953 if (CAN_CHANGE(element))
11954 HandleElementChange(x, y, page);
11956 if (HAS_ACTION(element))
11957 ExecuteCustomElementAction(x, y, element, page);
11960 element = Feld[x][y];
11961 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11964 #if 0 // ---------------------------------------------------------------------
11966 if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
11970 element = Feld[x][y];
11971 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11973 if (IS_ANIMATED(graphic) &&
11974 !IS_MOVING(x, y) &&
11976 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11978 if (IS_GEM(element) || element == EL_SP_INFOTRON)
11979 DrawTwinkleOnField(x, y);
11981 else if (IS_MOVING(x, y))
11982 ContinueMoving(x, y);
11989 case EL_EM_EXIT_OPEN:
11990 case EL_SP_EXIT_OPEN:
11991 case EL_STEEL_EXIT_OPEN:
11992 case EL_EM_STEEL_EXIT_OPEN:
11993 case EL_SP_TERMINAL:
11994 case EL_SP_TERMINAL_ACTIVE:
11995 case EL_EXTRA_TIME:
11996 case EL_SHIELD_NORMAL:
11997 case EL_SHIELD_DEADLY:
11998 if (IS_ANIMATED(graphic))
11999 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12002 case EL_DYNAMITE_ACTIVE:
12003 case EL_EM_DYNAMITE_ACTIVE:
12004 case EL_DYNABOMB_PLAYER_1_ACTIVE:
12005 case EL_DYNABOMB_PLAYER_2_ACTIVE:
12006 case EL_DYNABOMB_PLAYER_3_ACTIVE:
12007 case EL_DYNABOMB_PLAYER_4_ACTIVE:
12008 case EL_SP_DISK_RED_ACTIVE:
12009 CheckDynamite(x, y);
12012 case EL_AMOEBA_GROWING:
12013 AmoebeWaechst(x, y);
12016 case EL_AMOEBA_SHRINKING:
12017 AmoebaDisappearing(x, y);
12020 #if !USE_NEW_AMOEBA_CODE
12021 case EL_AMOEBA_WET:
12022 case EL_AMOEBA_DRY:
12023 case EL_AMOEBA_FULL:
12025 case EL_EMC_DRIPPER:
12026 AmoebeAbleger(x, y);
12030 case EL_GAME_OF_LIFE:
12035 case EL_EXIT_CLOSED:
12039 case EL_EM_EXIT_CLOSED:
12043 case EL_STEEL_EXIT_CLOSED:
12044 CheckExitSteel(x, y);
12047 case EL_EM_STEEL_EXIT_CLOSED:
12048 CheckExitSteelEM(x, y);
12051 case EL_SP_EXIT_CLOSED:
12055 case EL_EXPANDABLE_WALL_GROWING:
12056 case EL_EXPANDABLE_STEELWALL_GROWING:
12057 MauerWaechst(x, y);
12060 case EL_EXPANDABLE_WALL:
12061 case EL_EXPANDABLE_WALL_HORIZONTAL:
12062 case EL_EXPANDABLE_WALL_VERTICAL:
12063 case EL_EXPANDABLE_WALL_ANY:
12064 case EL_BD_EXPANDABLE_WALL:
12065 MauerAbleger(x, y);
12068 case EL_EXPANDABLE_STEELWALL_HORIZONTAL:
12069 case EL_EXPANDABLE_STEELWALL_VERTICAL:
12070 case EL_EXPANDABLE_STEELWALL_ANY:
12071 MauerAblegerStahl(x, y);
12075 CheckForDragon(x, y);
12081 case EL_ELEMENT_SNAPPING:
12082 case EL_DIAGONAL_SHRINKING:
12083 case EL_DIAGONAL_GROWING:
12086 el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
12088 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12093 if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
12094 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12099 #else // ---------------------------------------------------------------------
12101 if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
12105 element = Feld[x][y];
12106 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12108 if (IS_ANIMATED(graphic) &&
12109 !IS_MOVING(x, y) &&
12111 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12113 if (IS_GEM(element) || element == EL_SP_INFOTRON)
12114 DrawTwinkleOnField(x, y);
12116 else if ((element == EL_ACID ||
12117 element == EL_EXIT_OPEN ||
12118 element == EL_EM_EXIT_OPEN ||
12119 element == EL_SP_EXIT_OPEN ||
12120 element == EL_STEEL_EXIT_OPEN ||
12121 element == EL_EM_STEEL_EXIT_OPEN ||
12122 element == EL_SP_TERMINAL ||
12123 element == EL_SP_TERMINAL_ACTIVE ||
12124 element == EL_EXTRA_TIME ||
12125 element == EL_SHIELD_NORMAL ||
12126 element == EL_SHIELD_DEADLY) &&
12127 IS_ANIMATED(graphic))
12128 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12129 else if (IS_MOVING(x, y))
12130 ContinueMoving(x, y);
12131 else if (IS_ACTIVE_BOMB(element))
12132 CheckDynamite(x, y);
12133 else if (element == EL_AMOEBA_GROWING)
12134 AmoebeWaechst(x, y);
12135 else if (element == EL_AMOEBA_SHRINKING)
12136 AmoebaDisappearing(x, y);
12138 #if !USE_NEW_AMOEBA_CODE
12139 else if (IS_AMOEBALIVE(element))
12140 AmoebeAbleger(x, y);
12143 else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
12145 else if (element == EL_EXIT_CLOSED)
12147 else if (element == EL_EM_EXIT_CLOSED)
12149 else if (element == EL_STEEL_EXIT_CLOSED)
12150 CheckExitSteel(x, y);
12151 else if (element == EL_EM_STEEL_EXIT_CLOSED)
12152 CheckExitSteelEM(x, y);
12153 else if (element == EL_SP_EXIT_CLOSED)
12155 else if (element == EL_EXPANDABLE_WALL_GROWING ||
12156 element == EL_EXPANDABLE_STEELWALL_GROWING)
12157 MauerWaechst(x, y);
12158 else if (element == EL_EXPANDABLE_WALL ||
12159 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
12160 element == EL_EXPANDABLE_WALL_VERTICAL ||
12161 element == EL_EXPANDABLE_WALL_ANY ||
12162 element == EL_BD_EXPANDABLE_WALL)
12163 MauerAbleger(x, y);
12164 else if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
12165 element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
12166 element == EL_EXPANDABLE_STEELWALL_ANY)
12167 MauerAblegerStahl(x, y);
12168 else if (element == EL_FLAMES)
12169 CheckForDragon(x, y);
12170 else if (element == EL_EXPLOSION)
12171 ; /* drawing of correct explosion animation is handled separately */
12172 else if (element == EL_ELEMENT_SNAPPING ||
12173 element == EL_DIAGONAL_SHRINKING ||
12174 element == EL_DIAGONAL_GROWING)
12176 graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
12178 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12180 else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
12181 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12183 #endif // ---------------------------------------------------------------------
12185 if (IS_BELT_ACTIVE(element))
12186 PlayLevelSoundAction(x, y, ACTION_ACTIVE);
12188 if (game.magic_wall_active)
12190 int jx = local_player->jx, jy = local_player->jy;
12192 /* play the element sound at the position nearest to the player */
12193 if ((element == EL_MAGIC_WALL_FULL ||
12194 element == EL_MAGIC_WALL_ACTIVE ||
12195 element == EL_MAGIC_WALL_EMPTYING ||
12196 element == EL_BD_MAGIC_WALL_FULL ||
12197 element == EL_BD_MAGIC_WALL_ACTIVE ||
12198 element == EL_BD_MAGIC_WALL_EMPTYING ||
12199 element == EL_DC_MAGIC_WALL_FULL ||
12200 element == EL_DC_MAGIC_WALL_ACTIVE ||
12201 element == EL_DC_MAGIC_WALL_EMPTYING) &&
12202 ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
12211 debug_print_timestamp(0, "- time for MAIN loop: -->");
12214 #if USE_NEW_AMOEBA_CODE
12215 /* new experimental amoeba growth stuff */
12216 if (!(FrameCounter % 8))
12218 static unsigned long random = 1684108901;
12220 for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
12222 x = RND(lev_fieldx);
12223 y = RND(lev_fieldy);
12224 element = Feld[x][y];
12226 if (!IS_PLAYER(x,y) &&
12227 (element == EL_EMPTY ||
12228 CAN_GROW_INTO(element) ||
12229 element == EL_QUICKSAND_EMPTY ||
12230 element == EL_QUICKSAND_FAST_EMPTY ||
12231 element == EL_ACID_SPLASH_LEFT ||
12232 element == EL_ACID_SPLASH_RIGHT))
12234 if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
12235 (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
12236 (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
12237 (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
12238 Feld[x][y] = EL_AMOEBA_DROP;
12241 random = random * 129 + 1;
12247 if (game.explosions_delayed)
12250 game.explosions_delayed = FALSE;
12252 SCAN_PLAYFIELD(x, y)
12254 element = Feld[x][y];
12256 if (ExplodeField[x][y])
12257 Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
12258 else if (element == EL_EXPLOSION)
12259 Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
12261 ExplodeField[x][y] = EX_TYPE_NONE;
12264 game.explosions_delayed = TRUE;
12267 if (game.magic_wall_active)
12269 if (!(game.magic_wall_time_left % 4))
12271 int element = Feld[magic_wall_x][magic_wall_y];
12273 if (element == EL_BD_MAGIC_WALL_FULL ||
12274 element == EL_BD_MAGIC_WALL_ACTIVE ||
12275 element == EL_BD_MAGIC_WALL_EMPTYING)
12276 PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
12277 else if (element == EL_DC_MAGIC_WALL_FULL ||
12278 element == EL_DC_MAGIC_WALL_ACTIVE ||
12279 element == EL_DC_MAGIC_WALL_EMPTYING)
12280 PlayLevelSound(magic_wall_x, magic_wall_y, SND_DC_MAGIC_WALL_ACTIVE);
12282 PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
12285 if (game.magic_wall_time_left > 0)
12287 game.magic_wall_time_left--;
12289 if (!game.magic_wall_time_left)
12291 SCAN_PLAYFIELD(x, y)
12293 element = Feld[x][y];
12295 if (element == EL_MAGIC_WALL_ACTIVE ||
12296 element == EL_MAGIC_WALL_FULL)
12298 Feld[x][y] = EL_MAGIC_WALL_DEAD;
12299 DrawLevelField(x, y);
12301 else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
12302 element == EL_BD_MAGIC_WALL_FULL)
12304 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
12305 DrawLevelField(x, y);
12307 else if (element == EL_DC_MAGIC_WALL_ACTIVE ||
12308 element == EL_DC_MAGIC_WALL_FULL)
12310 Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
12311 DrawLevelField(x, y);
12315 game.magic_wall_active = FALSE;
12320 if (game.light_time_left > 0)
12322 game.light_time_left--;
12324 if (game.light_time_left == 0)
12325 RedrawAllLightSwitchesAndInvisibleElements();
12328 if (game.timegate_time_left > 0)
12330 game.timegate_time_left--;
12332 if (game.timegate_time_left == 0)
12333 CloseAllOpenTimegates();
12336 if (game.lenses_time_left > 0)
12338 game.lenses_time_left--;
12340 if (game.lenses_time_left == 0)
12341 RedrawAllInvisibleElementsForLenses();
12344 if (game.magnify_time_left > 0)
12346 game.magnify_time_left--;
12348 if (game.magnify_time_left == 0)
12349 RedrawAllInvisibleElementsForMagnifier();
12352 for (i = 0; i < MAX_PLAYERS; i++)
12354 struct PlayerInfo *player = &stored_player[i];
12356 if (SHIELD_ON(player))
12358 if (player->shield_deadly_time_left)
12359 PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
12360 else if (player->shield_normal_time_left)
12361 PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
12368 PlayAllPlayersSound();
12370 if (options.debug) /* calculate frames per second */
12372 static unsigned long fps_counter = 0;
12373 static int fps_frames = 0;
12374 unsigned long fps_delay_ms = Counter() - fps_counter;
12378 if (fps_delay_ms >= 500) /* calculate fps every 0.5 seconds */
12380 global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
12383 fps_counter = Counter();
12386 redraw_mask |= REDRAW_FPS;
12389 AdvanceFrameAndPlayerCounters(-1); /* advance counters for all players */
12391 if (local_player->show_envelope != 0 && local_player->MovPos == 0)
12393 ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
12395 local_player->show_envelope = 0;
12399 debug_print_timestamp(0, "stop main loop profiling ");
12400 printf("----------------------------------------------------------\n");
12403 /* use random number generator in every frame to make it less predictable */
12404 if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12408 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
12410 int min_x = x, min_y = y, max_x = x, max_y = y;
12413 for (i = 0; i < MAX_PLAYERS; i++)
12415 int jx = stored_player[i].jx, jy = stored_player[i].jy;
12417 if (!stored_player[i].active || &stored_player[i] == player)
12420 min_x = MIN(min_x, jx);
12421 min_y = MIN(min_y, jy);
12422 max_x = MAX(max_x, jx);
12423 max_y = MAX(max_y, jy);
12426 return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
12429 static boolean AllPlayersInVisibleScreen()
12433 for (i = 0; i < MAX_PLAYERS; i++)
12435 int jx = stored_player[i].jx, jy = stored_player[i].jy;
12437 if (!stored_player[i].active)
12440 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12447 void ScrollLevel(int dx, int dy)
12450 static Bitmap *bitmap_db_field2 = NULL;
12451 int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
12458 /* !!! THIS IS APPARENTLY WRONG FOR PLAYER RELOCATION !!! */
12459 /* only horizontal XOR vertical scroll direction allowed */
12460 if ((dx == 0 && dy == 0) || (dx != 0 && dy != 0))
12465 if (bitmap_db_field2 == NULL)
12466 bitmap_db_field2 = CreateBitmap(FXSIZE, FYSIZE, DEFAULT_DEPTH);
12468 /* needed when blitting directly to same bitmap -- should not be needed with
12469 recent SDL libraries, but apparently does not work in 1.2.11 directly */
12470 BlitBitmap(drawto_field, bitmap_db_field2,
12471 FX + TILEX * (dx == -1) - softscroll_offset,
12472 FY + TILEY * (dy == -1) - softscroll_offset,
12473 SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
12474 SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
12475 FX + TILEX * (dx == 1) - softscroll_offset,
12476 FY + TILEY * (dy == 1) - softscroll_offset);
12477 BlitBitmap(bitmap_db_field2, drawto_field,
12478 FX + TILEX * (dx == 1) - softscroll_offset,
12479 FY + TILEY * (dy == 1) - softscroll_offset,
12480 SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
12481 SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
12482 FX + TILEX * (dx == 1) - softscroll_offset,
12483 FY + TILEY * (dy == 1) - softscroll_offset);
12488 /* !!! DOES NOT WORK FOR DIAGONAL PLAYER RELOCATION !!! */
12489 int xsize = (BX2 - BX1 + 1);
12490 int ysize = (BY2 - BY1 + 1);
12491 int start = (dx != 0 ? (dx == -1 ? BX1 : BX2) : (dy == -1 ? BY1 : BY2));
12492 int end = (dx != 0 ? (dx == -1 ? BX2 : BX1) : (dy == -1 ? BY2 : BY1));
12493 int step = (start < end ? +1 : -1);
12495 for (i = start; i != end; i += step)
12497 BlitBitmap(drawto_field, drawto_field,
12498 FX + TILEX * (dx != 0 ? i + step : 0),
12499 FY + TILEY * (dy != 0 ? i + step : 0),
12500 TILEX * (dx != 0 ? 1 : xsize),
12501 TILEY * (dy != 0 ? 1 : ysize),
12502 FX + TILEX * (dx != 0 ? i : 0),
12503 FY + TILEY * (dy != 0 ? i : 0));
12508 int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
12510 BlitBitmap(drawto_field, drawto_field,
12511 FX + TILEX * (dx == -1) - softscroll_offset,
12512 FY + TILEY * (dy == -1) - softscroll_offset,
12513 SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
12514 SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
12515 FX + TILEX * (dx == 1) - softscroll_offset,
12516 FY + TILEY * (dy == 1) - softscroll_offset);
12522 x = (dx == 1 ? BX1 : BX2);
12523 for (y = BY1; y <= BY2; y++)
12524 DrawScreenField(x, y);
12529 y = (dy == 1 ? BY1 : BY2);
12530 for (x = BX1; x <= BX2; x++)
12531 DrawScreenField(x, y);
12534 redraw_mask |= REDRAW_FIELD;
12537 static boolean canFallDown(struct PlayerInfo *player)
12539 int jx = player->jx, jy = player->jy;
12541 return (IN_LEV_FIELD(jx, jy + 1) &&
12542 (IS_FREE(jx, jy + 1) ||
12543 (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
12544 IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
12545 !IS_WALKABLE_INSIDE(Feld[jx][jy]));
12548 static boolean canPassField(int x, int y, int move_dir)
12550 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12551 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12552 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
12553 int nextx = x + dx;
12554 int nexty = y + dy;
12555 int element = Feld[x][y];
12557 return (IS_PASSABLE_FROM(element, opposite_dir) &&
12558 !CAN_MOVE(element) &&
12559 IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
12560 IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
12561 (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
12564 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
12566 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12567 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12568 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
12572 return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
12573 IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
12574 (IS_DIGGABLE(Feld[newx][newy]) ||
12575 IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
12576 canPassField(newx, newy, move_dir)));
12579 static void CheckGravityMovement(struct PlayerInfo *player)
12581 #if USE_PLAYER_GRAVITY
12582 if (player->gravity && !player->programmed_action)
12584 if (game.gravity && !player->programmed_action)
12587 int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
12588 int move_dir_vertical = player->effective_action & MV_VERTICAL;
12589 boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
12590 int jx = player->jx, jy = player->jy;
12591 boolean player_is_moving_to_valid_field =
12592 (!player_is_snapping &&
12593 (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
12594 canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
12595 boolean player_can_fall_down = canFallDown(player);
12597 if (player_can_fall_down &&
12598 !player_is_moving_to_valid_field)
12599 player->programmed_action = MV_DOWN;
12603 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
12605 return CheckGravityMovement(player);
12607 #if USE_PLAYER_GRAVITY
12608 if (player->gravity && !player->programmed_action)
12610 if (game.gravity && !player->programmed_action)
12613 int jx = player->jx, jy = player->jy;
12614 boolean field_under_player_is_free =
12615 (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
12616 boolean player_is_standing_on_valid_field =
12617 (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
12618 (IS_WALKABLE(Feld[jx][jy]) &&
12619 !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
12621 if (field_under_player_is_free && !player_is_standing_on_valid_field)
12622 player->programmed_action = MV_DOWN;
12627 MovePlayerOneStep()
12628 -----------------------------------------------------------------------------
12629 dx, dy: direction (non-diagonal) to try to move the player to
12630 real_dx, real_dy: direction as read from input device (can be diagonal)
12633 boolean MovePlayerOneStep(struct PlayerInfo *player,
12634 int dx, int dy, int real_dx, int real_dy)
12636 int jx = player->jx, jy = player->jy;
12637 int new_jx = jx + dx, new_jy = jy + dy;
12638 #if !USE_FIXED_DONT_RUN_INTO
12642 boolean player_can_move = !player->cannot_move;
12644 if (!player->active || (!dx && !dy))
12645 return MP_NO_ACTION;
12647 player->MovDir = (dx < 0 ? MV_LEFT :
12648 dx > 0 ? MV_RIGHT :
12650 dy > 0 ? MV_DOWN : MV_NONE);
12652 if (!IN_LEV_FIELD(new_jx, new_jy))
12653 return MP_NO_ACTION;
12655 if (!player_can_move)
12657 if (player->MovPos == 0)
12659 player->is_moving = FALSE;
12660 player->is_digging = FALSE;
12661 player->is_collecting = FALSE;
12662 player->is_snapping = FALSE;
12663 player->is_pushing = FALSE;
12668 if (!options.network && game.centered_player_nr == -1 &&
12669 !AllPlayersInSight(player, new_jx, new_jy))
12670 return MP_NO_ACTION;
12672 if (!options.network && !AllPlayersInSight(player, new_jx, new_jy))
12673 return MP_NO_ACTION;
12676 #if !USE_FIXED_DONT_RUN_INTO
12677 element = MovingOrBlocked2ElementIfNotLeaving(new_jx, new_jy);
12679 /* (moved to DigField()) */
12680 if (player_can_move && DONT_RUN_INTO(element))
12682 if (element == EL_ACID && dx == 0 && dy == 1)
12684 SplashAcid(new_jx, new_jy);
12685 Feld[jx][jy] = EL_PLAYER_1;
12686 InitMovingField(jx, jy, MV_DOWN);
12687 Store[jx][jy] = EL_ACID;
12688 ContinueMoving(jx, jy);
12689 BuryPlayer(player);
12692 TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
12698 can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
12699 if (can_move != MP_MOVING)
12702 /* check if DigField() has caused relocation of the player */
12703 if (player->jx != jx || player->jy != jy)
12704 return MP_NO_ACTION; /* <-- !!! CHECK THIS [-> MP_ACTION ?] !!! */
12706 StorePlayer[jx][jy] = 0;
12707 player->last_jx = jx;
12708 player->last_jy = jy;
12709 player->jx = new_jx;
12710 player->jy = new_jy;
12711 StorePlayer[new_jx][new_jy] = player->element_nr;
12713 if (player->move_delay_value_next != -1)
12715 player->move_delay_value = player->move_delay_value_next;
12716 player->move_delay_value_next = -1;
12720 (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
12722 player->step_counter++;
12724 PlayerVisit[jx][jy] = FrameCounter;
12726 #if USE_UFAST_PLAYER_EXIT_BUGFIX
12727 player->is_moving = TRUE;
12731 /* should better be called in MovePlayer(), but this breaks some tapes */
12732 ScrollPlayer(player, SCROLL_INIT);
12738 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
12740 int jx = player->jx, jy = player->jy;
12741 int old_jx = jx, old_jy = jy;
12742 int moved = MP_NO_ACTION;
12744 if (!player->active)
12749 if (player->MovPos == 0)
12751 player->is_moving = FALSE;
12752 player->is_digging = FALSE;
12753 player->is_collecting = FALSE;
12754 player->is_snapping = FALSE;
12755 player->is_pushing = FALSE;
12761 if (player->move_delay > 0)
12764 player->move_delay = -1; /* set to "uninitialized" value */
12766 /* store if player is automatically moved to next field */
12767 player->is_auto_moving = (player->programmed_action != MV_NONE);
12769 /* remove the last programmed player action */
12770 player->programmed_action = 0;
12772 if (player->MovPos)
12774 /* should only happen if pre-1.2 tape recordings are played */
12775 /* this is only for backward compatibility */
12777 int original_move_delay_value = player->move_delay_value;
12780 printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%ld]\n",
12784 /* scroll remaining steps with finest movement resolution */
12785 player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
12787 while (player->MovPos)
12789 ScrollPlayer(player, SCROLL_GO_ON);
12790 ScrollScreen(NULL, SCROLL_GO_ON);
12792 AdvanceFrameAndPlayerCounters(player->index_nr);
12798 player->move_delay_value = original_move_delay_value;
12801 player->is_active = FALSE;
12803 if (player->last_move_dir & MV_HORIZONTAL)
12805 if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
12806 moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
12810 if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
12811 moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
12814 #if USE_FIXED_BORDER_RUNNING_GFX
12815 if (!moved && !player->is_active)
12817 player->is_moving = FALSE;
12818 player->is_digging = FALSE;
12819 player->is_collecting = FALSE;
12820 player->is_snapping = FALSE;
12821 player->is_pushing = FALSE;
12829 if (moved & MP_MOVING && !ScreenMovPos &&
12830 (player->index_nr == game.centered_player_nr ||
12831 game.centered_player_nr == -1))
12833 if (moved & MP_MOVING && !ScreenMovPos &&
12834 (player == local_player || !options.network))
12837 int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
12838 int offset = game.scroll_delay_value;
12840 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12842 /* actual player has left the screen -- scroll in that direction */
12843 if (jx != old_jx) /* player has moved horizontally */
12844 scroll_x += (jx - old_jx);
12845 else /* player has moved vertically */
12846 scroll_y += (jy - old_jy);
12850 if (jx != old_jx) /* player has moved horizontally */
12852 if ((player->MovDir == MV_LEFT && scroll_x > jx - MIDPOSX + offset) ||
12853 (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
12854 scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
12856 /* don't scroll over playfield boundaries */
12857 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
12858 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
12860 /* don't scroll more than one field at a time */
12861 scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
12863 /* don't scroll against the player's moving direction */
12864 if ((player->MovDir == MV_LEFT && scroll_x > old_scroll_x) ||
12865 (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
12866 scroll_x = old_scroll_x;
12868 else /* player has moved vertically */
12870 if ((player->MovDir == MV_UP && scroll_y > jy - MIDPOSY + offset) ||
12871 (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
12872 scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
12874 /* don't scroll over playfield boundaries */
12875 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
12876 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
12878 /* don't scroll more than one field at a time */
12879 scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
12881 /* don't scroll against the player's moving direction */
12882 if ((player->MovDir == MV_UP && scroll_y > old_scroll_y) ||
12883 (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
12884 scroll_y = old_scroll_y;
12888 if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
12891 if (!options.network && game.centered_player_nr == -1 &&
12892 !AllPlayersInVisibleScreen())
12894 scroll_x = old_scroll_x;
12895 scroll_y = old_scroll_y;
12899 if (!options.network && !AllPlayersInVisibleScreen())
12901 scroll_x = old_scroll_x;
12902 scroll_y = old_scroll_y;
12907 ScrollScreen(player, SCROLL_INIT);
12908 ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
12913 player->StepFrame = 0;
12915 if (moved & MP_MOVING)
12917 if (old_jx != jx && old_jy == jy)
12918 player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
12919 else if (old_jx == jx && old_jy != jy)
12920 player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
12922 DrawLevelField(jx, jy); /* for "crumbled sand" */
12924 player->last_move_dir = player->MovDir;
12925 player->is_moving = TRUE;
12926 player->is_snapping = FALSE;
12927 player->is_switching = FALSE;
12928 player->is_dropping = FALSE;
12929 player->is_dropping_pressed = FALSE;
12930 player->drop_pressed_delay = 0;
12933 /* should better be called here than above, but this breaks some tapes */
12934 ScrollPlayer(player, SCROLL_INIT);
12939 CheckGravityMovementWhenNotMoving(player);
12941 player->is_moving = FALSE;
12943 /* at this point, the player is allowed to move, but cannot move right now
12944 (e.g. because of something blocking the way) -- ensure that the player
12945 is also allowed to move in the next frame (in old versions before 3.1.1,
12946 the player was forced to wait again for eight frames before next try) */
12948 if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12949 player->move_delay = 0; /* allow direct movement in the next frame */
12952 if (player->move_delay == -1) /* not yet initialized by DigField() */
12953 player->move_delay = player->move_delay_value;
12955 if (game.engine_version < VERSION_IDENT(3,0,7,0))
12957 TestIfPlayerTouchesBadThing(jx, jy);
12958 TestIfPlayerTouchesCustomElement(jx, jy);
12961 if (!player->active)
12962 RemovePlayer(player);
12967 void ScrollPlayer(struct PlayerInfo *player, int mode)
12969 int jx = player->jx, jy = player->jy;
12970 int last_jx = player->last_jx, last_jy = player->last_jy;
12971 int move_stepsize = TILEX / player->move_delay_value;
12973 #if USE_NEW_PLAYER_SPEED
12974 if (!player->active)
12977 if (player->MovPos == 0 && mode == SCROLL_GO_ON) /* player not moving */
12980 if (!player->active || player->MovPos == 0)
12984 if (mode == SCROLL_INIT)
12986 player->actual_frame_counter = FrameCounter;
12987 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
12989 if ((player->block_last_field || player->block_delay_adjustment > 0) &&
12990 Feld[last_jx][last_jy] == EL_EMPTY)
12992 int last_field_block_delay = 0; /* start with no blocking at all */
12993 int block_delay_adjustment = player->block_delay_adjustment;
12995 /* if player blocks last field, add delay for exactly one move */
12996 if (player->block_last_field)
12998 last_field_block_delay += player->move_delay_value;
13000 /* when blocking enabled, prevent moving up despite gravity */
13001 #if USE_PLAYER_GRAVITY
13002 if (player->gravity && player->MovDir == MV_UP)
13003 block_delay_adjustment = -1;
13005 if (game.gravity && player->MovDir == MV_UP)
13006 block_delay_adjustment = -1;
13010 /* add block delay adjustment (also possible when not blocking) */
13011 last_field_block_delay += block_delay_adjustment;
13013 Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
13014 MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
13017 #if USE_NEW_PLAYER_SPEED
13018 if (player->MovPos != 0) /* player has not yet reached destination */
13024 else if (!FrameReached(&player->actual_frame_counter, 1))
13027 #if USE_NEW_PLAYER_SPEED
13028 if (player->MovPos != 0)
13030 player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
13031 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13033 /* before DrawPlayer() to draw correct player graphic for this case */
13034 if (player->MovPos == 0)
13035 CheckGravityMovement(player);
13038 player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
13039 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13041 /* before DrawPlayer() to draw correct player graphic for this case */
13042 if (player->MovPos == 0)
13043 CheckGravityMovement(player);
13046 if (player->MovPos == 0) /* player reached destination field */
13048 if (player->move_delay_reset_counter > 0)
13050 player->move_delay_reset_counter--;
13052 if (player->move_delay_reset_counter == 0)
13054 /* continue with normal speed after quickly moving through gate */
13055 HALVE_PLAYER_SPEED(player);
13057 /* be able to make the next move without delay */
13058 player->move_delay = 0;
13062 player->last_jx = jx;
13063 player->last_jy = jy;
13065 if (Feld[jx][jy] == EL_EXIT_OPEN ||
13066 Feld[jx][jy] == EL_EM_EXIT_OPEN ||
13067 Feld[jx][jy] == EL_STEEL_EXIT_OPEN ||
13068 Feld[jx][jy] == EL_EM_STEEL_EXIT_OPEN ||
13069 Feld[jx][jy] == EL_SP_EXIT_OPEN ||
13070 Feld[jx][jy] == EL_SP_EXIT_OPENING) /* <-- special case */
13072 DrawPlayer(player); /* needed here only to cleanup last field */
13073 RemovePlayer(player);
13075 if (local_player->friends_still_needed == 0 ||
13076 IS_SP_ELEMENT(Feld[jx][jy]))
13077 PlayerWins(player);
13080 /* this breaks one level: "machine", level 000 */
13082 int move_direction = player->MovDir;
13083 int enter_side = MV_DIR_OPPOSITE(move_direction);
13084 int leave_side = move_direction;
13085 int old_jx = last_jx;
13086 int old_jy = last_jy;
13087 int old_element = Feld[old_jx][old_jy];
13088 int new_element = Feld[jx][jy];
13090 if (IS_CUSTOM_ELEMENT(old_element))
13091 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
13093 player->index_bit, leave_side);
13095 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
13096 CE_PLAYER_LEAVES_X,
13097 player->index_bit, leave_side);
13099 if (IS_CUSTOM_ELEMENT(new_element))
13100 CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
13101 player->index_bit, enter_side);
13103 CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
13104 CE_PLAYER_ENTERS_X,
13105 player->index_bit, enter_side);
13107 CheckTriggeredElementChangeBySide(jx, jy, player->element_nr,
13108 CE_MOVE_OF_X, move_direction);
13111 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13113 TestIfPlayerTouchesBadThing(jx, jy);
13114 TestIfPlayerTouchesCustomElement(jx, jy);
13116 /* needed because pushed element has not yet reached its destination,
13117 so it would trigger a change event at its previous field location */
13118 if (!player->is_pushing)
13119 TestIfElementTouchesCustomElement(jx, jy); /* for empty space */
13121 if (!player->active)
13122 RemovePlayer(player);
13125 if (!local_player->LevelSolved && level.use_step_counter)
13135 if (TimeLeft <= 10 && setup.time_limit)
13136 PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
13139 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
13141 DisplayGameControlValues();
13143 DrawGameValue_Time(TimeLeft);
13146 if (!TimeLeft && setup.time_limit)
13147 for (i = 0; i < MAX_PLAYERS; i++)
13148 KillPlayer(&stored_player[i]);
13151 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
13153 game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
13155 DisplayGameControlValues();
13158 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
13159 DrawGameValue_Time(TimePlayed);
13163 if (tape.single_step && tape.recording && !tape.pausing &&
13164 !player->programmed_action)
13165 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
13169 void ScrollScreen(struct PlayerInfo *player, int mode)
13171 static unsigned long screen_frame_counter = 0;
13173 if (mode == SCROLL_INIT)
13175 /* set scrolling step size according to actual player's moving speed */
13176 ScrollStepSize = TILEX / player->move_delay_value;
13178 screen_frame_counter = FrameCounter;
13179 ScreenMovDir = player->MovDir;
13180 ScreenMovPos = player->MovPos;
13181 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
13184 else if (!FrameReached(&screen_frame_counter, 1))
13189 ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
13190 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
13191 redraw_mask |= REDRAW_FIELD;
13194 ScreenMovDir = MV_NONE;
13197 void TestIfPlayerTouchesCustomElement(int x, int y)
13199 static int xy[4][2] =
13206 static int trigger_sides[4][2] =
13208 /* center side border side */
13209 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
13210 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
13211 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
13212 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
13214 static int touch_dir[4] =
13216 MV_LEFT | MV_RIGHT,
13221 int center_element = Feld[x][y]; /* should always be non-moving! */
13224 for (i = 0; i < NUM_DIRECTIONS; i++)
13226 int xx = x + xy[i][0];
13227 int yy = y + xy[i][1];
13228 int center_side = trigger_sides[i][0];
13229 int border_side = trigger_sides[i][1];
13230 int border_element;
13232 if (!IN_LEV_FIELD(xx, yy))
13235 if (IS_PLAYER(x, y))
13237 struct PlayerInfo *player = PLAYERINFO(x, y);
13239 if (game.engine_version < VERSION_IDENT(3,0,7,0))
13240 border_element = Feld[xx][yy]; /* may be moving! */
13241 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
13242 border_element = Feld[xx][yy];
13243 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
13244 border_element = MovingOrBlocked2Element(xx, yy);
13246 continue; /* center and border element do not touch */
13248 CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
13249 player->index_bit, border_side);
13250 CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
13251 CE_PLAYER_TOUCHES_X,
13252 player->index_bit, border_side);
13254 else if (IS_PLAYER(xx, yy))
13256 struct PlayerInfo *player = PLAYERINFO(xx, yy);
13258 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13260 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
13261 continue; /* center and border element do not touch */
13264 CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
13265 player->index_bit, center_side);
13266 CheckTriggeredElementChangeByPlayer(x, y, center_element,
13267 CE_PLAYER_TOUCHES_X,
13268 player->index_bit, center_side);
13274 #if USE_ELEMENT_TOUCHING_BUGFIX
13276 void TestIfElementTouchesCustomElement(int x, int y)
13278 static int xy[4][2] =
13285 static int trigger_sides[4][2] =
13287 /* center side border side */
13288 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
13289 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
13290 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
13291 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
13293 static int touch_dir[4] =
13295 MV_LEFT | MV_RIGHT,
13300 boolean change_center_element = FALSE;
13301 int center_element = Feld[x][y]; /* should always be non-moving! */
13302 int border_element_old[NUM_DIRECTIONS];
13305 for (i = 0; i < NUM_DIRECTIONS; i++)
13307 int xx = x + xy[i][0];
13308 int yy = y + xy[i][1];
13309 int border_element;
13311 border_element_old[i] = -1;
13313 if (!IN_LEV_FIELD(xx, yy))
13316 if (game.engine_version < VERSION_IDENT(3,0,7,0))
13317 border_element = Feld[xx][yy]; /* may be moving! */
13318 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
13319 border_element = Feld[xx][yy];
13320 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
13321 border_element = MovingOrBlocked2Element(xx, yy);
13323 continue; /* center and border element do not touch */
13325 border_element_old[i] = border_element;
13328 for (i = 0; i < NUM_DIRECTIONS; i++)
13330 int xx = x + xy[i][0];
13331 int yy = y + xy[i][1];
13332 int center_side = trigger_sides[i][0];
13333 int border_element = border_element_old[i];
13335 if (border_element == -1)
13338 /* check for change of border element */
13339 CheckElementChangeBySide(xx, yy, border_element, center_element,
13340 CE_TOUCHING_X, center_side);
13343 for (i = 0; i < NUM_DIRECTIONS; i++)
13345 int border_side = trigger_sides[i][1];
13346 int border_element = border_element_old[i];
13348 if (border_element == -1)
13351 /* check for change of center element (but change it only once) */
13352 if (!change_center_element)
13353 change_center_element =
13354 CheckElementChangeBySide(x, y, center_element, border_element,
13355 CE_TOUCHING_X, border_side);
13361 void TestIfElementTouchesCustomElement_OLD(int x, int y)
13363 static int xy[4][2] =
13370 static int trigger_sides[4][2] =
13372 /* center side border side */
13373 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
13374 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
13375 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
13376 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
13378 static int touch_dir[4] =
13380 MV_LEFT | MV_RIGHT,
13385 boolean change_center_element = FALSE;
13386 int center_element = Feld[x][y]; /* should always be non-moving! */
13389 for (i = 0; i < NUM_DIRECTIONS; i++)
13391 int xx = x + xy[i][0];
13392 int yy = y + xy[i][1];
13393 int center_side = trigger_sides[i][0];
13394 int border_side = trigger_sides[i][1];
13395 int border_element;
13397 if (!IN_LEV_FIELD(xx, yy))
13400 if (game.engine_version < VERSION_IDENT(3,0,7,0))
13401 border_element = Feld[xx][yy]; /* may be moving! */
13402 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
13403 border_element = Feld[xx][yy];
13404 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
13405 border_element = MovingOrBlocked2Element(xx, yy);
13407 continue; /* center and border element do not touch */
13409 /* check for change of center element (but change it only once) */
13410 if (!change_center_element)
13411 change_center_element =
13412 CheckElementChangeBySide(x, y, center_element, border_element,
13413 CE_TOUCHING_X, border_side);
13415 /* check for change of border element */
13416 CheckElementChangeBySide(xx, yy, border_element, center_element,
13417 CE_TOUCHING_X, center_side);
13423 void TestIfElementHitsCustomElement(int x, int y, int direction)
13425 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
13426 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
13427 int hitx = x + dx, hity = y + dy;
13428 int hitting_element = Feld[x][y];
13429 int touched_element;
13431 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
13434 touched_element = (IN_LEV_FIELD(hitx, hity) ?
13435 MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
13437 if (IN_LEV_FIELD(hitx, hity))
13439 int opposite_direction = MV_DIR_OPPOSITE(direction);
13440 int hitting_side = direction;
13441 int touched_side = opposite_direction;
13442 boolean object_hit = (!IS_MOVING(hitx, hity) ||
13443 MovDir[hitx][hity] != direction ||
13444 ABS(MovPos[hitx][hity]) <= TILEY / 2);
13450 CheckElementChangeBySide(x, y, hitting_element, touched_element,
13451 CE_HITTING_X, touched_side);
13453 CheckElementChangeBySide(hitx, hity, touched_element,
13454 hitting_element, CE_HIT_BY_X, hitting_side);
13456 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13457 CE_HIT_BY_SOMETHING, opposite_direction);
13461 /* "hitting something" is also true when hitting the playfield border */
13462 CheckElementChangeBySide(x, y, hitting_element, touched_element,
13463 CE_HITTING_SOMETHING, direction);
13467 void TestIfElementSmashesCustomElement(int x, int y, int direction)
13469 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
13470 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
13471 int hitx = x + dx, hity = y + dy;
13472 int hitting_element = Feld[x][y];
13473 int touched_element;
13475 boolean object_hit = (IN_LEV_FIELD(hitx, hity) &&
13476 !IS_FREE(hitx, hity) &&
13477 (!IS_MOVING(hitx, hity) ||
13478 MovDir[hitx][hity] != direction ||
13479 ABS(MovPos[hitx][hity]) <= TILEY / 2));
13482 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
13486 if (IN_LEV_FIELD(hitx, hity) && !object_hit)
13490 touched_element = (IN_LEV_FIELD(hitx, hity) ?
13491 MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
13493 CheckElementChangeBySide(x, y, hitting_element, touched_element,
13494 EP_CAN_SMASH_EVERYTHING, direction);
13496 if (IN_LEV_FIELD(hitx, hity))
13498 int opposite_direction = MV_DIR_OPPOSITE(direction);
13499 int hitting_side = direction;
13500 int touched_side = opposite_direction;
13502 int touched_element = MovingOrBlocked2Element(hitx, hity);
13505 boolean object_hit = (!IS_MOVING(hitx, hity) ||
13506 MovDir[hitx][hity] != direction ||
13507 ABS(MovPos[hitx][hity]) <= TILEY / 2);
13516 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13517 CE_SMASHED_BY_SOMETHING, opposite_direction);
13519 CheckElementChangeBySide(x, y, hitting_element, touched_element,
13520 CE_OTHER_IS_SMASHING, touched_side);
13522 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13523 CE_OTHER_GETS_SMASHED, hitting_side);
13529 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
13531 int i, kill_x = -1, kill_y = -1;
13533 int bad_element = -1;
13534 static int test_xy[4][2] =
13541 static int test_dir[4] =
13549 for (i = 0; i < NUM_DIRECTIONS; i++)
13551 int test_x, test_y, test_move_dir, test_element;
13553 test_x = good_x + test_xy[i][0];
13554 test_y = good_y + test_xy[i][1];
13556 if (!IN_LEV_FIELD(test_x, test_y))
13560 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13562 test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
13564 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13565 2nd case: DONT_TOUCH style bad thing does not move away from good thing
13567 if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
13568 (DONT_TOUCH(test_element) && test_move_dir != test_dir[i]))
13572 bad_element = test_element;
13578 if (kill_x != -1 || kill_y != -1)
13580 if (IS_PLAYER(good_x, good_y))
13582 struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
13584 if (player->shield_deadly_time_left > 0 &&
13585 !IS_INDESTRUCTIBLE(bad_element))
13586 Bang(kill_x, kill_y);
13587 else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
13588 KillPlayer(player);
13591 Bang(good_x, good_y);
13595 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
13597 int i, kill_x = -1, kill_y = -1;
13598 int bad_element = Feld[bad_x][bad_y];
13599 static int test_xy[4][2] =
13606 static int touch_dir[4] =
13608 MV_LEFT | MV_RIGHT,
13613 static int test_dir[4] =
13621 if (bad_element == EL_EXPLOSION) /* skip just exploding bad things */
13624 for (i = 0; i < NUM_DIRECTIONS; i++)
13626 int test_x, test_y, test_move_dir, test_element;
13628 test_x = bad_x + test_xy[i][0];
13629 test_y = bad_y + test_xy[i][1];
13630 if (!IN_LEV_FIELD(test_x, test_y))
13634 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13636 test_element = Feld[test_x][test_y];
13638 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13639 2nd case: DONT_TOUCH style bad thing does not move away from good thing
13641 if ((DONT_RUN_INTO(bad_element) && bad_move_dir == test_dir[i]) ||
13642 (DONT_TOUCH(bad_element) && test_move_dir != test_dir[i]))
13644 /* good thing is player or penguin that does not move away */
13645 if (IS_PLAYER(test_x, test_y))
13647 struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
13649 if (bad_element == EL_ROBOT && player->is_moving)
13650 continue; /* robot does not kill player if he is moving */
13652 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13654 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
13655 continue; /* center and border element do not touch */
13662 else if (test_element == EL_PENGUIN)
13671 if (kill_x != -1 || kill_y != -1)
13673 if (IS_PLAYER(kill_x, kill_y))
13675 struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
13677 if (player->shield_deadly_time_left > 0 &&
13678 !IS_INDESTRUCTIBLE(bad_element))
13679 Bang(bad_x, bad_y);
13680 else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
13681 KillPlayer(player);
13684 Bang(kill_x, kill_y);
13688 void TestIfPlayerTouchesBadThing(int x, int y)
13690 TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13693 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
13695 TestIfGoodThingHitsBadThing(x, y, move_dir);
13698 void TestIfBadThingTouchesPlayer(int x, int y)
13700 TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13703 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
13705 TestIfBadThingHitsGoodThing(x, y, move_dir);
13708 void TestIfFriendTouchesBadThing(int x, int y)
13710 TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13713 void TestIfBadThingTouchesFriend(int x, int y)
13715 TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13718 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
13720 int i, kill_x = bad_x, kill_y = bad_y;
13721 static int xy[4][2] =
13729 for (i = 0; i < NUM_DIRECTIONS; i++)
13733 x = bad_x + xy[i][0];
13734 y = bad_y + xy[i][1];
13735 if (!IN_LEV_FIELD(x, y))
13738 element = Feld[x][y];
13739 if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
13740 element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
13748 if (kill_x != bad_x || kill_y != bad_y)
13749 Bang(bad_x, bad_y);
13752 void KillPlayer(struct PlayerInfo *player)
13754 int jx = player->jx, jy = player->jy;
13756 if (!player->active)
13759 /* the following code was introduced to prevent an infinite loop when calling
13761 -> CheckTriggeredElementChangeExt()
13762 -> ExecuteCustomElementAction()
13764 -> (infinitely repeating the above sequence of function calls)
13765 which occurs when killing the player while having a CE with the setting
13766 "kill player X when explosion of <player X>"; the solution using a new
13767 field "player->killed" was chosen for backwards compatibility, although
13768 clever use of the fields "player->active" etc. would probably also work */
13770 if (player->killed)
13774 player->killed = TRUE;
13776 /* remove accessible field at the player's position */
13777 Feld[jx][jy] = EL_EMPTY;
13779 /* deactivate shield (else Bang()/Explode() would not work right) */
13780 player->shield_normal_time_left = 0;
13781 player->shield_deadly_time_left = 0;
13784 BuryPlayer(player);
13787 static void KillPlayerUnlessEnemyProtected(int x, int y)
13789 if (!PLAYER_ENEMY_PROTECTED(x, y))
13790 KillPlayer(PLAYERINFO(x, y));
13793 static void KillPlayerUnlessExplosionProtected(int x, int y)
13795 if (!PLAYER_EXPLOSION_PROTECTED(x, y))
13796 KillPlayer(PLAYERINFO(x, y));
13799 void BuryPlayer(struct PlayerInfo *player)
13801 int jx = player->jx, jy = player->jy;
13803 if (!player->active)
13806 PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
13807 PlayLevelSound(jx, jy, SND_GAME_LOSING);
13809 player->GameOver = TRUE;
13810 RemovePlayer(player);
13813 void RemovePlayer(struct PlayerInfo *player)
13815 int jx = player->jx, jy = player->jy;
13816 int i, found = FALSE;
13818 player->present = FALSE;
13819 player->active = FALSE;
13821 if (!ExplodeField[jx][jy])
13822 StorePlayer[jx][jy] = 0;
13824 if (player->is_moving)
13825 DrawLevelField(player->last_jx, player->last_jy);
13827 for (i = 0; i < MAX_PLAYERS; i++)
13828 if (stored_player[i].active)
13832 AllPlayersGone = TRUE;
13838 #if USE_NEW_SNAP_DELAY
13839 static void setFieldForSnapping(int x, int y, int element, int direction)
13841 struct ElementInfo *ei = &element_info[element];
13842 int direction_bit = MV_DIR_TO_BIT(direction);
13843 int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
13844 int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
13845 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
13847 Feld[x][y] = EL_ELEMENT_SNAPPING;
13848 MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
13850 ResetGfxAnimation(x, y);
13852 GfxElement[x][y] = element;
13853 GfxAction[x][y] = action;
13854 GfxDir[x][y] = direction;
13855 GfxFrame[x][y] = -1;
13860 =============================================================================
13861 checkDiagonalPushing()
13862 -----------------------------------------------------------------------------
13863 check if diagonal input device direction results in pushing of object
13864 (by checking if the alternative direction is walkable, diggable, ...)
13865 =============================================================================
13868 static boolean checkDiagonalPushing(struct PlayerInfo *player,
13869 int x, int y, int real_dx, int real_dy)
13871 int jx, jy, dx, dy, xx, yy;
13873 if (real_dx == 0 || real_dy == 0) /* no diagonal direction => push */
13876 /* diagonal direction: check alternative direction */
13881 xx = jx + (dx == 0 ? real_dx : 0);
13882 yy = jy + (dy == 0 ? real_dy : 0);
13884 return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
13888 =============================================================================
13890 -----------------------------------------------------------------------------
13891 x, y: field next to player (non-diagonal) to try to dig to
13892 real_dx, real_dy: direction as read from input device (can be diagonal)
13893 =============================================================================
13896 int DigField(struct PlayerInfo *player,
13897 int oldx, int oldy, int x, int y,
13898 int real_dx, int real_dy, int mode)
13900 boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
13901 boolean player_was_pushing = player->is_pushing;
13902 boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
13903 boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
13904 int jx = oldx, jy = oldy;
13905 int dx = x - jx, dy = y - jy;
13906 int nextx = x + dx, nexty = y + dy;
13907 int move_direction = (dx == -1 ? MV_LEFT :
13908 dx == +1 ? MV_RIGHT :
13910 dy == +1 ? MV_DOWN : MV_NONE);
13911 int opposite_direction = MV_DIR_OPPOSITE(move_direction);
13912 int dig_side = MV_DIR_OPPOSITE(move_direction);
13913 int old_element = Feld[jx][jy];
13914 #if USE_FIXED_DONT_RUN_INTO
13915 int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
13921 if (is_player) /* function can also be called by EL_PENGUIN */
13923 if (player->MovPos == 0)
13925 player->is_digging = FALSE;
13926 player->is_collecting = FALSE;
13929 if (player->MovPos == 0) /* last pushing move finished */
13930 player->is_pushing = FALSE;
13932 if (mode == DF_NO_PUSH) /* player just stopped pushing */
13934 player->is_switching = FALSE;
13935 player->push_delay = -1;
13937 return MP_NO_ACTION;
13941 #if !USE_FIXED_DONT_RUN_INTO
13942 if (IS_MOVING(x, y) || IS_PLAYER(x, y))
13943 return MP_NO_ACTION;
13946 if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
13947 old_element = Back[jx][jy];
13949 /* in case of element dropped at player position, check background */
13950 else if (Back[jx][jy] != EL_EMPTY &&
13951 game.engine_version >= VERSION_IDENT(2,2,0,0))
13952 old_element = Back[jx][jy];
13954 if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
13955 return MP_NO_ACTION; /* field has no opening in this direction */
13957 if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
13958 return MP_NO_ACTION; /* field has no opening in this direction */
13960 #if USE_FIXED_DONT_RUN_INTO
13961 if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
13965 Feld[jx][jy] = player->artwork_element;
13966 InitMovingField(jx, jy, MV_DOWN);
13967 Store[jx][jy] = EL_ACID;
13968 ContinueMoving(jx, jy);
13969 BuryPlayer(player);
13971 return MP_DONT_RUN_INTO;
13975 #if USE_FIXED_DONT_RUN_INTO
13976 if (player_can_move && DONT_RUN_INTO(element))
13978 TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
13980 return MP_DONT_RUN_INTO;
13984 #if USE_FIXED_DONT_RUN_INTO
13985 if (IS_MOVING(x, y) || IS_PLAYER(x, y))
13986 return MP_NO_ACTION;
13989 #if !USE_FIXED_DONT_RUN_INTO
13990 element = Feld[x][y];
13993 collect_count = element_info[element].collect_count_initial;
13995 if (!is_player && !IS_COLLECTIBLE(element)) /* penguin cannot collect it */
13996 return MP_NO_ACTION;
13998 if (game.engine_version < VERSION_IDENT(2,2,0,0))
13999 player_can_move = player_can_move_or_snap;
14001 if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
14002 game.engine_version >= VERSION_IDENT(2,2,0,0))
14004 CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
14005 player->index_bit, dig_side);
14006 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14007 player->index_bit, dig_side);
14009 if (element == EL_DC_LANDMINE)
14012 if (Feld[x][y] != element) /* field changed by snapping */
14015 return MP_NO_ACTION;
14018 #if USE_PLAYER_GRAVITY
14019 if (player->gravity && is_player && !player->is_auto_moving &&
14020 canFallDown(player) && move_direction != MV_DOWN &&
14021 !canMoveToValidFieldWithGravity(jx, jy, move_direction))
14022 return MP_NO_ACTION; /* player cannot walk here due to gravity */
14024 if (game.gravity && is_player && !player->is_auto_moving &&
14025 canFallDown(player) && move_direction != MV_DOWN &&
14026 !canMoveToValidFieldWithGravity(jx, jy, move_direction))
14027 return MP_NO_ACTION; /* player cannot walk here due to gravity */
14030 if (player_can_move &&
14031 IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
14033 int sound_element = SND_ELEMENT(element);
14034 int sound_action = ACTION_WALKING;
14036 if (IS_RND_GATE(element))
14038 if (!player->key[RND_GATE_NR(element)])
14039 return MP_NO_ACTION;
14041 else if (IS_RND_GATE_GRAY(element))
14043 if (!player->key[RND_GATE_GRAY_NR(element)])
14044 return MP_NO_ACTION;
14046 else if (IS_RND_GATE_GRAY_ACTIVE(element))
14048 if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
14049 return MP_NO_ACTION;
14051 else if (element == EL_EXIT_OPEN ||
14052 element == EL_EM_EXIT_OPEN ||
14053 element == EL_STEEL_EXIT_OPEN ||
14054 element == EL_EM_STEEL_EXIT_OPEN ||
14055 element == EL_SP_EXIT_OPEN ||
14056 element == EL_SP_EXIT_OPENING)
14058 sound_action = ACTION_PASSING; /* player is passing exit */
14060 else if (element == EL_EMPTY)
14062 sound_action = ACTION_MOVING; /* nothing to walk on */
14065 /* play sound from background or player, whatever is available */
14066 if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
14067 PlayLevelSoundElementAction(x, y, sound_element, sound_action);
14069 PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
14071 else if (player_can_move &&
14072 IS_PASSABLE(element) && canPassField(x, y, move_direction))
14074 if (!ACCESS_FROM(element, opposite_direction))
14075 return MP_NO_ACTION; /* field not accessible from this direction */
14077 if (CAN_MOVE(element)) /* only fixed elements can be passed! */
14078 return MP_NO_ACTION;
14080 if (IS_EM_GATE(element))
14082 if (!player->key[EM_GATE_NR(element)])
14083 return MP_NO_ACTION;
14085 else if (IS_EM_GATE_GRAY(element))
14087 if (!player->key[EM_GATE_GRAY_NR(element)])
14088 return MP_NO_ACTION;
14090 else if (IS_EM_GATE_GRAY_ACTIVE(element))
14092 if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
14093 return MP_NO_ACTION;
14095 else if (IS_EMC_GATE(element))
14097 if (!player->key[EMC_GATE_NR(element)])
14098 return MP_NO_ACTION;
14100 else if (IS_EMC_GATE_GRAY(element))
14102 if (!player->key[EMC_GATE_GRAY_NR(element)])
14103 return MP_NO_ACTION;
14105 else if (IS_EMC_GATE_GRAY_ACTIVE(element))
14107 if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
14108 return MP_NO_ACTION;
14110 else if (element == EL_DC_GATE_WHITE ||
14111 element == EL_DC_GATE_WHITE_GRAY ||
14112 element == EL_DC_GATE_WHITE_GRAY_ACTIVE)
14114 if (player->num_white_keys == 0)
14115 return MP_NO_ACTION;
14117 player->num_white_keys--;
14119 else if (IS_SP_PORT(element))
14121 if (element == EL_SP_GRAVITY_PORT_LEFT ||
14122 element == EL_SP_GRAVITY_PORT_RIGHT ||
14123 element == EL_SP_GRAVITY_PORT_UP ||
14124 element == EL_SP_GRAVITY_PORT_DOWN)
14125 #if USE_PLAYER_GRAVITY
14126 player->gravity = !player->gravity;
14128 game.gravity = !game.gravity;
14130 else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
14131 element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
14132 element == EL_SP_GRAVITY_ON_PORT_UP ||
14133 element == EL_SP_GRAVITY_ON_PORT_DOWN)
14134 #if USE_PLAYER_GRAVITY
14135 player->gravity = TRUE;
14137 game.gravity = TRUE;
14139 else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
14140 element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
14141 element == EL_SP_GRAVITY_OFF_PORT_UP ||
14142 element == EL_SP_GRAVITY_OFF_PORT_DOWN)
14143 #if USE_PLAYER_GRAVITY
14144 player->gravity = FALSE;
14146 game.gravity = FALSE;
14150 /* automatically move to the next field with double speed */
14151 player->programmed_action = move_direction;
14153 if (player->move_delay_reset_counter == 0)
14155 player->move_delay_reset_counter = 2; /* two double speed steps */
14157 DOUBLE_PLAYER_SPEED(player);
14160 PlayLevelSoundAction(x, y, ACTION_PASSING);
14162 else if (player_can_move_or_snap && IS_DIGGABLE(element))
14166 if (mode != DF_SNAP)
14168 GfxElement[x][y] = GFX_ELEMENT(element);
14169 player->is_digging = TRUE;
14172 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14174 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
14175 player->index_bit, dig_side);
14177 if (mode == DF_SNAP)
14179 #if USE_NEW_SNAP_DELAY
14180 if (level.block_snap_field)
14181 setFieldForSnapping(x, y, element, move_direction);
14183 TestIfElementTouchesCustomElement(x, y); /* for empty space */
14185 TestIfElementTouchesCustomElement(x, y); /* for empty space */
14188 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14189 player->index_bit, dig_side);
14192 else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
14196 if (is_player && mode != DF_SNAP)
14198 GfxElement[x][y] = element;
14199 player->is_collecting = TRUE;
14202 if (element == EL_SPEED_PILL)
14204 player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
14206 else if (element == EL_EXTRA_TIME && level.time > 0)
14208 TimeLeft += level.extra_time;
14211 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
14213 DisplayGameControlValues();
14215 DrawGameValue_Time(TimeLeft);
14218 else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
14220 player->shield_normal_time_left += level.shield_normal_time;
14221 if (element == EL_SHIELD_DEADLY)
14222 player->shield_deadly_time_left += level.shield_deadly_time;
14224 else if (element == EL_DYNAMITE ||
14225 element == EL_EM_DYNAMITE ||
14226 element == EL_SP_DISK_RED)
14228 if (player->inventory_size < MAX_INVENTORY_SIZE)
14229 player->inventory_element[player->inventory_size++] = element;
14231 DrawGameDoorValues();
14233 else if (element == EL_DYNABOMB_INCREASE_NUMBER)
14235 player->dynabomb_count++;
14236 player->dynabombs_left++;
14238 else if (element == EL_DYNABOMB_INCREASE_SIZE)
14240 player->dynabomb_size++;
14242 else if (element == EL_DYNABOMB_INCREASE_POWER)
14244 player->dynabomb_xl = TRUE;
14246 else if (IS_KEY(element))
14248 player->key[KEY_NR(element)] = TRUE;
14250 DrawGameDoorValues();
14252 else if (element == EL_DC_KEY_WHITE)
14254 player->num_white_keys++;
14256 /* display white keys? */
14257 /* DrawGameDoorValues(); */
14259 else if (IS_ENVELOPE(element))
14261 player->show_envelope = element;
14263 else if (element == EL_EMC_LENSES)
14265 game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
14267 RedrawAllInvisibleElementsForLenses();
14269 else if (element == EL_EMC_MAGNIFIER)
14271 game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
14273 RedrawAllInvisibleElementsForMagnifier();
14275 else if (IS_DROPPABLE(element) ||
14276 IS_THROWABLE(element)) /* can be collected and dropped */
14280 if (collect_count == 0)
14281 player->inventory_infinite_element = element;
14283 for (i = 0; i < collect_count; i++)
14284 if (player->inventory_size < MAX_INVENTORY_SIZE)
14285 player->inventory_element[player->inventory_size++] = element;
14287 DrawGameDoorValues();
14289 else if (collect_count > 0)
14291 local_player->gems_still_needed -= collect_count;
14292 if (local_player->gems_still_needed < 0)
14293 local_player->gems_still_needed = 0;
14296 game_panel_controls[GAME_PANEL_GEMS].value = local_player->gems_still_needed;
14298 DisplayGameControlValues();
14300 DrawGameValue_Emeralds(local_player->gems_still_needed);
14304 RaiseScoreElement(element);
14305 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
14308 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
14309 player->index_bit, dig_side);
14311 if (mode == DF_SNAP)
14313 #if USE_NEW_SNAP_DELAY
14314 if (level.block_snap_field)
14315 setFieldForSnapping(x, y, element, move_direction);
14317 TestIfElementTouchesCustomElement(x, y); /* for empty space */
14319 TestIfElementTouchesCustomElement(x, y); /* for empty space */
14322 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14323 player->index_bit, dig_side);
14326 else if (player_can_move_or_snap && IS_PUSHABLE(element))
14328 if (mode == DF_SNAP && element != EL_BD_ROCK)
14329 return MP_NO_ACTION;
14331 if (CAN_FALL(element) && dy)
14332 return MP_NO_ACTION;
14334 if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
14335 !(element == EL_SPRING && level.use_spring_bug))
14336 return MP_NO_ACTION;
14338 if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
14339 ((move_direction & MV_VERTICAL &&
14340 ((element_info[element].move_pattern & MV_LEFT &&
14341 IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
14342 (element_info[element].move_pattern & MV_RIGHT &&
14343 IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
14344 (move_direction & MV_HORIZONTAL &&
14345 ((element_info[element].move_pattern & MV_UP &&
14346 IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
14347 (element_info[element].move_pattern & MV_DOWN &&
14348 IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
14349 return MP_NO_ACTION;
14351 /* do not push elements already moving away faster than player */
14352 if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
14353 ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
14354 return MP_NO_ACTION;
14356 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
14358 if (player->push_delay_value == -1 || !player_was_pushing)
14359 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14361 else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
14363 if (player->push_delay_value == -1)
14364 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14366 else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
14368 if (!player->is_pushing)
14369 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14372 player->is_pushing = TRUE;
14373 player->is_active = TRUE;
14375 if (!(IN_LEV_FIELD(nextx, nexty) &&
14376 (IS_FREE(nextx, nexty) ||
14377 (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY &&
14378 IS_SB_ELEMENT(element)))))
14379 return MP_NO_ACTION;
14381 if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
14382 return MP_NO_ACTION;
14384 if (player->push_delay == -1) /* new pushing; restart delay */
14385 player->push_delay = 0;
14387 if (player->push_delay < player->push_delay_value &&
14388 !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
14389 element != EL_SPRING && element != EL_BALLOON)
14391 /* make sure that there is no move delay before next try to push */
14392 if (game.engine_version >= VERSION_IDENT(3,0,7,1))
14393 player->move_delay = 0;
14395 return MP_NO_ACTION;
14398 if (IS_SB_ELEMENT(element))
14400 if (element == EL_SOKOBAN_FIELD_FULL)
14402 Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
14403 local_player->sokobanfields_still_needed++;
14406 if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
14408 Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
14409 local_player->sokobanfields_still_needed--;
14412 Feld[x][y] = EL_SOKOBAN_OBJECT;
14414 if (Back[x][y] == Back[nextx][nexty])
14415 PlayLevelSoundAction(x, y, ACTION_PUSHING);
14416 else if (Back[x][y] != 0)
14417 PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
14420 PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
14423 if (local_player->sokobanfields_still_needed == 0 &&
14424 game.emulation == EMU_SOKOBAN)
14426 PlayerWins(player);
14428 PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
14432 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14434 InitMovingField(x, y, move_direction);
14435 GfxAction[x][y] = ACTION_PUSHING;
14437 if (mode == DF_SNAP)
14438 ContinueMoving(x, y);
14440 MovPos[x][y] = (dx != 0 ? dx : dy);
14442 Pushed[x][y] = TRUE;
14443 Pushed[nextx][nexty] = TRUE;
14445 if (game.engine_version < VERSION_IDENT(2,2,0,7))
14446 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14448 player->push_delay_value = -1; /* get new value later */
14450 /* check for element change _after_ element has been pushed */
14451 if (game.use_change_when_pushing_bug)
14453 CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
14454 player->index_bit, dig_side);
14455 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
14456 player->index_bit, dig_side);
14459 else if (IS_SWITCHABLE(element))
14461 if (PLAYER_SWITCHING(player, x, y))
14463 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14464 player->index_bit, dig_side);
14469 player->is_switching = TRUE;
14470 player->switch_x = x;
14471 player->switch_y = y;
14473 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
14475 if (element == EL_ROBOT_WHEEL)
14477 Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
14481 game.robot_wheel_active = TRUE;
14483 DrawLevelField(x, y);
14485 else if (element == EL_SP_TERMINAL)
14489 SCAN_PLAYFIELD(xx, yy)
14491 if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
14493 else if (Feld[xx][yy] == EL_SP_TERMINAL)
14494 Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
14497 else if (IS_BELT_SWITCH(element))
14499 ToggleBeltSwitch(x, y);
14501 else if (element == EL_SWITCHGATE_SWITCH_UP ||
14502 element == EL_SWITCHGATE_SWITCH_DOWN ||
14503 element == EL_DC_SWITCHGATE_SWITCH_UP ||
14504 element == EL_DC_SWITCHGATE_SWITCH_DOWN)
14506 ToggleSwitchgateSwitch(x, y);
14508 else if (element == EL_LIGHT_SWITCH ||
14509 element == EL_LIGHT_SWITCH_ACTIVE)
14511 ToggleLightSwitch(x, y);
14513 else if (element == EL_TIMEGATE_SWITCH ||
14514 element == EL_DC_TIMEGATE_SWITCH)
14516 ActivateTimegateSwitch(x, y);
14518 else if (element == EL_BALLOON_SWITCH_LEFT ||
14519 element == EL_BALLOON_SWITCH_RIGHT ||
14520 element == EL_BALLOON_SWITCH_UP ||
14521 element == EL_BALLOON_SWITCH_DOWN ||
14522 element == EL_BALLOON_SWITCH_NONE ||
14523 element == EL_BALLOON_SWITCH_ANY)
14525 game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT ? MV_LEFT :
14526 element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
14527 element == EL_BALLOON_SWITCH_UP ? MV_UP :
14528 element == EL_BALLOON_SWITCH_DOWN ? MV_DOWN :
14529 element == EL_BALLOON_SWITCH_NONE ? MV_NONE :
14532 else if (element == EL_LAMP)
14534 Feld[x][y] = EL_LAMP_ACTIVE;
14535 local_player->lights_still_needed--;
14537 ResetGfxAnimation(x, y);
14538 DrawLevelField(x, y);
14540 else if (element == EL_TIME_ORB_FULL)
14542 Feld[x][y] = EL_TIME_ORB_EMPTY;
14544 if (level.time > 0 || level.use_time_orb_bug)
14546 TimeLeft += level.time_orb_time;
14549 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
14551 DisplayGameControlValues();
14553 DrawGameValue_Time(TimeLeft);
14557 ResetGfxAnimation(x, y);
14558 DrawLevelField(x, y);
14560 else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
14561 element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14565 game.ball_state = !game.ball_state;
14567 SCAN_PLAYFIELD(xx, yy)
14569 int e = Feld[xx][yy];
14571 if (game.ball_state)
14573 if (e == EL_EMC_MAGIC_BALL)
14574 CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
14575 else if (e == EL_EMC_MAGIC_BALL_SWITCH)
14576 CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
14580 if (e == EL_EMC_MAGIC_BALL_ACTIVE)
14581 CreateField(xx, yy, EL_EMC_MAGIC_BALL);
14582 else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14583 CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
14588 CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14589 player->index_bit, dig_side);
14591 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14592 player->index_bit, dig_side);
14594 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14595 player->index_bit, dig_side);
14601 if (!PLAYER_SWITCHING(player, x, y))
14603 player->is_switching = TRUE;
14604 player->switch_x = x;
14605 player->switch_y = y;
14607 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
14608 player->index_bit, dig_side);
14609 CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14610 player->index_bit, dig_side);
14612 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
14613 player->index_bit, dig_side);
14614 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14615 player->index_bit, dig_side);
14618 CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
14619 player->index_bit, dig_side);
14620 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14621 player->index_bit, dig_side);
14623 return MP_NO_ACTION;
14626 player->push_delay = -1;
14628 if (is_player) /* function can also be called by EL_PENGUIN */
14630 if (Feld[x][y] != element) /* really digged/collected something */
14632 player->is_collecting = !player->is_digging;
14633 player->is_active = TRUE;
14640 boolean SnapField(struct PlayerInfo *player, int dx, int dy)
14642 int jx = player->jx, jy = player->jy;
14643 int x = jx + dx, y = jy + dy;
14644 int snap_direction = (dx == -1 ? MV_LEFT :
14645 dx == +1 ? MV_RIGHT :
14647 dy == +1 ? MV_DOWN : MV_NONE);
14648 boolean can_continue_snapping = (level.continuous_snapping &&
14649 WasJustFalling[x][y] < CHECK_DELAY_FALLING);
14651 if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
14654 if (!player->active || !IN_LEV_FIELD(x, y))
14662 if (player->MovPos == 0)
14663 player->is_pushing = FALSE;
14665 player->is_snapping = FALSE;
14667 if (player->MovPos == 0)
14669 player->is_moving = FALSE;
14670 player->is_digging = FALSE;
14671 player->is_collecting = FALSE;
14677 #if USE_NEW_CONTINUOUS_SNAPPING
14678 /* prevent snapping with already pressed snap key when not allowed */
14679 if (player->is_snapping && !can_continue_snapping)
14682 if (player->is_snapping)
14686 player->MovDir = snap_direction;
14688 if (player->MovPos == 0)
14690 player->is_moving = FALSE;
14691 player->is_digging = FALSE;
14692 player->is_collecting = FALSE;
14695 player->is_dropping = FALSE;
14696 player->is_dropping_pressed = FALSE;
14697 player->drop_pressed_delay = 0;
14699 if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
14702 player->is_snapping = TRUE;
14703 player->is_active = TRUE;
14705 if (player->MovPos == 0)
14707 player->is_moving = FALSE;
14708 player->is_digging = FALSE;
14709 player->is_collecting = FALSE;
14712 if (player->MovPos != 0) /* prevent graphic bugs in versions < 2.2.0 */
14713 DrawLevelField(player->last_jx, player->last_jy);
14715 DrawLevelField(x, y);
14720 boolean DropElement(struct PlayerInfo *player)
14722 int old_element, new_element;
14723 int dropx = player->jx, dropy = player->jy;
14724 int drop_direction = player->MovDir;
14725 int drop_side = drop_direction;
14727 int drop_element = get_next_dropped_element(player);
14729 int drop_element = (player->inventory_size > 0 ?
14730 player->inventory_element[player->inventory_size - 1] :
14731 player->inventory_infinite_element != EL_UNDEFINED ?
14732 player->inventory_infinite_element :
14733 player->dynabombs_left > 0 ?
14734 EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
14738 player->is_dropping_pressed = TRUE;
14740 /* do not drop an element on top of another element; when holding drop key
14741 pressed without moving, dropped element must move away before the next
14742 element can be dropped (this is especially important if the next element
14743 is dynamite, which can be placed on background for historical reasons) */
14744 if (PLAYER_DROPPING(player, dropx, dropy) && Feld[dropx][dropy] != EL_EMPTY)
14747 if (IS_THROWABLE(drop_element))
14749 dropx += GET_DX_FROM_DIR(drop_direction);
14750 dropy += GET_DY_FROM_DIR(drop_direction);
14752 if (!IN_LEV_FIELD(dropx, dropy))
14756 old_element = Feld[dropx][dropy]; /* old element at dropping position */
14757 new_element = drop_element; /* default: no change when dropping */
14759 /* check if player is active, not moving and ready to drop */
14760 if (!player->active || player->MovPos || player->drop_delay > 0)
14763 /* check if player has anything that can be dropped */
14764 if (new_element == EL_UNDEFINED)
14767 /* check if drop key was pressed long enough for EM style dynamite */
14768 if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
14771 /* check if anything can be dropped at the current position */
14772 if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
14775 /* collected custom elements can only be dropped on empty fields */
14776 if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
14779 if (old_element != EL_EMPTY)
14780 Back[dropx][dropy] = old_element; /* store old element on this field */
14782 ResetGfxAnimation(dropx, dropy);
14783 ResetRandomAnimationValue(dropx, dropy);
14785 if (player->inventory_size > 0 ||
14786 player->inventory_infinite_element != EL_UNDEFINED)
14788 if (player->inventory_size > 0)
14790 player->inventory_size--;
14792 DrawGameDoorValues();
14794 if (new_element == EL_DYNAMITE)
14795 new_element = EL_DYNAMITE_ACTIVE;
14796 else if (new_element == EL_EM_DYNAMITE)
14797 new_element = EL_EM_DYNAMITE_ACTIVE;
14798 else if (new_element == EL_SP_DISK_RED)
14799 new_element = EL_SP_DISK_RED_ACTIVE;
14802 Feld[dropx][dropy] = new_element;
14804 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
14805 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
14806 el2img(Feld[dropx][dropy]), 0);
14808 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
14810 /* needed if previous element just changed to "empty" in the last frame */
14811 ChangeCount[dropx][dropy] = 0; /* allow at least one more change */
14813 CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
14814 player->index_bit, drop_side);
14815 CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
14817 player->index_bit, drop_side);
14819 TestIfElementTouchesCustomElement(dropx, dropy);
14821 else /* player is dropping a dyna bomb */
14823 player->dynabombs_left--;
14825 Feld[dropx][dropy] = new_element;
14827 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
14828 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
14829 el2img(Feld[dropx][dropy]), 0);
14831 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
14834 if (Feld[dropx][dropy] == new_element) /* uninitialized unless CE change */
14835 InitField_WithBug1(dropx, dropy, FALSE);
14837 new_element = Feld[dropx][dropy]; /* element might have changed */
14839 if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
14840 element_info[new_element].move_pattern == MV_WHEN_DROPPED)
14842 int move_direction, nextx, nexty;
14844 if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
14845 MovDir[dropx][dropy] = drop_direction;
14847 move_direction = MovDir[dropx][dropy];
14848 nextx = dropx + GET_DX_FROM_DIR(move_direction);
14849 nexty = dropy + GET_DY_FROM_DIR(move_direction);
14851 ChangeCount[dropx][dropy] = 0; /* allow at least one more change */
14853 #if USE_FIX_IMPACT_COLLISION
14854 /* do not cause impact style collision by dropping elements that can fall */
14855 CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
14857 CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
14861 player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
14862 player->is_dropping = TRUE;
14864 player->drop_pressed_delay = 0;
14865 player->is_dropping_pressed = FALSE;
14867 player->drop_x = dropx;
14868 player->drop_y = dropy;
14873 /* ------------------------------------------------------------------------- */
14874 /* game sound playing functions */
14875 /* ------------------------------------------------------------------------- */
14877 static int *loop_sound_frame = NULL;
14878 static int *loop_sound_volume = NULL;
14880 void InitPlayLevelSound()
14882 int num_sounds = getSoundListSize();
14884 checked_free(loop_sound_frame);
14885 checked_free(loop_sound_volume);
14887 loop_sound_frame = checked_calloc(num_sounds * sizeof(int));
14888 loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
14891 static void PlayLevelSound(int x, int y, int nr)
14893 int sx = SCREENX(x), sy = SCREENY(y);
14894 int volume, stereo_position;
14895 int max_distance = 8;
14896 int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
14898 if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
14899 (!setup.sound_loops && IS_LOOP_SOUND(nr)))
14902 if (!IN_LEV_FIELD(x, y) ||
14903 sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
14904 sy < -max_distance || sy >= SCR_FIELDY + max_distance)
14907 volume = SOUND_MAX_VOLUME;
14909 if (!IN_SCR_FIELD(sx, sy))
14911 int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
14912 int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
14914 volume -= volume * (dx > dy ? dx : dy) / max_distance;
14917 stereo_position = (SOUND_MAX_LEFT +
14918 (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
14919 (SCR_FIELDX + 2 * max_distance));
14921 if (IS_LOOP_SOUND(nr))
14923 /* This assures that quieter loop sounds do not overwrite louder ones,
14924 while restarting sound volume comparison with each new game frame. */
14926 if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
14929 loop_sound_volume[nr] = volume;
14930 loop_sound_frame[nr] = FrameCounter;
14933 PlaySoundExt(nr, volume, stereo_position, type);
14936 static void PlayLevelSoundNearest(int x, int y, int sound_action)
14938 PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
14939 x > LEVELX(BX2) ? LEVELX(BX2) : x,
14940 y < LEVELY(BY1) ? LEVELY(BY1) :
14941 y > LEVELY(BY2) ? LEVELY(BY2) : y,
14945 static void PlayLevelSoundAction(int x, int y, int action)
14947 PlayLevelSoundElementAction(x, y, Feld[x][y], action);
14950 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
14952 int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
14954 if (sound_effect != SND_UNDEFINED)
14955 PlayLevelSound(x, y, sound_effect);
14958 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
14961 int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
14963 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14964 PlayLevelSound(x, y, sound_effect);
14967 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
14969 int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
14971 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14972 PlayLevelSound(x, y, sound_effect);
14975 static void StopLevelSoundActionIfLoop(int x, int y, int action)
14977 int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
14979 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14980 StopSound(sound_effect);
14983 static void PlayLevelMusic()
14985 if (levelset.music[level_nr] != MUS_UNDEFINED)
14986 PlayMusic(levelset.music[level_nr]); /* from config file */
14988 PlayMusic(MAP_NOCONF_MUSIC(level_nr)); /* from music dir */
14991 void PlayLevelSound_EM(int xx, int yy, int element_em, int sample)
14993 int element = (element_em > -1 ? map_element_EM_to_RND(element_em) : 0);
14994 int offset = (BorderElement == EL_STEELWALL ? 1 : 0);
14995 int x = xx - 1 - offset;
14996 int y = yy - 1 - offset;
15001 PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
15005 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
15009 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15013 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15017 PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
15021 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15025 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15028 case SAMPLE_android_clone:
15029 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
15032 case SAMPLE_android_move:
15033 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15036 case SAMPLE_spring:
15037 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15041 PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
15045 PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
15048 case SAMPLE_eater_eat:
15049 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
15053 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15056 case SAMPLE_collect:
15057 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
15060 case SAMPLE_diamond:
15061 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15064 case SAMPLE_squash:
15065 /* !!! CHECK THIS !!! */
15067 PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
15069 PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
15073 case SAMPLE_wonderfall:
15074 PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
15078 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15082 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
15086 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
15090 PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
15094 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
15098 PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
15101 case SAMPLE_wonder:
15102 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
15106 PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
15109 case SAMPLE_exit_open:
15110 PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
15113 case SAMPLE_exit_leave:
15114 PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
15117 case SAMPLE_dynamite:
15118 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
15122 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
15126 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
15130 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
15134 PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
15138 PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
15142 PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
15146 PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
15152 void ChangeTime(int value)
15154 int *time = (level.time == 0 ? &TimePlayed : &TimeLeft);
15158 /* EMC game engine uses value from time counter of RND game engine */
15159 level.native_em_level->lev->time = *time;
15161 DrawGameValue_Time(*time);
15164 void RaiseScore(int value)
15166 /* EMC game engine and RND game engine have separate score counters */
15167 int *score = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
15168 &level.native_em_level->lev->score : &local_player->score);
15172 DrawGameValue_Score(*score);
15176 void RaiseScore(int value)
15178 local_player->score += value;
15181 game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
15183 DisplayGameControlValues();
15185 DrawGameValue_Score(local_player->score);
15189 void RaiseScoreElement(int element)
15194 case EL_BD_DIAMOND:
15195 case EL_EMERALD_YELLOW:
15196 case EL_EMERALD_RED:
15197 case EL_EMERALD_PURPLE:
15198 case EL_SP_INFOTRON:
15199 RaiseScore(level.score[SC_EMERALD]);
15202 RaiseScore(level.score[SC_DIAMOND]);
15205 RaiseScore(level.score[SC_CRYSTAL]);
15208 RaiseScore(level.score[SC_PEARL]);
15211 case EL_BD_BUTTERFLY:
15212 case EL_SP_ELECTRON:
15213 RaiseScore(level.score[SC_BUG]);
15216 case EL_BD_FIREFLY:
15217 case EL_SP_SNIKSNAK:
15218 RaiseScore(level.score[SC_SPACESHIP]);
15221 case EL_DARK_YAMYAM:
15222 RaiseScore(level.score[SC_YAMYAM]);
15225 RaiseScore(level.score[SC_ROBOT]);
15228 RaiseScore(level.score[SC_PACMAN]);
15231 RaiseScore(level.score[SC_NUT]);
15234 case EL_EM_DYNAMITE:
15235 case EL_SP_DISK_RED:
15236 case EL_DYNABOMB_INCREASE_NUMBER:
15237 case EL_DYNABOMB_INCREASE_SIZE:
15238 case EL_DYNABOMB_INCREASE_POWER:
15239 RaiseScore(level.score[SC_DYNAMITE]);
15241 case EL_SHIELD_NORMAL:
15242 case EL_SHIELD_DEADLY:
15243 RaiseScore(level.score[SC_SHIELD]);
15245 case EL_EXTRA_TIME:
15246 RaiseScore(level.extra_time_score);
15260 case EL_DC_KEY_WHITE:
15261 RaiseScore(level.score[SC_KEY]);
15264 RaiseScore(element_info[element].collect_score);
15269 void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message)
15271 if (skip_request || Request(message, REQ_ASK | REQ_STAY_CLOSED))
15273 #if defined(NETWORK_AVALIABLE)
15274 if (options.network)
15275 SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
15284 FadeSkipNextFadeIn();
15286 fading = fading_none;
15290 OpenDoor(DOOR_CLOSE_1);
15293 game_status = GAME_MODE_MAIN;
15296 DrawAndFadeInMainMenu(REDRAW_FIELD);
15304 FadeOut(REDRAW_FIELD);
15307 game_status = GAME_MODE_MAIN;
15309 DrawAndFadeInMainMenu(REDRAW_FIELD);
15313 else /* continue playing the game */
15315 if (tape.playing && tape.deactivate_display)
15316 TapeDeactivateDisplayOff(TRUE);
15318 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
15320 if (tape.playing && tape.deactivate_display)
15321 TapeDeactivateDisplayOn();
15325 void RequestQuitGame(boolean ask_if_really_quit)
15327 boolean quick_quit = (!ask_if_really_quit || level_editor_test_game);
15328 boolean skip_request = AllPlayersGone || quick_quit;
15330 RequestQuitGameExt(skip_request, quick_quit,
15331 "Do you really want to quit the game ?");
15335 /* ------------------------------------------------------------------------- */
15336 /* random generator functions */
15337 /* ------------------------------------------------------------------------- */
15339 unsigned int InitEngineRandom_RND(long seed)
15341 game.num_random_calls = 0;
15344 unsigned int rnd_seed = InitEngineRandom(seed);
15346 printf("::: START RND: %d\n", rnd_seed);
15351 return InitEngineRandom(seed);
15357 unsigned int RND(int max)
15361 game.num_random_calls++;
15363 return GetEngineRandom(max);
15370 /* ------------------------------------------------------------------------- */
15371 /* game engine snapshot handling functions */
15372 /* ------------------------------------------------------------------------- */
15374 #define ARGS_ADDRESS_AND_SIZEOF(x) (&(x)), (sizeof(x))
15376 struct EngineSnapshotInfo
15378 /* runtime values for custom element collect score */
15379 int collect_score[NUM_CUSTOM_ELEMENTS];
15381 /* runtime values for group element choice position */
15382 int choice_pos[NUM_GROUP_ELEMENTS];
15384 /* runtime values for belt position animations */
15385 int belt_graphic[4 * NUM_BELT_PARTS];
15386 int belt_anim_mode[4 * NUM_BELT_PARTS];
15389 struct EngineSnapshotNodeInfo
15396 static struct EngineSnapshotInfo engine_snapshot_rnd;
15397 static ListNode *engine_snapshot_list = NULL;
15398 static char *snapshot_level_identifier = NULL;
15399 static int snapshot_level_nr = -1;
15401 void FreeEngineSnapshot()
15403 while (engine_snapshot_list != NULL)
15404 deleteNodeFromList(&engine_snapshot_list, engine_snapshot_list->key,
15407 setString(&snapshot_level_identifier, NULL);
15408 snapshot_level_nr = -1;
15411 static void SaveEngineSnapshotValues_RND()
15413 static int belt_base_active_element[4] =
15415 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
15416 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
15417 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
15418 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
15422 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
15424 int element = EL_CUSTOM_START + i;
15426 engine_snapshot_rnd.collect_score[i] = element_info[element].collect_score;
15429 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15431 int element = EL_GROUP_START + i;
15433 engine_snapshot_rnd.choice_pos[i] = element_info[element].group->choice_pos;
15436 for (i = 0; i < 4; i++)
15438 for (j = 0; j < NUM_BELT_PARTS; j++)
15440 int element = belt_base_active_element[i] + j;
15441 int graphic = el2img(element);
15442 int anim_mode = graphic_info[graphic].anim_mode;
15444 engine_snapshot_rnd.belt_graphic[i * 4 + j] = graphic;
15445 engine_snapshot_rnd.belt_anim_mode[i * 4 + j] = anim_mode;
15450 static void LoadEngineSnapshotValues_RND()
15452 unsigned long num_random_calls = game.num_random_calls;
15455 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
15457 int element = EL_CUSTOM_START + i;
15459 element_info[element].collect_score = engine_snapshot_rnd.collect_score[i];
15462 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15464 int element = EL_GROUP_START + i;
15466 element_info[element].group->choice_pos = engine_snapshot_rnd.choice_pos[i];
15469 for (i = 0; i < 4; i++)
15471 for (j = 0; j < NUM_BELT_PARTS; j++)
15473 int graphic = engine_snapshot_rnd.belt_graphic[i * 4 + j];
15474 int anim_mode = engine_snapshot_rnd.belt_anim_mode[i * 4 + j];
15476 graphic_info[graphic].anim_mode = anim_mode;
15480 if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15482 InitRND(tape.random_seed);
15483 for (i = 0; i < num_random_calls; i++)
15487 if (game.num_random_calls != num_random_calls)
15489 Error(ERR_INFO, "number of random calls out of sync");
15490 Error(ERR_INFO, "number of random calls should be %d", num_random_calls);
15491 Error(ERR_INFO, "number of random calls is %d", game.num_random_calls);
15492 Error(ERR_EXIT, "this should not happen -- please debug");
15496 static void SaveEngineSnapshotBuffer(void *buffer, int size)
15498 struct EngineSnapshotNodeInfo *bi =
15499 checked_calloc(sizeof(struct EngineSnapshotNodeInfo));
15501 bi->buffer_orig = buffer;
15502 bi->buffer_copy = checked_malloc(size);
15505 memcpy(bi->buffer_copy, buffer, size);
15507 addNodeToList(&engine_snapshot_list, NULL, bi);
15510 void SaveEngineSnapshot()
15512 FreeEngineSnapshot(); /* free previous snapshot, if needed */
15514 if (level_editor_test_game) /* do not save snapshots from editor */
15517 /* copy some special values to a structure better suited for the snapshot */
15519 SaveEngineSnapshotValues_RND();
15520 SaveEngineSnapshotValues_EM();
15522 /* save values stored in special snapshot structure */
15524 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
15525 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
15527 /* save further RND engine values */
15529 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(stored_player));
15530 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(game));
15531 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(tape));
15533 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ZX));
15534 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ZY));
15535 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExitX));
15536 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExitY));
15538 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(FrameCounter));
15539 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimeFrames));
15540 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimePlayed));
15541 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimeLeft));
15542 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TapeTime));
15544 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenMovDir));
15545 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenMovPos));
15546 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenGfxPos));
15548 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScrollStepSize));
15550 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AllPlayersGone));
15552 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt));
15553 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2));
15555 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Feld));
15556 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovPos));
15557 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovDir));
15558 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovDelay));
15559 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeDelay));
15560 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangePage));
15561 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CustomValue));
15562 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Store));
15563 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Store2));
15564 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(StorePlayer));
15565 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Back));
15566 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaNr));
15567 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(WasJustMoving));
15568 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(WasJustFalling));
15569 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CheckCollision));
15570 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CheckImpact));
15571 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Stop));
15572 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Pushed));
15574 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeCount));
15575 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeEvent));
15577 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodePhase));
15578 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodeDelay));
15579 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodeField));
15581 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(RunnerVisit));
15582 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(PlayerVisit));
15584 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxFrame));
15585 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxRandom));
15586 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxElement));
15587 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxAction));
15588 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxDir));
15590 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(scroll_x));
15591 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(scroll_y));
15593 /* save level identification information */
15595 setString(&snapshot_level_identifier, leveldir_current->identifier);
15596 snapshot_level_nr = level_nr;
15599 ListNode *node = engine_snapshot_list;
15602 while (node != NULL)
15604 num_bytes += ((struct EngineSnapshotNodeInfo *)node->content)->size;
15609 printf("::: size of engine snapshot: %d bytes\n", num_bytes);
15613 static void LoadEngineSnapshotBuffer(struct EngineSnapshotNodeInfo *bi)
15615 memcpy(bi->buffer_orig, bi->buffer_copy, bi->size);
15618 void LoadEngineSnapshot()
15620 ListNode *node = engine_snapshot_list;
15622 if (engine_snapshot_list == NULL)
15625 while (node != NULL)
15627 LoadEngineSnapshotBuffer((struct EngineSnapshotNodeInfo *)node->content);
15632 /* restore special values from snapshot structure */
15634 LoadEngineSnapshotValues_RND();
15635 LoadEngineSnapshotValues_EM();
15638 boolean CheckEngineSnapshot()
15640 return (strEqual(snapshot_level_identifier, leveldir_current->identifier) &&
15641 snapshot_level_nr == level_nr);
15645 /* ---------- new game button stuff ---------------------------------------- */
15647 /* graphic position values for game buttons */
15648 #define GAME_BUTTON_XSIZE 30
15649 #define GAME_BUTTON_YSIZE 30
15650 #define GAME_BUTTON_XPOS 5
15651 #define GAME_BUTTON_YPOS 215
15652 #define SOUND_BUTTON_XPOS 5
15653 #define SOUND_BUTTON_YPOS (GAME_BUTTON_YPOS + GAME_BUTTON_YSIZE)
15655 #define GAME_BUTTON_STOP_XPOS (GAME_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
15656 #define GAME_BUTTON_PAUSE_XPOS (GAME_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
15657 #define GAME_BUTTON_PLAY_XPOS (GAME_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
15658 #define SOUND_BUTTON_MUSIC_XPOS (SOUND_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
15659 #define SOUND_BUTTON_LOOPS_XPOS (SOUND_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
15660 #define SOUND_BUTTON_SIMPLE_XPOS (SOUND_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
15668 } gamebutton_info[NUM_GAME_BUTTONS] =
15672 &game.button.stop.x, &game.button.stop.y,
15673 GAME_BUTTON_STOP_XPOS, GAME_BUTTON_YPOS,
15678 &game.button.pause.x, &game.button.pause.y,
15679 GAME_BUTTON_PAUSE_XPOS, GAME_BUTTON_YPOS,
15680 GAME_CTRL_ID_PAUSE,
15684 &game.button.play.x, &game.button.play.y,
15685 GAME_BUTTON_PLAY_XPOS, GAME_BUTTON_YPOS,
15690 &game.button.sound_music.x, &game.button.sound_music.y,
15691 SOUND_BUTTON_MUSIC_XPOS, SOUND_BUTTON_YPOS,
15692 SOUND_CTRL_ID_MUSIC,
15693 "background music on/off"
15696 &game.button.sound_loops.x, &game.button.sound_loops.y,
15697 SOUND_BUTTON_LOOPS_XPOS, SOUND_BUTTON_YPOS,
15698 SOUND_CTRL_ID_LOOPS,
15699 "sound loops on/off"
15702 &game.button.sound_simple.x,&game.button.sound_simple.y,
15703 SOUND_BUTTON_SIMPLE_XPOS, SOUND_BUTTON_YPOS,
15704 SOUND_CTRL_ID_SIMPLE,
15705 "normal sounds on/off"
15709 GAME_BUTTON_STOP_XPOS, GAME_BUTTON_YPOS,
15714 GAME_BUTTON_PAUSE_XPOS, GAME_BUTTON_YPOS,
15715 GAME_CTRL_ID_PAUSE,
15719 GAME_BUTTON_PLAY_XPOS, GAME_BUTTON_YPOS,
15724 SOUND_BUTTON_MUSIC_XPOS, SOUND_BUTTON_YPOS,
15725 SOUND_CTRL_ID_MUSIC,
15726 "background music on/off"
15729 SOUND_BUTTON_LOOPS_XPOS, SOUND_BUTTON_YPOS,
15730 SOUND_CTRL_ID_LOOPS,
15731 "sound loops on/off"
15734 SOUND_BUTTON_SIMPLE_XPOS, SOUND_BUTTON_YPOS,
15735 SOUND_CTRL_ID_SIMPLE,
15736 "normal sounds on/off"
15741 void CreateGameButtons()
15745 for (i = 0; i < NUM_GAME_BUTTONS; i++)
15747 Bitmap *gd_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
15748 struct GadgetInfo *gi;
15751 unsigned long event_mask;
15753 int gd_xoffset, gd_yoffset;
15754 int gd_x1, gd_x2, gd_y1, gd_y2;
15757 x = DX + *gamebutton_info[i].x;
15758 y = DY + *gamebutton_info[i].y;
15759 gd_xoffset = gamebutton_info[i].gd_x;
15760 gd_yoffset = gamebutton_info[i].gd_y;
15761 gd_x1 = DOOR_GFX_PAGEX4 + gd_xoffset;
15762 gd_x2 = DOOR_GFX_PAGEX3 + gd_xoffset;
15764 if (id == GAME_CTRL_ID_STOP ||
15765 id == GAME_CTRL_ID_PAUSE ||
15766 id == GAME_CTRL_ID_PLAY)
15768 button_type = GD_TYPE_NORMAL_BUTTON;
15770 event_mask = GD_EVENT_RELEASED;
15771 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
15772 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
15776 button_type = GD_TYPE_CHECK_BUTTON;
15778 ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
15779 (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
15780 (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
15781 event_mask = GD_EVENT_PRESSED;
15782 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset;
15783 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
15786 gi = CreateGadget(GDI_CUSTOM_ID, id,
15787 GDI_INFO_TEXT, gamebutton_info[i].infotext,
15792 GDI_X, DX + gd_xoffset,
15793 GDI_Y, DY + gd_yoffset,
15795 GDI_WIDTH, GAME_BUTTON_XSIZE,
15796 GDI_HEIGHT, GAME_BUTTON_YSIZE,
15797 GDI_TYPE, button_type,
15798 GDI_STATE, GD_BUTTON_UNPRESSED,
15799 GDI_CHECKED, checked,
15800 GDI_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y1,
15801 GDI_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y1,
15802 GDI_ALT_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y2,
15803 GDI_ALT_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y2,
15804 GDI_DIRECT_DRAW, FALSE,
15805 GDI_EVENT_MASK, event_mask,
15806 GDI_CALLBACK_ACTION, HandleGameButtons,
15810 Error(ERR_EXIT, "cannot create gadget");
15812 game_gadget[id] = gi;
15816 void FreeGameButtons()
15820 for (i = 0; i < NUM_GAME_BUTTONS; i++)
15821 FreeGadget(game_gadget[i]);
15824 static void MapGameButtons()
15828 for (i = 0; i < NUM_GAME_BUTTONS; i++)
15829 MapGadget(game_gadget[i]);
15832 void UnmapGameButtons()
15836 for (i = 0; i < NUM_GAME_BUTTONS; i++)
15837 UnmapGadget(game_gadget[i]);
15840 void RedrawGameButtons()
15844 for (i = 0; i < NUM_GAME_BUTTONS; i++)
15845 RedrawGadget(game_gadget[i]);
15848 static void HandleGameButtons(struct GadgetInfo *gi)
15850 int id = gi->custom_id;
15852 if (game_status != GAME_MODE_PLAYING)
15857 case GAME_CTRL_ID_STOP:
15861 RequestQuitGame(TRUE);
15864 case GAME_CTRL_ID_PAUSE:
15865 if (options.network)
15867 #if defined(NETWORK_AVALIABLE)
15869 SendToServer_ContinuePlaying();
15871 SendToServer_PausePlaying();
15875 TapeTogglePause(TAPE_TOGGLE_MANUAL);
15878 case GAME_CTRL_ID_PLAY:
15881 #if defined(NETWORK_AVALIABLE)
15882 if (options.network)
15883 SendToServer_ContinuePlaying();
15887 tape.pausing = FALSE;
15888 DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF, 0);
15893 case SOUND_CTRL_ID_MUSIC:
15894 if (setup.sound_music)
15896 setup.sound_music = FALSE;
15899 else if (audio.music_available)
15901 setup.sound = setup.sound_music = TRUE;
15903 SetAudioMode(setup.sound);
15909 case SOUND_CTRL_ID_LOOPS:
15910 if (setup.sound_loops)
15911 setup.sound_loops = FALSE;
15912 else if (audio.loops_available)
15914 setup.sound = setup.sound_loops = TRUE;
15915 SetAudioMode(setup.sound);
15919 case SOUND_CTRL_ID_SIMPLE:
15920 if (setup.sound_simple)
15921 setup.sound_simple = FALSE;
15922 else if (audio.sound_available)
15924 setup.sound = setup.sound_simple = TRUE;
15925 SetAudioMode(setup.sound);