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_ELEMENT_1 69
204 #define GAME_PANEL_ELEMENT_2 70
205 #define GAME_PANEL_ELEMENT_3 71
206 #define GAME_PANEL_ELEMENT_4 72
207 #define GAME_PANEL_ELEMENT_5 73
208 #define GAME_PANEL_ELEMENT_6 74
209 #define GAME_PANEL_ELEMENT_7 75
210 #define GAME_PANEL_ELEMENT_8 76
211 #define GAME_PANEL_ELEMENT_COUNT_1 77
212 #define GAME_PANEL_ELEMENT_COUNT_2 78
213 #define GAME_PANEL_ELEMENT_COUNT_3 79
214 #define GAME_PANEL_ELEMENT_COUNT_4 80
215 #define GAME_PANEL_ELEMENT_COUNT_5 81
216 #define GAME_PANEL_ELEMENT_COUNT_6 82
217 #define GAME_PANEL_ELEMENT_COUNT_7 83
218 #define GAME_PANEL_ELEMENT_COUNT_8 84
219 #define GAME_PANEL_CE_SCORE_1 85
220 #define GAME_PANEL_CE_SCORE_2 86
221 #define GAME_PANEL_CE_SCORE_3 87
222 #define GAME_PANEL_CE_SCORE_4 88
223 #define GAME_PANEL_CE_SCORE_5 89
224 #define GAME_PANEL_CE_SCORE_6 90
225 #define GAME_PANEL_CE_SCORE_7 91
226 #define GAME_PANEL_CE_SCORE_8 92
227 #define GAME_PANEL_CE_SCORE_1_ELEMENT 93
228 #define GAME_PANEL_CE_SCORE_2_ELEMENT 94
229 #define GAME_PANEL_CE_SCORE_3_ELEMENT 95
230 #define GAME_PANEL_CE_SCORE_4_ELEMENT 96
231 #define GAME_PANEL_CE_SCORE_5_ELEMENT 97
232 #define GAME_PANEL_CE_SCORE_6_ELEMENT 98
233 #define GAME_PANEL_CE_SCORE_7_ELEMENT 99
234 #define GAME_PANEL_CE_SCORE_8_ELEMENT 100
235 #define GAME_PANEL_PLAYER_NAME 101
236 #define GAME_PANEL_LEVEL_NAME 102
237 #define GAME_PANEL_LEVEL_AUTHOR 103
239 #define NUM_GAME_PANEL_CONTROLS 104
241 struct GamePanelOrderInfo
247 static struct GamePanelOrderInfo game_panel_order[NUM_GAME_PANEL_CONTROLS];
249 struct GamePanelControlInfo
253 struct TextPosInfo *pos;
256 int value, last_value;
257 int frame, last_frame;
262 static struct GamePanelControlInfo game_panel_controls[] =
265 GAME_PANEL_LEVEL_NUMBER,
266 &game.panel.level_number,
275 GAME_PANEL_INVENTORY_COUNT,
276 &game.panel.inventory_count,
280 GAME_PANEL_INVENTORY_FIRST_1,
281 &game.panel.inventory_first[0],
285 GAME_PANEL_INVENTORY_FIRST_2,
286 &game.panel.inventory_first[1],
290 GAME_PANEL_INVENTORY_FIRST_3,
291 &game.panel.inventory_first[2],
295 GAME_PANEL_INVENTORY_FIRST_4,
296 &game.panel.inventory_first[3],
300 GAME_PANEL_INVENTORY_FIRST_5,
301 &game.panel.inventory_first[4],
305 GAME_PANEL_INVENTORY_FIRST_6,
306 &game.panel.inventory_first[5],
310 GAME_PANEL_INVENTORY_FIRST_7,
311 &game.panel.inventory_first[6],
315 GAME_PANEL_INVENTORY_FIRST_8,
316 &game.panel.inventory_first[7],
320 GAME_PANEL_INVENTORY_LAST_1,
321 &game.panel.inventory_last[0],
325 GAME_PANEL_INVENTORY_LAST_2,
326 &game.panel.inventory_last[1],
330 GAME_PANEL_INVENTORY_LAST_3,
331 &game.panel.inventory_last[2],
335 GAME_PANEL_INVENTORY_LAST_4,
336 &game.panel.inventory_last[3],
340 GAME_PANEL_INVENTORY_LAST_5,
341 &game.panel.inventory_last[4],
345 GAME_PANEL_INVENTORY_LAST_6,
346 &game.panel.inventory_last[5],
350 GAME_PANEL_INVENTORY_LAST_7,
351 &game.panel.inventory_last[6],
355 GAME_PANEL_INVENTORY_LAST_8,
356 &game.panel.inventory_last[7],
400 GAME_PANEL_KEY_WHITE,
401 &game.panel.key_white,
405 GAME_PANEL_KEY_WHITE_COUNT,
406 &game.panel.key_white_count,
435 GAME_PANEL_SHIELD_NORMAL,
436 &game.panel.shield_normal,
440 GAME_PANEL_SHIELD_NORMAL_TIME,
441 &game.panel.shield_normal_time,
445 GAME_PANEL_SHIELD_DEADLY,
446 &game.panel.shield_deadly,
450 GAME_PANEL_SHIELD_DEADLY_TIME,
451 &game.panel.shield_deadly_time,
460 GAME_PANEL_EMC_MAGIC_BALL,
461 &game.panel.emc_magic_ball,
465 GAME_PANEL_EMC_MAGIC_BALL_SWITCH,
466 &game.panel.emc_magic_ball_switch,
470 GAME_PANEL_LIGHT_SWITCH,
471 &game.panel.light_switch,
475 GAME_PANEL_LIGHT_SWITCH_TIME,
476 &game.panel.light_switch_time,
480 GAME_PANEL_TIMEGATE_SWITCH,
481 &game.panel.timegate_switch,
485 GAME_PANEL_TIMEGATE_SWITCH_TIME,
486 &game.panel.timegate_switch_time,
490 GAME_PANEL_SWITCHGATE_SWITCH,
491 &game.panel.switchgate_switch,
495 GAME_PANEL_EMC_LENSES,
496 &game.panel.emc_lenses,
500 GAME_PANEL_EMC_LENSES_TIME,
501 &game.panel.emc_lenses_time,
505 GAME_PANEL_EMC_MAGNIFIER,
506 &game.panel.emc_magnifier,
510 GAME_PANEL_EMC_MAGNIFIER_TIME,
511 &game.panel.emc_magnifier_time,
515 GAME_PANEL_BALLOON_SWITCH,
516 &game.panel.balloon_switch,
520 GAME_PANEL_DYNABOMB_NUMBER,
521 &game.panel.dynabomb_number,
525 GAME_PANEL_DYNABOMB_SIZE,
526 &game.panel.dynabomb_size,
530 GAME_PANEL_DYNABOMB_POWER,
531 &game.panel.dynabomb_power,
536 &game.panel.penguins,
540 GAME_PANEL_SOKOBAN_OBJECTS,
541 &game.panel.sokoban_objects,
545 GAME_PANEL_SOKOBAN_FIELDS,
546 &game.panel.sokoban_fields,
550 GAME_PANEL_ROBOT_WHEEL,
551 &game.panel.robot_wheel,
555 GAME_PANEL_CONVEYOR_BELT_1,
556 &game.panel.conveyor_belt[0],
560 GAME_PANEL_CONVEYOR_BELT_2,
561 &game.panel.conveyor_belt[1],
565 GAME_PANEL_CONVEYOR_BELT_3,
566 &game.panel.conveyor_belt[2],
570 GAME_PANEL_CONVEYOR_BELT_4,
571 &game.panel.conveyor_belt[3],
575 GAME_PANEL_CONVEYOR_BELT_1_SWITCH,
576 &game.panel.conveyor_belt_switch[0],
580 GAME_PANEL_CONVEYOR_BELT_2_SWITCH,
581 &game.panel.conveyor_belt_switch[1],
585 GAME_PANEL_CONVEYOR_BELT_3_SWITCH,
586 &game.panel.conveyor_belt_switch[2],
590 GAME_PANEL_CONVEYOR_BELT_4_SWITCH,
591 &game.panel.conveyor_belt_switch[3],
595 GAME_PANEL_MAGIC_WALL,
596 &game.panel.magic_wall,
600 GAME_PANEL_MAGIC_WALL_TIME,
601 &game.panel.magic_wall_time,
605 GAME_PANEL_GRAVITY_STATE,
606 &game.panel.gravity_state,
610 GAME_PANEL_ELEMENT_1,
611 &game.panel.element[0],
615 GAME_PANEL_ELEMENT_2,
616 &game.panel.element[1],
620 GAME_PANEL_ELEMENT_3,
621 &game.panel.element[2],
625 GAME_PANEL_ELEMENT_4,
626 &game.panel.element[3],
630 GAME_PANEL_ELEMENT_5,
631 &game.panel.element[4],
635 GAME_PANEL_ELEMENT_6,
636 &game.panel.element[5],
640 GAME_PANEL_ELEMENT_7,
641 &game.panel.element[6],
645 GAME_PANEL_ELEMENT_8,
646 &game.panel.element[7],
650 GAME_PANEL_ELEMENT_COUNT_1,
651 &game.panel.element_count[0],
655 GAME_PANEL_ELEMENT_COUNT_2,
656 &game.panel.element_count[1],
660 GAME_PANEL_ELEMENT_COUNT_3,
661 &game.panel.element_count[2],
665 GAME_PANEL_ELEMENT_COUNT_4,
666 &game.panel.element_count[3],
670 GAME_PANEL_ELEMENT_COUNT_5,
671 &game.panel.element_count[4],
675 GAME_PANEL_ELEMENT_COUNT_6,
676 &game.panel.element_count[5],
680 GAME_PANEL_ELEMENT_COUNT_7,
681 &game.panel.element_count[6],
685 GAME_PANEL_ELEMENT_COUNT_8,
686 &game.panel.element_count[7],
690 GAME_PANEL_CE_SCORE_1,
691 &game.panel.ce_score[0],
695 GAME_PANEL_CE_SCORE_2,
696 &game.panel.ce_score[1],
700 GAME_PANEL_CE_SCORE_3,
701 &game.panel.ce_score[2],
705 GAME_PANEL_CE_SCORE_4,
706 &game.panel.ce_score[3],
710 GAME_PANEL_CE_SCORE_5,
711 &game.panel.ce_score[4],
715 GAME_PANEL_CE_SCORE_6,
716 &game.panel.ce_score[5],
720 GAME_PANEL_CE_SCORE_7,
721 &game.panel.ce_score[6],
725 GAME_PANEL_CE_SCORE_8,
726 &game.panel.ce_score[7],
730 GAME_PANEL_CE_SCORE_1_ELEMENT,
731 &game.panel.ce_score_element[0],
735 GAME_PANEL_CE_SCORE_2_ELEMENT,
736 &game.panel.ce_score_element[1],
740 GAME_PANEL_CE_SCORE_3_ELEMENT,
741 &game.panel.ce_score_element[2],
745 GAME_PANEL_CE_SCORE_4_ELEMENT,
746 &game.panel.ce_score_element[3],
750 GAME_PANEL_CE_SCORE_5_ELEMENT,
751 &game.panel.ce_score_element[4],
755 GAME_PANEL_CE_SCORE_6_ELEMENT,
756 &game.panel.ce_score_element[5],
760 GAME_PANEL_CE_SCORE_7_ELEMENT,
761 &game.panel.ce_score_element[6],
765 GAME_PANEL_CE_SCORE_8_ELEMENT,
766 &game.panel.ce_score_element[7],
770 GAME_PANEL_PLAYER_NAME,
771 &game.panel.player_name,
775 GAME_PANEL_LEVEL_NAME,
776 &game.panel.level_name,
780 GAME_PANEL_LEVEL_AUTHOR,
781 &game.panel.level_author,
794 /* values for delayed check of falling and moving elements and for collision */
795 #define CHECK_DELAY_MOVING 3
796 #define CHECK_DELAY_FALLING CHECK_DELAY_MOVING
797 #define CHECK_DELAY_COLLISION 2
798 #define CHECK_DELAY_IMPACT CHECK_DELAY_COLLISION
800 /* values for initial player move delay (initial delay counter value) */
801 #define INITIAL_MOVE_DELAY_OFF -1
802 #define INITIAL_MOVE_DELAY_ON 0
804 /* values for player movement speed (which is in fact a delay value) */
805 #define MOVE_DELAY_MIN_SPEED 32
806 #define MOVE_DELAY_NORMAL_SPEED 8
807 #define MOVE_DELAY_HIGH_SPEED 4
808 #define MOVE_DELAY_MAX_SPEED 1
810 #define DOUBLE_MOVE_DELAY(x) (x = (x < MOVE_DELAY_MIN_SPEED ? x * 2 : x))
811 #define HALVE_MOVE_DELAY(x) (x = (x > MOVE_DELAY_MAX_SPEED ? x / 2 : x))
813 #define DOUBLE_PLAYER_SPEED(p) (HALVE_MOVE_DELAY( (p)->move_delay_value))
814 #define HALVE_PLAYER_SPEED(p) (DOUBLE_MOVE_DELAY((p)->move_delay_value))
816 /* values for other actions */
817 #define MOVE_STEPSIZE_NORMAL (TILEX / MOVE_DELAY_NORMAL_SPEED)
818 #define MOVE_STEPSIZE_MIN (1)
819 #define MOVE_STEPSIZE_MAX (TILEX)
821 #define GET_DX_FROM_DIR(d) ((d) == MV_LEFT ? -1 : (d) == MV_RIGHT ? 1 : 0)
822 #define GET_DY_FROM_DIR(d) ((d) == MV_UP ? -1 : (d) == MV_DOWN ? 1 : 0)
824 #define INIT_GFX_RANDOM() (GetSimpleRandom(1000000))
826 #define GET_NEW_PUSH_DELAY(e) ( (element_info[e].push_delay_fixed) + \
827 RND(element_info[e].push_delay_random))
828 #define GET_NEW_DROP_DELAY(e) ( (element_info[e].drop_delay_fixed) + \
829 RND(element_info[e].drop_delay_random))
830 #define GET_NEW_MOVE_DELAY(e) ( (element_info[e].move_delay_fixed) + \
831 RND(element_info[e].move_delay_random))
832 #define GET_MAX_MOVE_DELAY(e) ( (element_info[e].move_delay_fixed) + \
833 (element_info[e].move_delay_random))
834 #define GET_NEW_CE_VALUE(e) ( (element_info[e].ce_value_fixed_initial) +\
835 RND(element_info[e].ce_value_random_initial))
836 #define GET_CE_SCORE(e) ( (element_info[e].collect_score))
837 #define GET_CHANGE_DELAY(c) ( ((c)->delay_fixed * (c)->delay_frames) + \
838 RND((c)->delay_random * (c)->delay_frames))
839 #define GET_CE_DELAY_VALUE(c) ( ((c)->delay_fixed) + \
840 RND((c)->delay_random))
843 #define GET_VALID_RUNTIME_ELEMENT(e) \
844 ((e) >= NUM_RUNTIME_ELEMENTS ? EL_UNKNOWN : (e))
846 #define RESOLVED_REFERENCE_ELEMENT(be, e) \
847 ((be) + (e) - EL_SELF < EL_CUSTOM_START ? EL_CUSTOM_START : \
848 (be) + (e) - EL_SELF > EL_CUSTOM_END ? EL_CUSTOM_END : \
849 (be) + (e) - EL_SELF)
851 #define GET_TARGET_ELEMENT(be, e, ch, cv, cs) \
852 ((e) == EL_TRIGGER_PLAYER ? (ch)->actual_trigger_player : \
853 (e) == EL_TRIGGER_ELEMENT ? (ch)->actual_trigger_element : \
854 (e) == EL_TRIGGER_CE_VALUE ? (ch)->actual_trigger_ce_value : \
855 (e) == EL_TRIGGER_CE_SCORE ? (ch)->actual_trigger_ce_score : \
856 (e) == EL_CURRENT_CE_VALUE ? (cv) : \
857 (e) == EL_CURRENT_CE_SCORE ? (cs) : \
858 (e) >= EL_PREV_CE_8 && (e) <= EL_NEXT_CE_8 ? \
859 RESOLVED_REFERENCE_ELEMENT(be, e) : \
862 #define CAN_GROW_INTO(e) \
863 ((e) == EL_SAND || (IS_DIGGABLE(e) && level.grow_into_diggable))
865 #define ELEMENT_CAN_ENTER_FIELD_BASE_X(x, y, condition) \
866 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
869 #define ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, condition) \
870 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
871 (CAN_MOVE_INTO_ACID(e) && \
872 Feld[x][y] == EL_ACID) || \
875 #define ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, condition) \
876 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) || \
877 (CAN_MOVE_INTO_ACID(e) && \
878 Feld[x][y] == EL_ACID) || \
881 #define ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, condition) \
882 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
884 (CAN_MOVE_INTO_ACID(e) && \
885 Feld[x][y] == EL_ACID) || \
886 (DONT_COLLIDE_WITH(e) && \
888 !PLAYER_ENEMY_PROTECTED(x, y))))
890 #define ELEMENT_CAN_ENTER_FIELD(e, x, y) \
891 ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, 0)
893 #define SATELLITE_CAN_ENTER_FIELD(x, y) \
894 ELEMENT_CAN_ENTER_FIELD_BASE_2(EL_SATELLITE, x, y, 0)
896 #define ANDROID_CAN_ENTER_FIELD(e, x, y) \
897 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, Feld[x][y] == EL_EMC_PLANT)
899 #define ANDROID_CAN_CLONE_FIELD(x, y) \
900 (IN_LEV_FIELD(x, y) && (CAN_BE_CLONED_BY_ANDROID(Feld[x][y]) || \
901 CAN_BE_CLONED_BY_ANDROID(EL_TRIGGER_ELEMENT)))
903 #define ENEMY_CAN_ENTER_FIELD(e, x, y) \
904 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
906 #define YAMYAM_CAN_ENTER_FIELD(e, x, y) \
907 ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, Feld[x][y] == EL_DIAMOND)
909 #define DARK_YAMYAM_CAN_ENTER_FIELD(e, x, y) \
910 ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x,y, IS_FOOD_DARK_YAMYAM(Feld[x][y]))
912 #define PACMAN_CAN_ENTER_FIELD(e, x, y) \
913 ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, IS_AMOEBOID(Feld[x][y]))
915 #define PIG_CAN_ENTER_FIELD(e, x, y) \
916 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, IS_FOOD_PIG(Feld[x][y]))
918 #define PENGUIN_CAN_ENTER_FIELD(e, x, y) \
919 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (Feld[x][y] == EL_EXIT_OPEN || \
920 Feld[x][y] == EL_EM_EXIT_OPEN || \
921 Feld[x][y] == EL_STEEL_EXIT_OPEN || \
922 Feld[x][y] == EL_EM_STEEL_EXIT_OPEN || \
923 IS_FOOD_PENGUIN(Feld[x][y])))
924 #define DRAGON_CAN_ENTER_FIELD(e, x, y) \
925 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
927 #define MOLE_CAN_ENTER_FIELD(e, x, y, condition) \
928 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (condition))
930 #define SPRING_CAN_ENTER_FIELD(e, x, y) \
931 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
933 #define SPRING_CAN_BUMP_FROM_FIELD(x, y) \
934 (IN_LEV_FIELD(x, y) && (Feld[x][y] == EL_EMC_SPRING_BUMPER || \
935 Feld[x][y] == EL_EMC_SPRING_BUMPER_ACTIVE))
937 #define MOVE_ENTER_EL(e) (element_info[e].move_enter_element)
939 #define CE_ENTER_FIELD_COND(e, x, y) \
940 (!IS_PLAYER(x, y) && \
941 IS_EQUAL_OR_IN_GROUP(Feld[x][y], MOVE_ENTER_EL(e)))
943 #define CUSTOM_ELEMENT_CAN_ENTER_FIELD(e, x, y) \
944 ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, CE_ENTER_FIELD_COND(e, x, y))
946 #define IN_LEV_FIELD_AND_IS_FREE(x, y) (IN_LEV_FIELD(x, y) && IS_FREE(x, y))
947 #define IN_LEV_FIELD_AND_NOT_FREE(x, y) (IN_LEV_FIELD(x, y) && !IS_FREE(x, y))
949 #define ACCESS_FROM(e, d) (element_info[e].access_direction &(d))
950 #define IS_WALKABLE_FROM(e, d) (IS_WALKABLE(e) && ACCESS_FROM(e, d))
951 #define IS_PASSABLE_FROM(e, d) (IS_PASSABLE(e) && ACCESS_FROM(e, d))
952 #define IS_ACCESSIBLE_FROM(e, d) (IS_ACCESSIBLE(e) && ACCESS_FROM(e, d))
954 /* game button identifiers */
955 #define GAME_CTRL_ID_STOP 0
956 #define GAME_CTRL_ID_PAUSE 1
957 #define GAME_CTRL_ID_PLAY 2
958 #define SOUND_CTRL_ID_MUSIC 3
959 #define SOUND_CTRL_ID_LOOPS 4
960 #define SOUND_CTRL_ID_SIMPLE 5
962 #define NUM_GAME_BUTTONS 6
965 /* forward declaration for internal use */
967 static void CreateField(int, int, int);
969 static void ResetGfxAnimation(int, int);
971 static void SetPlayerWaiting(struct PlayerInfo *, boolean);
972 static void AdvanceFrameAndPlayerCounters(int);
974 static boolean MovePlayerOneStep(struct PlayerInfo *, int, int, int, int);
975 static boolean MovePlayer(struct PlayerInfo *, int, int);
976 static void ScrollPlayer(struct PlayerInfo *, int);
977 static void ScrollScreen(struct PlayerInfo *, int);
979 int DigField(struct PlayerInfo *, int, int, int, int, int, int, int);
981 static void InitBeltMovement(void);
982 static void CloseAllOpenTimegates(void);
983 static void CheckGravityMovement(struct PlayerInfo *);
984 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *);
985 static void KillPlayerUnlessEnemyProtected(int, int);
986 static void KillPlayerUnlessExplosionProtected(int, int);
988 static void TestIfPlayerTouchesCustomElement(int, int);
989 static void TestIfElementTouchesCustomElement(int, int);
990 static void TestIfElementHitsCustomElement(int, int, int);
992 static void TestIfElementSmashesCustomElement(int, int, int);
995 static void HandleElementChange(int, int, int);
996 static void ExecuteCustomElementAction(int, int, int, int);
997 static boolean ChangeElement(int, int, int, int);
999 static boolean CheckTriggeredElementChangeExt(int, int, int, int, int,int,int);
1000 #define CheckTriggeredElementChange(x, y, e, ev) \
1001 CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY,CH_SIDE_ANY, -1)
1002 #define CheckTriggeredElementChangeByPlayer(x, y, e, ev, p, s) \
1003 CheckTriggeredElementChangeExt(x, y, e, ev, p, s, -1)
1004 #define CheckTriggeredElementChangeBySide(x, y, e, ev, s) \
1005 CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, s, -1)
1006 #define CheckTriggeredElementChangeByPage(x, y, e, ev, p) \
1007 CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY, CH_SIDE_ANY, p)
1009 static boolean CheckElementChangeExt(int, int, int, int, int, int, int);
1010 #define CheckElementChange(x, y, e, te, ev) \
1011 CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, CH_SIDE_ANY)
1012 #define CheckElementChangeByPlayer(x, y, e, ev, p, s) \
1013 CheckElementChangeExt(x, y, e, EL_EMPTY, ev, p, s)
1014 #define CheckElementChangeBySide(x, y, e, te, ev, s) \
1015 CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, s)
1017 static void PlayLevelSound(int, int, int);
1018 static void PlayLevelSoundNearest(int, int, int);
1019 static void PlayLevelSoundAction(int, int, int);
1020 static void PlayLevelSoundElementAction(int, int, int, int);
1021 static void PlayLevelSoundElementActionIfLoop(int, int, int, int);
1022 static void PlayLevelSoundActionIfLoop(int, int, int);
1023 static void StopLevelSoundActionIfLoop(int, int, int);
1024 static void PlayLevelMusic();
1026 static void MapGameButtons();
1027 static void HandleGameButtons(struct GadgetInfo *);
1029 int AmoebeNachbarNr(int, int);
1030 void AmoebeUmwandeln(int, int);
1031 void ContinueMoving(int, int);
1032 void Bang(int, int);
1033 void InitMovDir(int, int);
1034 void InitAmoebaNr(int, int);
1035 int NewHiScore(void);
1037 void TestIfGoodThingHitsBadThing(int, int, int);
1038 void TestIfBadThingHitsGoodThing(int, int, int);
1039 void TestIfPlayerTouchesBadThing(int, int);
1040 void TestIfPlayerRunsIntoBadThing(int, int, int);
1041 void TestIfBadThingTouchesPlayer(int, int);
1042 void TestIfBadThingRunsIntoPlayer(int, int, int);
1043 void TestIfFriendTouchesBadThing(int, int);
1044 void TestIfBadThingTouchesFriend(int, int);
1045 void TestIfBadThingTouchesOtherBadThing(int, int);
1047 void KillPlayer(struct PlayerInfo *);
1048 void BuryPlayer(struct PlayerInfo *);
1049 void RemovePlayer(struct PlayerInfo *);
1051 boolean SnapField(struct PlayerInfo *, int, int);
1052 boolean DropElement(struct PlayerInfo *);
1054 static int getInvisibleActiveFromInvisibleElement(int);
1055 static int getInvisibleFromInvisibleActiveElement(int);
1057 static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
1059 /* for detection of endless loops, caused by custom element programming */
1060 /* (using maximal playfield width x 10 is just a rough approximation) */
1061 #define MAX_ELEMENT_CHANGE_RECURSION_DEPTH (MAX_PLAYFIELD_WIDTH * 10)
1063 #define RECURSION_LOOP_DETECTION_START(e, rc) \
1065 if (recursion_loop_detected) \
1068 if (recursion_loop_depth > MAX_ELEMENT_CHANGE_RECURSION_DEPTH) \
1070 recursion_loop_detected = TRUE; \
1071 recursion_loop_element = (e); \
1074 recursion_loop_depth++; \
1077 #define RECURSION_LOOP_DETECTION_END() \
1079 recursion_loop_depth--; \
1082 static int recursion_loop_depth;
1083 static boolean recursion_loop_detected;
1084 static boolean recursion_loop_element;
1087 /* ------------------------------------------------------------------------- */
1088 /* definition of elements that automatically change to other elements after */
1089 /* a specified time, eventually calling a function when changing */
1090 /* ------------------------------------------------------------------------- */
1092 /* forward declaration for changer functions */
1093 static void InitBuggyBase(int, int);
1094 static void WarnBuggyBase(int, int);
1096 static void InitTrap(int, int);
1097 static void ActivateTrap(int, int);
1098 static void ChangeActiveTrap(int, int);
1100 static void InitRobotWheel(int, int);
1101 static void RunRobotWheel(int, int);
1102 static void StopRobotWheel(int, int);
1104 static void InitTimegateWheel(int, int);
1105 static void RunTimegateWheel(int, int);
1107 static void InitMagicBallDelay(int, int);
1108 static void ActivateMagicBall(int, int);
1110 struct ChangingElementInfo
1115 void (*pre_change_function)(int x, int y);
1116 void (*change_function)(int x, int y);
1117 void (*post_change_function)(int x, int y);
1120 static struct ChangingElementInfo change_delay_list[] =
1155 EL_STEEL_EXIT_OPENING,
1163 EL_STEEL_EXIT_CLOSING,
1164 EL_STEEL_EXIT_CLOSED,
1191 EL_EM_STEEL_EXIT_OPENING,
1192 EL_EM_STEEL_EXIT_OPEN,
1199 EL_EM_STEEL_EXIT_CLOSING,
1203 EL_EM_STEEL_EXIT_CLOSED,
1227 EL_SWITCHGATE_OPENING,
1235 EL_SWITCHGATE_CLOSING,
1236 EL_SWITCHGATE_CLOSED,
1243 EL_TIMEGATE_OPENING,
1251 EL_TIMEGATE_CLOSING,
1260 EL_ACID_SPLASH_LEFT,
1268 EL_ACID_SPLASH_RIGHT,
1277 EL_SP_BUGGY_BASE_ACTIVATING,
1284 EL_SP_BUGGY_BASE_ACTIVATING,
1285 EL_SP_BUGGY_BASE_ACTIVE,
1292 EL_SP_BUGGY_BASE_ACTIVE,
1316 EL_ROBOT_WHEEL_ACTIVE,
1324 EL_TIMEGATE_SWITCH_ACTIVE,
1332 EL_DC_TIMEGATE_SWITCH_ACTIVE,
1333 EL_DC_TIMEGATE_SWITCH,
1340 EL_EMC_MAGIC_BALL_ACTIVE,
1341 EL_EMC_MAGIC_BALL_ACTIVE,
1348 EL_EMC_SPRING_BUMPER_ACTIVE,
1349 EL_EMC_SPRING_BUMPER,
1356 EL_DIAGONAL_SHRINKING,
1364 EL_DIAGONAL_GROWING,
1385 int push_delay_fixed, push_delay_random;
1389 { EL_SPRING, 0, 0 },
1390 { EL_BALLOON, 0, 0 },
1392 { EL_SOKOBAN_OBJECT, 2, 0 },
1393 { EL_SOKOBAN_FIELD_FULL, 2, 0 },
1394 { EL_SATELLITE, 2, 0 },
1395 { EL_SP_DISK_YELLOW, 2, 0 },
1397 { EL_UNDEFINED, 0, 0 },
1405 move_stepsize_list[] =
1407 { EL_AMOEBA_DROP, 2 },
1408 { EL_AMOEBA_DROPPING, 2 },
1409 { EL_QUICKSAND_FILLING, 1 },
1410 { EL_QUICKSAND_EMPTYING, 1 },
1411 { EL_QUICKSAND_FAST_FILLING, 2 },
1412 { EL_QUICKSAND_FAST_EMPTYING, 2 },
1413 { EL_MAGIC_WALL_FILLING, 2 },
1414 { EL_MAGIC_WALL_EMPTYING, 2 },
1415 { EL_BD_MAGIC_WALL_FILLING, 2 },
1416 { EL_BD_MAGIC_WALL_EMPTYING, 2 },
1417 { EL_DC_MAGIC_WALL_FILLING, 2 },
1418 { EL_DC_MAGIC_WALL_EMPTYING, 2 },
1420 { EL_UNDEFINED, 0 },
1428 collect_count_list[] =
1431 { EL_BD_DIAMOND, 1 },
1432 { EL_EMERALD_YELLOW, 1 },
1433 { EL_EMERALD_RED, 1 },
1434 { EL_EMERALD_PURPLE, 1 },
1436 { EL_SP_INFOTRON, 1 },
1440 { EL_UNDEFINED, 0 },
1448 access_direction_list[] =
1450 { EL_TUBE_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1451 { EL_TUBE_VERTICAL, MV_UP | MV_DOWN },
1452 { EL_TUBE_HORIZONTAL, MV_LEFT | MV_RIGHT },
1453 { EL_TUBE_VERTICAL_LEFT, MV_LEFT | MV_UP | MV_DOWN },
1454 { EL_TUBE_VERTICAL_RIGHT, MV_RIGHT | MV_UP | MV_DOWN },
1455 { EL_TUBE_HORIZONTAL_UP, MV_LEFT | MV_RIGHT | MV_UP },
1456 { EL_TUBE_HORIZONTAL_DOWN, MV_LEFT | MV_RIGHT | MV_DOWN },
1457 { EL_TUBE_LEFT_UP, MV_LEFT | MV_UP },
1458 { EL_TUBE_LEFT_DOWN, MV_LEFT | MV_DOWN },
1459 { EL_TUBE_RIGHT_UP, MV_RIGHT | MV_UP },
1460 { EL_TUBE_RIGHT_DOWN, MV_RIGHT | MV_DOWN },
1462 { EL_SP_PORT_LEFT, MV_RIGHT },
1463 { EL_SP_PORT_RIGHT, MV_LEFT },
1464 { EL_SP_PORT_UP, MV_DOWN },
1465 { EL_SP_PORT_DOWN, MV_UP },
1466 { EL_SP_PORT_HORIZONTAL, MV_LEFT | MV_RIGHT },
1467 { EL_SP_PORT_VERTICAL, MV_UP | MV_DOWN },
1468 { EL_SP_PORT_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1469 { EL_SP_GRAVITY_PORT_LEFT, MV_RIGHT },
1470 { EL_SP_GRAVITY_PORT_RIGHT, MV_LEFT },
1471 { EL_SP_GRAVITY_PORT_UP, MV_DOWN },
1472 { EL_SP_GRAVITY_PORT_DOWN, MV_UP },
1473 { EL_SP_GRAVITY_ON_PORT_LEFT, MV_RIGHT },
1474 { EL_SP_GRAVITY_ON_PORT_RIGHT, MV_LEFT },
1475 { EL_SP_GRAVITY_ON_PORT_UP, MV_DOWN },
1476 { EL_SP_GRAVITY_ON_PORT_DOWN, MV_UP },
1477 { EL_SP_GRAVITY_OFF_PORT_LEFT, MV_RIGHT },
1478 { EL_SP_GRAVITY_OFF_PORT_RIGHT, MV_LEFT },
1479 { EL_SP_GRAVITY_OFF_PORT_UP, MV_DOWN },
1480 { EL_SP_GRAVITY_OFF_PORT_DOWN, MV_UP },
1482 { EL_UNDEFINED, MV_NONE }
1485 static boolean trigger_events[MAX_NUM_ELEMENTS][NUM_CHANGE_EVENTS];
1487 #define IS_AUTO_CHANGING(e) (element_info[e].has_change_event[CE_DELAY])
1488 #define IS_JUST_CHANGING(x, y) (ChangeDelay[x][y] != 0)
1489 #define IS_CHANGING(x, y) (IS_AUTO_CHANGING(Feld[x][y]) || \
1490 IS_JUST_CHANGING(x, y))
1492 #define CE_PAGE(e, ce) (element_info[e].event_page[ce])
1494 /* static variables for playfield scan mode (scanning forward or backward) */
1495 static int playfield_scan_start_x = 0;
1496 static int playfield_scan_start_y = 0;
1497 static int playfield_scan_delta_x = 1;
1498 static int playfield_scan_delta_y = 1;
1500 #define SCAN_PLAYFIELD(x, y) for ((y) = playfield_scan_start_y; \
1501 (y) >= 0 && (y) <= lev_fieldy - 1; \
1502 (y) += playfield_scan_delta_y) \
1503 for ((x) = playfield_scan_start_x; \
1504 (x) >= 0 && (x) <= lev_fieldx - 1; \
1505 (x) += playfield_scan_delta_x)
1508 void DEBUG_SetMaximumDynamite()
1512 for (i = 0; i < MAX_INVENTORY_SIZE; i++)
1513 if (local_player->inventory_size < MAX_INVENTORY_SIZE)
1514 local_player->inventory_element[local_player->inventory_size++] =
1519 static void InitPlayfieldScanModeVars()
1521 if (game.use_reverse_scan_direction)
1523 playfield_scan_start_x = lev_fieldx - 1;
1524 playfield_scan_start_y = lev_fieldy - 1;
1526 playfield_scan_delta_x = -1;
1527 playfield_scan_delta_y = -1;
1531 playfield_scan_start_x = 0;
1532 playfield_scan_start_y = 0;
1534 playfield_scan_delta_x = 1;
1535 playfield_scan_delta_y = 1;
1539 static void InitPlayfieldScanMode(int mode)
1541 game.use_reverse_scan_direction =
1542 (mode == CA_ARG_SCAN_MODE_REVERSE ? TRUE : FALSE);
1544 InitPlayfieldScanModeVars();
1547 static int get_move_delay_from_stepsize(int move_stepsize)
1550 MIN(MAX(MOVE_STEPSIZE_MIN, move_stepsize), MOVE_STEPSIZE_MAX);
1552 /* make sure that stepsize value is always a power of 2 */
1553 move_stepsize = (1 << log_2(move_stepsize));
1555 return TILEX / move_stepsize;
1558 static void SetPlayerMoveSpeed(struct PlayerInfo *player, int move_stepsize,
1561 int player_nr = player->index_nr;
1562 int move_delay = get_move_delay_from_stepsize(move_stepsize);
1563 boolean cannot_move = (move_stepsize == STEPSIZE_NOT_MOVING ? TRUE : FALSE);
1565 /* do no immediately change move delay -- the player might just be moving */
1566 player->move_delay_value_next = move_delay;
1568 /* information if player can move must be set separately */
1569 player->cannot_move = cannot_move;
1573 player->move_delay = game.initial_move_delay[player_nr];
1574 player->move_delay_value = game.initial_move_delay_value[player_nr];
1576 player->move_delay_value_next = -1;
1578 player->move_delay_reset_counter = 0;
1582 void GetPlayerConfig()
1584 GameFrameDelay = setup.game_frame_delay;
1586 if (!audio.sound_available)
1587 setup.sound_simple = FALSE;
1589 if (!audio.loops_available)
1590 setup.sound_loops = FALSE;
1592 if (!audio.music_available)
1593 setup.sound_music = FALSE;
1595 if (!video.fullscreen_available)
1596 setup.fullscreen = FALSE;
1598 setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
1600 SetAudioMode(setup.sound);
1604 int GetElementFromGroupElement(int element)
1606 if (IS_GROUP_ELEMENT(element))
1608 struct ElementGroupInfo *group = element_info[element].group;
1609 int last_anim_random_frame = gfx.anim_random_frame;
1612 if (group->choice_mode == ANIM_RANDOM)
1613 gfx.anim_random_frame = RND(group->num_elements_resolved);
1615 element_pos = getAnimationFrame(group->num_elements_resolved, 1,
1616 group->choice_mode, 0,
1619 if (group->choice_mode == ANIM_RANDOM)
1620 gfx.anim_random_frame = last_anim_random_frame;
1622 group->choice_pos++;
1624 element = group->element_resolved[element_pos];
1630 static void InitPlayerField(int x, int y, int element, boolean init_game)
1632 if (element == EL_SP_MURPHY)
1636 if (stored_player[0].present)
1638 Feld[x][y] = EL_SP_MURPHY_CLONE;
1644 stored_player[0].use_murphy = TRUE;
1646 if (!level.use_artwork_element[0])
1647 stored_player[0].artwork_element = EL_SP_MURPHY;
1650 Feld[x][y] = EL_PLAYER_1;
1656 struct PlayerInfo *player = &stored_player[Feld[x][y] - EL_PLAYER_1];
1657 int jx = player->jx, jy = player->jy;
1659 player->present = TRUE;
1661 player->block_last_field = (element == EL_SP_MURPHY ?
1662 level.sp_block_last_field :
1663 level.block_last_field);
1665 /* ---------- initialize player's last field block delay --------------- */
1667 /* always start with reliable default value (no adjustment needed) */
1668 player->block_delay_adjustment = 0;
1670 /* special case 1: in Supaplex, Murphy blocks last field one more frame */
1671 if (player->block_last_field && element == EL_SP_MURPHY)
1672 player->block_delay_adjustment = 1;
1674 /* special case 2: in game engines before 3.1.1, blocking was different */
1675 if (game.use_block_last_field_bug)
1676 player->block_delay_adjustment = (player->block_last_field ? -1 : 1);
1678 if (!options.network || player->connected)
1680 player->active = TRUE;
1682 /* remove potentially duplicate players */
1683 if (StorePlayer[jx][jy] == Feld[x][y])
1684 StorePlayer[jx][jy] = 0;
1686 StorePlayer[x][y] = Feld[x][y];
1690 printf("Player %d activated.\n", player->element_nr);
1691 printf("[Local player is %d and currently %s.]\n",
1692 local_player->element_nr,
1693 local_player->active ? "active" : "not active");
1697 Feld[x][y] = EL_EMPTY;
1699 player->jx = player->last_jx = x;
1700 player->jy = player->last_jy = y;
1704 static void InitField(int x, int y, boolean init_game)
1706 int element = Feld[x][y];
1715 InitPlayerField(x, y, element, init_game);
1718 case EL_SOKOBAN_FIELD_PLAYER:
1719 element = Feld[x][y] = EL_PLAYER_1;
1720 InitField(x, y, init_game);
1722 element = Feld[x][y] = EL_SOKOBAN_FIELD_EMPTY;
1723 InitField(x, y, init_game);
1726 case EL_SOKOBAN_FIELD_EMPTY:
1727 local_player->sokobanfields_still_needed++;
1731 if (x < lev_fieldx-1 && Feld[x+1][y] == EL_ACID)
1732 Feld[x][y] = EL_ACID_POOL_TOPLEFT;
1733 else if (x > 0 && Feld[x-1][y] == EL_ACID)
1734 Feld[x][y] = EL_ACID_POOL_TOPRIGHT;
1735 else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPLEFT)
1736 Feld[x][y] = EL_ACID_POOL_BOTTOMLEFT;
1737 else if (y > 0 && Feld[x][y-1] == EL_ACID)
1738 Feld[x][y] = EL_ACID_POOL_BOTTOM;
1739 else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPRIGHT)
1740 Feld[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
1749 case EL_SPACESHIP_RIGHT:
1750 case EL_SPACESHIP_UP:
1751 case EL_SPACESHIP_LEFT:
1752 case EL_SPACESHIP_DOWN:
1753 case EL_BD_BUTTERFLY:
1754 case EL_BD_BUTTERFLY_RIGHT:
1755 case EL_BD_BUTTERFLY_UP:
1756 case EL_BD_BUTTERFLY_LEFT:
1757 case EL_BD_BUTTERFLY_DOWN:
1759 case EL_BD_FIREFLY_RIGHT:
1760 case EL_BD_FIREFLY_UP:
1761 case EL_BD_FIREFLY_LEFT:
1762 case EL_BD_FIREFLY_DOWN:
1763 case EL_PACMAN_RIGHT:
1765 case EL_PACMAN_LEFT:
1766 case EL_PACMAN_DOWN:
1768 case EL_YAMYAM_LEFT:
1769 case EL_YAMYAM_RIGHT:
1771 case EL_YAMYAM_DOWN:
1772 case EL_DARK_YAMYAM:
1775 case EL_SP_SNIKSNAK:
1776 case EL_SP_ELECTRON:
1785 case EL_AMOEBA_FULL:
1790 case EL_AMOEBA_DROP:
1791 if (y == lev_fieldy - 1)
1793 Feld[x][y] = EL_AMOEBA_GROWING;
1794 Store[x][y] = EL_AMOEBA_WET;
1798 case EL_DYNAMITE_ACTIVE:
1799 case EL_SP_DISK_RED_ACTIVE:
1800 case EL_DYNABOMB_PLAYER_1_ACTIVE:
1801 case EL_DYNABOMB_PLAYER_2_ACTIVE:
1802 case EL_DYNABOMB_PLAYER_3_ACTIVE:
1803 case EL_DYNABOMB_PLAYER_4_ACTIVE:
1804 MovDelay[x][y] = 96;
1807 case EL_EM_DYNAMITE_ACTIVE:
1808 MovDelay[x][y] = 32;
1812 local_player->lights_still_needed++;
1816 local_player->friends_still_needed++;
1821 GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
1824 case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
1825 case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
1826 case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
1827 case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
1828 case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
1829 case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
1830 case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
1831 case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
1832 case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
1833 case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
1834 case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
1835 case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
1838 int belt_nr = getBeltNrFromBeltSwitchElement(Feld[x][y]);
1839 int belt_dir = getBeltDirFromBeltSwitchElement(Feld[x][y]);
1840 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Feld[x][y]);
1842 if (game.belt_dir_nr[belt_nr] == 3) /* initial value */
1844 game.belt_dir[belt_nr] = belt_dir;
1845 game.belt_dir_nr[belt_nr] = belt_dir_nr;
1847 else /* more than one switch -- set it like the first switch */
1849 Feld[x][y] = Feld[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
1854 #if !USE_BOTH_SWITCHGATE_SWITCHES
1855 case EL_SWITCHGATE_SWITCH_DOWN: /* always start with same switch pos */
1857 Feld[x][y] = EL_SWITCHGATE_SWITCH_UP;
1860 case EL_DC_SWITCHGATE_SWITCH_DOWN: /* always start with same switch pos */
1862 Feld[x][y] = EL_DC_SWITCHGATE_SWITCH_UP;
1866 case EL_LIGHT_SWITCH_ACTIVE:
1868 game.light_time_left = level.time_light * FRAMES_PER_SECOND;
1871 case EL_INVISIBLE_STEELWALL:
1872 case EL_INVISIBLE_WALL:
1873 case EL_INVISIBLE_SAND:
1874 if (game.light_time_left > 0 ||
1875 game.lenses_time_left > 0)
1876 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
1879 case EL_EMC_MAGIC_BALL:
1880 if (game.ball_state)
1881 Feld[x][y] = EL_EMC_MAGIC_BALL_ACTIVE;
1884 case EL_EMC_MAGIC_BALL_SWITCH:
1885 if (game.ball_state)
1886 Feld[x][y] = EL_EMC_MAGIC_BALL_SWITCH_ACTIVE;
1890 if (IS_CUSTOM_ELEMENT(element))
1892 if (CAN_MOVE(element))
1895 #if USE_NEW_CUSTOM_VALUE
1896 if (!element_info[element].use_last_ce_value || init_game)
1897 CustomValue[x][y] = GET_NEW_CE_VALUE(Feld[x][y]);
1900 else if (IS_GROUP_ELEMENT(element))
1902 Feld[x][y] = GetElementFromGroupElement(element);
1904 InitField(x, y, init_game);
1911 CheckTriggeredElementChange(x, y, element, CE_CREATION_OF_X);
1914 static inline void InitField_WithBug1(int x, int y, boolean init_game)
1916 InitField(x, y, init_game);
1918 /* not needed to call InitMovDir() -- already done by InitField()! */
1919 if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1920 CAN_MOVE(Feld[x][y]))
1924 static inline void InitField_WithBug2(int x, int y, boolean init_game)
1926 int old_element = Feld[x][y];
1928 InitField(x, y, init_game);
1930 /* not needed to call InitMovDir() -- already done by InitField()! */
1931 if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1932 CAN_MOVE(old_element) &&
1933 (old_element < EL_MOLE_LEFT || old_element > EL_MOLE_DOWN))
1936 /* this case is in fact a combination of not less than three bugs:
1937 first, it calls InitMovDir() for elements that can move, although this is
1938 already done by InitField(); then, it checks the element that was at this
1939 field _before_ the call to InitField() (which can change it); lastly, it
1940 was not called for "mole with direction" elements, which were treated as
1941 "cannot move" due to (fixed) wrong element initialization in "src/init.c"
1947 static int get_key_element_from_nr(int key_nr)
1949 int key_base_element = (key_nr >= STD_NUM_KEYS ? EL_EMC_KEY_5 - STD_NUM_KEYS :
1950 level.game_engine_type == GAME_ENGINE_TYPE_EM ?
1951 EL_EM_KEY_1 : EL_KEY_1);
1953 return key_base_element + key_nr;
1956 static int get_next_dropped_element(struct PlayerInfo *player)
1958 return (player->inventory_size > 0 ?
1959 player->inventory_element[player->inventory_size - 1] :
1960 player->inventory_infinite_element != EL_UNDEFINED ?
1961 player->inventory_infinite_element :
1962 player->dynabombs_left > 0 ?
1963 EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
1967 static int get_inventory_element_from_pos(struct PlayerInfo *player, int pos)
1969 /* pos >= 0: get element from bottom of the stack;
1970 pos < 0: get element from top of the stack */
1974 int min_inventory_size = -pos;
1975 int inventory_pos = player->inventory_size - min_inventory_size;
1976 int min_dynabombs_left = min_inventory_size - player->inventory_size;
1978 return (player->inventory_size >= min_inventory_size ?
1979 player->inventory_element[inventory_pos] :
1980 player->inventory_infinite_element != EL_UNDEFINED ?
1981 player->inventory_infinite_element :
1982 player->dynabombs_left >= min_dynabombs_left ?
1983 EL_DYNABOMB_PLAYER_1 + player->index_nr :
1988 int min_dynabombs_left = pos + 1;
1989 int min_inventory_size = pos + 1 - player->dynabombs_left;
1990 int inventory_pos = pos - player->dynabombs_left;
1992 return (player->inventory_infinite_element != EL_UNDEFINED ?
1993 player->inventory_infinite_element :
1994 player->dynabombs_left >= min_dynabombs_left ?
1995 EL_DYNABOMB_PLAYER_1 + player->index_nr :
1996 player->inventory_size >= min_inventory_size ?
1997 player->inventory_element[inventory_pos] :
2002 static int compareGamePanelOrderInfo(const void *object1, const void *object2)
2004 const struct GamePanelOrderInfo *gpo1 = (struct GamePanelOrderInfo *)object1;
2005 const struct GamePanelOrderInfo *gpo2 = (struct GamePanelOrderInfo *)object2;
2008 if (gpo1->sort_priority != gpo2->sort_priority)
2009 compare_result = gpo1->sort_priority - gpo2->sort_priority;
2011 compare_result = gpo1->nr - gpo2->nr;
2013 return compare_result;
2016 void InitGameControlValues()
2020 for (i = 0; game_panel_controls[i].nr != -1; i++)
2022 struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2023 struct GamePanelOrderInfo *gpo = &game_panel_order[i];
2024 struct TextPosInfo *pos = gpc->pos;
2026 int type = gpc->type;
2030 Error(ERR_INFO, "'game_panel_controls' structure corrupted");
2031 Error(ERR_EXIT, "this should not happen -- please debug");
2034 /* force update of game controls after initialization */
2035 gpc->value = gpc->last_value = -1;
2036 gpc->frame = gpc->last_frame = -1;
2037 gpc->gfx_frame = -1;
2039 /* determine panel value width for later calculation of alignment */
2040 if (type == TYPE_INTEGER || type == TYPE_STRING)
2042 pos->width = pos->size * getFontWidth(pos->font);
2043 pos->height = getFontHeight(pos->font);
2045 else if (type == TYPE_ELEMENT)
2047 pos->width = pos->size;
2048 pos->height = pos->size;
2051 /* fill structure for game panel draw order */
2053 gpo->sort_priority = pos->sort_priority;
2056 /* sort game panel controls according to sort_priority and control number */
2057 qsort(game_panel_order, NUM_GAME_PANEL_CONTROLS,
2058 sizeof(struct GamePanelOrderInfo), compareGamePanelOrderInfo);
2061 void UpdatePlayfieldElementCount()
2065 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2066 element_info[i].element_count = 0;
2068 SCAN_PLAYFIELD(x, y)
2070 element_info[Feld[x][y]].element_count++;
2073 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
2074 for (j = 0; j < MAX_NUM_ELEMENTS; j++)
2075 if (IS_IN_GROUP(j, i))
2076 element_info[EL_GROUP_START + i].element_count +=
2077 element_info[j].element_count;
2080 void UpdateGameControlValues()
2083 int time = (local_player->LevelSolved ?
2084 local_player->LevelSolved_CountingTime :
2085 level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2086 level.native_em_level->lev->time :
2087 level.time == 0 ? TimePlayed : TimeLeft);
2088 int score = (local_player->LevelSolved ?
2089 local_player->LevelSolved_CountingScore :
2090 level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2091 level.native_em_level->lev->score :
2092 local_player->score);
2093 int gems = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2094 level.native_em_level->lev->required :
2095 local_player->gems_still_needed);
2096 int exit_closed = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2097 level.native_em_level->lev->required > 0 :
2098 local_player->gems_still_needed > 0 ||
2099 local_player->sokobanfields_still_needed > 0 ||
2100 local_player->lights_still_needed > 0);
2102 UpdatePlayfieldElementCount();
2104 /* update game panel control values */
2106 game_panel_controls[GAME_PANEL_LEVEL_NUMBER].value = level_nr;
2107 game_panel_controls[GAME_PANEL_GEMS].value = gems;
2109 game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value = 0;
2110 for (i = 0; i < MAX_NUM_KEYS; i++)
2111 game_panel_controls[GAME_PANEL_KEY_1 + i].value = EL_EMPTY;
2112 game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_EMPTY;
2113 game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value = 0;
2115 if (game.centered_player_nr == -1)
2117 for (i = 0; i < MAX_PLAYERS; i++)
2119 for (k = 0; k < MAX_NUM_KEYS; k++)
2121 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2123 if (level.native_em_level->ply[i]->keys & (1 << k))
2124 game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2125 get_key_element_from_nr(k);
2127 else if (stored_player[i].key[k])
2128 game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2129 get_key_element_from_nr(k);
2132 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2133 game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2134 level.native_em_level->ply[i]->dynamite;
2136 game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2137 stored_player[i].inventory_size;
2139 if (stored_player[i].num_white_keys > 0)
2140 game_panel_controls[GAME_PANEL_KEY_WHITE].value =
2143 game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2144 stored_player[i].num_white_keys;
2149 int player_nr = game.centered_player_nr;
2151 for (k = 0; k < MAX_NUM_KEYS; k++)
2153 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2155 if (level.native_em_level->ply[player_nr]->keys & (1 << k))
2156 game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2157 get_key_element_from_nr(k);
2159 else if (stored_player[player_nr].key[k])
2160 game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2161 get_key_element_from_nr(k);
2164 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2165 game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2166 level.native_em_level->ply[player_nr]->dynamite;
2168 game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2169 stored_player[player_nr].inventory_size;
2171 if (stored_player[player_nr].num_white_keys > 0)
2172 game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_DC_KEY_WHITE;
2174 game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2175 stored_player[player_nr].num_white_keys;
2178 for (i = 0; i < NUM_PANEL_INVENTORY; i++)
2180 game_panel_controls[GAME_PANEL_INVENTORY_FIRST_1 + i].value =
2181 get_inventory_element_from_pos(local_player, i);
2182 game_panel_controls[GAME_PANEL_INVENTORY_LAST_1 + i].value =
2183 get_inventory_element_from_pos(local_player, -i - 1);
2186 game_panel_controls[GAME_PANEL_SCORE].value = score;
2188 game_panel_controls[GAME_PANEL_TIME].value = time;
2190 game_panel_controls[GAME_PANEL_TIME_HH].value = time / 3600;
2191 game_panel_controls[GAME_PANEL_TIME_MM].value = (time / 60) % 60;
2192 game_panel_controls[GAME_PANEL_TIME_SS].value = time % 60;
2194 game_panel_controls[GAME_PANEL_SHIELD_NORMAL].value =
2195 (local_player->shield_normal_time_left > 0 ? EL_SHIELD_NORMAL_ACTIVE :
2197 game_panel_controls[GAME_PANEL_SHIELD_NORMAL_TIME].value =
2198 local_player->shield_normal_time_left;
2199 game_panel_controls[GAME_PANEL_SHIELD_DEADLY].value =
2200 (local_player->shield_deadly_time_left > 0 ? EL_SHIELD_DEADLY_ACTIVE :
2202 game_panel_controls[GAME_PANEL_SHIELD_DEADLY_TIME].value =
2203 local_player->shield_deadly_time_left;
2205 game_panel_controls[GAME_PANEL_EXIT].value =
2206 (exit_closed ? EL_EXIT_CLOSED : EL_EXIT_OPEN);
2208 game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL].value =
2209 (game.ball_state ? EL_EMC_MAGIC_BALL_ACTIVE : EL_EMC_MAGIC_BALL);
2210 game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL_SWITCH].value =
2211 (game.ball_state ? EL_EMC_MAGIC_BALL_SWITCH_ACTIVE :
2212 EL_EMC_MAGIC_BALL_SWITCH);
2214 game_panel_controls[GAME_PANEL_LIGHT_SWITCH].value =
2215 (game.light_time_left > 0 ? EL_LIGHT_SWITCH_ACTIVE : EL_LIGHT_SWITCH);
2216 game_panel_controls[GAME_PANEL_LIGHT_SWITCH_TIME].value =
2217 game.light_time_left;
2219 game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH].value =
2220 (game.timegate_time_left > 0 ? EL_TIMEGATE_OPEN : EL_TIMEGATE_CLOSED);
2221 game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH_TIME].value =
2222 game.timegate_time_left;
2224 game_panel_controls[GAME_PANEL_SWITCHGATE_SWITCH].value =
2225 EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
2227 game_panel_controls[GAME_PANEL_EMC_LENSES].value =
2228 (game.lenses_time_left > 0 ? EL_EMC_LENSES : EL_EMPTY);
2229 game_panel_controls[GAME_PANEL_EMC_LENSES_TIME].value =
2230 game.lenses_time_left;
2232 game_panel_controls[GAME_PANEL_EMC_MAGNIFIER].value =
2233 (game.magnify_time_left > 0 ? EL_EMC_MAGNIFIER : EL_EMPTY);
2234 game_panel_controls[GAME_PANEL_EMC_MAGNIFIER_TIME].value =
2235 game.magnify_time_left;
2237 game_panel_controls[GAME_PANEL_BALLOON_SWITCH].value =
2238 (game.wind_direction == MV_LEFT ? EL_BALLOON_SWITCH_LEFT :
2239 game.wind_direction == MV_RIGHT ? EL_BALLOON_SWITCH_RIGHT :
2240 game.wind_direction == MV_UP ? EL_BALLOON_SWITCH_UP :
2241 game.wind_direction == MV_DOWN ? EL_BALLOON_SWITCH_DOWN :
2242 EL_BALLOON_SWITCH_NONE);
2244 game_panel_controls[GAME_PANEL_DYNABOMB_NUMBER].value =
2245 local_player->dynabomb_count;
2246 game_panel_controls[GAME_PANEL_DYNABOMB_SIZE].value =
2247 local_player->dynabomb_size;
2248 game_panel_controls[GAME_PANEL_DYNABOMB_POWER].value =
2249 (local_player->dynabomb_xl ? EL_DYNABOMB_INCREASE_POWER : EL_EMPTY);
2251 game_panel_controls[GAME_PANEL_PENGUINS].value =
2252 local_player->friends_still_needed;
2254 game_panel_controls[GAME_PANEL_SOKOBAN_OBJECTS].value =
2255 local_player->sokobanfields_still_needed;
2256 game_panel_controls[GAME_PANEL_SOKOBAN_FIELDS].value =
2257 local_player->sokobanfields_still_needed;
2259 game_panel_controls[GAME_PANEL_ROBOT_WHEEL].value =
2260 (game.robot_wheel_active ? EL_ROBOT_WHEEL_ACTIVE : EL_ROBOT_WHEEL);
2262 for (i = 0; i < NUM_BELTS; i++)
2264 game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1 + i].value =
2265 (game.belt_dir[i] != MV_NONE ? EL_CONVEYOR_BELT_1_MIDDLE_ACTIVE :
2266 EL_CONVEYOR_BELT_1_MIDDLE) + i;
2267 game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1_SWITCH + i].value =
2268 getBeltSwitchElementFromBeltNrAndBeltDir(i, game.belt_dir[i]);
2271 game_panel_controls[GAME_PANEL_MAGIC_WALL].value =
2272 (game.magic_wall_active ? EL_MAGIC_WALL_ACTIVE : EL_MAGIC_WALL);
2273 game_panel_controls[GAME_PANEL_MAGIC_WALL_TIME].value =
2274 game.magic_wall_time_left;
2276 #if USE_PLAYER_GRAVITY
2277 game_panel_controls[GAME_PANEL_GRAVITY_STATE].value =
2278 local_player->gravity;
2280 game_panel_controls[GAME_PANEL_GRAVITY_STATE].value = game.gravity;
2283 for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2284 game_panel_controls[GAME_PANEL_ELEMENT_1 + i].value =
2285 (IS_DRAWABLE_ELEMENT(game.panel.element[i].id) ?
2286 game.panel.element[i].id : EL_UNDEFINED);
2288 for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2289 game_panel_controls[GAME_PANEL_ELEMENT_COUNT_1 + i].value =
2290 (IS_VALID_ELEMENT(game.panel.element_count[i].id) ?
2291 element_info[game.panel.element_count[i].id].element_count :
2294 for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2295 game_panel_controls[GAME_PANEL_CE_SCORE_1 + i].value =
2296 (IS_CUSTOM_ELEMENT(game.panel.ce_score[i].id) ?
2297 element_info[game.panel.ce_score[i].id].collect_score : 0);
2299 for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2300 game_panel_controls[GAME_PANEL_CE_SCORE_1_ELEMENT + i].value =
2301 (IS_CUSTOM_ELEMENT(game.panel.ce_score_element[i].id) ?
2302 element_info[game.panel.ce_score_element[i].id].collect_score :
2305 game_panel_controls[GAME_PANEL_PLAYER_NAME].value = 0;
2306 game_panel_controls[GAME_PANEL_LEVEL_NAME].value = 0;
2307 game_panel_controls[GAME_PANEL_LEVEL_AUTHOR].value = 0;
2309 /* update game panel control frames */
2311 for (i = 0; game_panel_controls[i].nr != -1; i++)
2313 struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2315 if (gpc->type == TYPE_ELEMENT)
2317 int last_anim_random_frame = gfx.anim_random_frame;
2318 int element = gpc->value;
2319 int graphic = el2panelimg(element);
2321 if (gpc->value != gpc->last_value)
2324 gpc->gfx_random = INIT_GFX_RANDOM();
2330 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2331 IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2332 gpc->gfx_random = INIT_GFX_RANDOM();
2335 if (ANIM_MODE(graphic) == ANIM_RANDOM)
2336 gfx.anim_random_frame = gpc->gfx_random;
2338 if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
2339 gpc->gfx_frame = element_info[element].collect_score;
2341 gpc->frame = getGraphicAnimationFrame(el2panelimg(gpc->value),
2344 if (ANIM_MODE(graphic) == ANIM_RANDOM)
2345 gfx.anim_random_frame = last_anim_random_frame;
2350 void DisplayGameControlValues()
2352 boolean redraw_panel = FALSE;
2355 for (i = 0; game_panel_controls[i].nr != -1; i++)
2357 struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2359 if (PANEL_DEACTIVATED(gpc->pos))
2362 if (gpc->value == gpc->last_value &&
2363 gpc->frame == gpc->last_frame)
2366 redraw_panel = TRUE;
2372 /* copy default game door content to main double buffer */
2373 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
2374 DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
2376 /* redraw game control buttons */
2378 RedrawGameButtons();
2384 game_status = GAME_MODE_PSEUDO_PANEL;
2387 for (i = 0; i < NUM_GAME_PANEL_CONTROLS; i++)
2389 for (i = 0; game_panel_controls[i].nr != -1; i++)
2393 int nr = game_panel_order[i].nr;
2394 struct GamePanelControlInfo *gpc = &game_panel_controls[nr];
2396 struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2399 struct TextPosInfo *pos = gpc->pos;
2400 int type = gpc->type;
2401 int value = gpc->value;
2402 int frame = gpc->frame;
2404 int last_value = gpc->last_value;
2405 int last_frame = gpc->last_frame;
2407 int size = pos->size;
2408 int font = pos->font;
2409 boolean draw_masked = pos->draw_masked;
2410 int mask_mode = (draw_masked ? BLIT_MASKED : BLIT_OPAQUE);
2412 if (PANEL_DEACTIVATED(pos))
2416 if (value == last_value && frame == last_frame)
2420 gpc->last_value = value;
2421 gpc->last_frame = frame;
2424 printf("::: value %d changed from %d to %d\n", nr, last_value, value);
2427 if (type == TYPE_INTEGER)
2429 if (nr == GAME_PANEL_LEVEL_NUMBER ||
2430 nr == GAME_PANEL_TIME)
2432 boolean use_dynamic_size = (size == -1 ? TRUE : FALSE);
2434 if (use_dynamic_size) /* use dynamic number of digits */
2436 int value_change = (nr == GAME_PANEL_LEVEL_NUMBER ? 100 : 1000);
2437 int size1 = (nr == GAME_PANEL_LEVEL_NUMBER ? 2 : 3);
2438 int size2 = size1 + 1;
2439 int font1 = pos->font;
2440 int font2 = pos->font_alt;
2442 size = (value < value_change ? size1 : size2);
2443 font = (value < value_change ? font1 : font2);
2446 /* clear background if value just changed its size (dynamic digits) */
2447 if ((last_value < value_change) != (value < value_change))
2449 int width1 = size1 * getFontWidth(font1);
2450 int width2 = size2 * getFontWidth(font2);
2451 int max_width = MAX(width1, width2);
2452 int max_height = MAX(getFontHeight(font1), getFontHeight(font2));
2454 pos->width = max_width;
2456 ClearRectangleOnBackground(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2457 max_width, max_height);
2464 /* correct text size if "digits" is zero or less */
2466 size = strlen(int2str(value, size));
2468 /* dynamically correct text alignment */
2469 pos->width = size * getFontWidth(font);
2472 DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2473 int2str(value, size), font, mask_mode);
2475 else if (type == TYPE_ELEMENT)
2477 int element, graphic;
2481 int dst_x = PANEL_XPOS(pos);
2482 int dst_y = PANEL_YPOS(pos);
2485 if (value != EL_UNDEFINED && value != EL_EMPTY)
2488 graphic = el2panelimg(value);
2490 getSizedGraphicSource(graphic, frame, size, &src_bitmap,
2493 width = graphic_info[graphic].width * size / TILESIZE;
2494 height = graphic_info[graphic].height * size / TILESIZE;
2498 SetClipOrigin(src_bitmap, src_bitmap->stored_clip_gc,
2499 dst_x - src_x, dst_y - src_y);
2500 BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2505 BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2510 if (value == EL_UNDEFINED || value == EL_EMPTY)
2512 element = (last_value == EL_UNDEFINED ? EL_EMPTY : last_value);
2513 graphic = el2panelimg(element);
2515 src_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
2516 src_x = DOOR_GFX_PAGEX5 + ALIGNED_TEXT_XPOS(pos);
2517 src_y = DOOR_GFX_PAGEY1 + ALIGNED_TEXT_YPOS(pos);
2522 graphic = el2panelimg(value);
2524 getSizedGraphicSource(graphic, frame, size, &src_bitmap, &src_x,&src_y);
2527 width = graphic_info[graphic].width * size / TILESIZE;
2528 height = graphic_info[graphic].height * size / TILESIZE;
2530 BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height, dst_x, dst_y);
2533 else if (type == TYPE_STRING)
2535 boolean active = (value != 0);
2536 char *state_normal = "off";
2537 char *state_active = "on";
2538 char *state = (active ? state_active : state_normal);
2539 char *s = (nr == GAME_PANEL_GRAVITY_STATE ? state :
2540 nr == GAME_PANEL_PLAYER_NAME ? setup.player_name :
2541 nr == GAME_PANEL_LEVEL_NAME ? level.name :
2542 nr == GAME_PANEL_LEVEL_AUTHOR ? level.author : NULL);
2544 if (nr == GAME_PANEL_GRAVITY_STATE)
2546 int font1 = pos->font; /* (used for normal state) */
2547 int font2 = pos->font_alt; /* (used for active state) */
2549 int size1 = strlen(state_normal);
2550 int size2 = strlen(state_active);
2551 int width1 = size1 * getFontWidth(font1);
2552 int width2 = size2 * getFontWidth(font2);
2553 int max_width = MAX(width1, width2);
2554 int max_height = MAX(getFontHeight(font1), getFontHeight(font2));
2556 pos->width = max_width;
2558 /* clear background for values that may have changed its size */
2559 ClearRectangleOnBackground(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2560 max_width, max_height);
2563 font = (active ? font2 : font1);
2573 /* don't truncate output if "chars" is zero or less */
2576 /* dynamically correct text alignment */
2577 pos->width = size * getFontWidth(font);
2581 s_cut = getStringCopyN(s, size);
2583 DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2584 s_cut, font, mask_mode);
2590 redraw_mask |= REDRAW_DOOR_1;
2593 game_status = GAME_MODE_PLAYING;
2596 void DrawGameValue_Emeralds(int value)
2598 struct TextPosInfo *pos = &game.panel.gems;
2600 int font_nr = pos->font;
2602 int font_nr = FONT_TEXT_2;
2604 int font_width = getFontWidth(font_nr);
2605 int chars = pos->size;
2608 return; /* !!! USE NEW STUFF !!! */
2611 if (PANEL_DEACTIVATED(pos))
2614 pos->width = chars * font_width;
2616 DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2619 void DrawGameValue_Dynamite(int value)
2621 struct TextPosInfo *pos = &game.panel.inventory_count;
2623 int font_nr = pos->font;
2625 int font_nr = FONT_TEXT_2;
2627 int font_width = getFontWidth(font_nr);
2628 int chars = pos->size;
2631 return; /* !!! USE NEW STUFF !!! */
2634 if (PANEL_DEACTIVATED(pos))
2637 pos->width = chars * font_width;
2639 DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2642 void DrawGameValue_Score(int value)
2644 struct TextPosInfo *pos = &game.panel.score;
2646 int font_nr = pos->font;
2648 int font_nr = FONT_TEXT_2;
2650 int font_width = getFontWidth(font_nr);
2651 int chars = pos->size;
2654 return; /* !!! USE NEW STUFF !!! */
2657 if (PANEL_DEACTIVATED(pos))
2660 pos->width = chars * font_width;
2662 DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2665 void DrawGameValue_Time(int value)
2667 struct TextPosInfo *pos = &game.panel.time;
2668 static int last_value = -1;
2671 int chars = pos->size;
2673 int font1_nr = pos->font;
2674 int font2_nr = pos->font_alt;
2676 int font1_nr = FONT_TEXT_2;
2677 int font2_nr = FONT_TEXT_1;
2679 int font_nr = font1_nr;
2680 boolean use_dynamic_chars = (chars == -1 ? TRUE : FALSE);
2683 return; /* !!! USE NEW STUFF !!! */
2686 if (PANEL_DEACTIVATED(pos))
2689 if (use_dynamic_chars) /* use dynamic number of chars */
2691 chars = (value < 1000 ? chars1 : chars2);
2692 font_nr = (value < 1000 ? font1_nr : font2_nr);
2695 /* clear background if value just changed its size (dynamic chars only) */
2696 if (use_dynamic_chars && (last_value < 1000) != (value < 1000))
2698 int width1 = chars1 * getFontWidth(font1_nr);
2699 int width2 = chars2 * getFontWidth(font2_nr);
2700 int max_width = MAX(width1, width2);
2701 int max_height = MAX(getFontHeight(font1_nr), getFontHeight(font2_nr));
2703 pos->width = max_width;
2705 ClearRectangleOnBackground(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2706 max_width, max_height);
2709 pos->width = chars * getFontWidth(font_nr);
2711 DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2716 void DrawGameValue_Level(int value)
2718 struct TextPosInfo *pos = &game.panel.level_number;
2721 int chars = pos->size;
2723 int font1_nr = pos->font;
2724 int font2_nr = pos->font_alt;
2726 int font1_nr = FONT_TEXT_2;
2727 int font2_nr = FONT_TEXT_1;
2729 int font_nr = font1_nr;
2730 boolean use_dynamic_chars = (chars == -1 ? TRUE : FALSE);
2733 return; /* !!! USE NEW STUFF !!! */
2736 if (PANEL_DEACTIVATED(pos))
2739 if (use_dynamic_chars) /* use dynamic number of chars */
2741 chars = (level_nr < 100 ? chars1 : chars2);
2742 font_nr = (level_nr < 100 ? font1_nr : font2_nr);
2745 pos->width = chars * getFontWidth(font_nr);
2747 DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2750 void DrawGameValue_Keys(int key[MAX_NUM_KEYS])
2753 struct TextPosInfo *pos = &game.panel.keys;
2756 int base_key_graphic = EL_KEY_1;
2761 return; /* !!! USE NEW STUFF !!! */
2765 if (PANEL_DEACTIVATED(pos))
2770 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2771 base_key_graphic = EL_EM_KEY_1;
2775 pos->width = 4 * MINI_TILEX;
2779 for (i = 0; i < MAX_NUM_KEYS; i++)
2781 /* currently only 4 of 8 possible keys are displayed */
2782 for (i = 0; i < STD_NUM_KEYS; i++)
2786 struct TextPosInfo *pos = &game.panel.key[i];
2788 int src_x = DOOR_GFX_PAGEX5 + 18 + (i % 4) * MINI_TILEX;
2789 int src_y = DOOR_GFX_PAGEY1 + 123;
2791 int dst_x = PANEL_XPOS(pos);
2792 int dst_y = PANEL_YPOS(pos);
2794 int dst_x = PANEL_XPOS(pos) + i * MINI_TILEX;
2795 int dst_y = PANEL_YPOS(pos);
2799 int element = (i >= STD_NUM_KEYS ? EL_EMC_KEY_5 - 4 :
2800 level.game_engine_type == GAME_ENGINE_TYPE_EM ? EL_EM_KEY_1 :
2802 int graphic = el2edimg(element);
2806 if (PANEL_DEACTIVATED(pos))
2811 /* masked blit with tiles from half-size scaled bitmap does not work yet
2812 (no mask bitmap created for these sizes after loading and scaling) --
2813 solution: load without creating mask, scale, then create final mask */
2815 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto, src_x, src_y,
2816 MINI_TILEX, MINI_TILEY, dst_x, dst_y);
2821 int graphic = el2edimg(base_key_graphic + i);
2826 getMiniGraphicSource(graphic, &src_bitmap, &src_x, &src_y);
2828 SetClipOrigin(src_bitmap, src_bitmap->stored_clip_gc,
2829 dst_x - src_x, dst_y - src_y);
2830 BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, MINI_TILEX, MINI_TILEY,
2836 DrawMiniGraphicExt(drawto, dst_x, dst_y, graphic);
2838 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto, src_x, src_y,
2839 MINI_TILEX, MINI_TILEY, dst_x, dst_y);
2842 DrawMiniGraphicExt(drawto, dst_x, dst_y, el2edimg(base_key_graphic + i));
2844 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto, src_x, src_y,
2845 MINI_TILEX, MINI_TILEY, dst_x, dst_y);
2853 void DrawGameValue_Emeralds(int value)
2855 int font_nr = FONT_TEXT_2;
2856 int xpos = (3 * 14 - 3 * getFontWidth(font_nr)) / 2;
2858 if (PANEL_DEACTIVATED(game.panel.gems))
2861 DrawText(DX_EMERALDS + xpos, DY_EMERALDS, int2str(value, 3), font_nr);
2864 void DrawGameValue_Dynamite(int value)
2866 int font_nr = FONT_TEXT_2;
2867 int xpos = (3 * 14 - 3 * getFontWidth(font_nr)) / 2;
2869 if (PANEL_DEACTIVATED(game.panel.inventory_count))
2872 DrawText(DX_DYNAMITE + xpos, DY_DYNAMITE, int2str(value, 3), font_nr);
2875 void DrawGameValue_Score(int value)
2877 int font_nr = FONT_TEXT_2;
2878 int xpos = (5 * 14 - 5 * getFontWidth(font_nr)) / 2;
2880 if (PANEL_DEACTIVATED(game.panel.score))
2883 DrawText(DX_SCORE + xpos, DY_SCORE, int2str(value, 5), font_nr);
2886 void DrawGameValue_Time(int value)
2888 int font1_nr = FONT_TEXT_2;
2890 int font2_nr = FONT_TEXT_1;
2892 int font2_nr = FONT_LEVEL_NUMBER;
2894 int xpos3 = (3 * 14 - 3 * getFontWidth(font1_nr)) / 2;
2895 int xpos4 = (4 * 10 - 4 * getFontWidth(font2_nr)) / 2;
2897 if (PANEL_DEACTIVATED(game.panel.time))
2900 /* clear background if value just changed its size */
2901 if (value == 999 || value == 1000)
2902 ClearRectangleOnBackground(drawto, DX_TIME1, DY_TIME, 14 * 3, 14);
2905 DrawText(DX_TIME1 + xpos3, DY_TIME, int2str(value, 3), font1_nr);
2907 DrawText(DX_TIME2 + xpos4, DY_TIME, int2str(value, 4), font2_nr);
2910 void DrawGameValue_Level(int value)
2912 int font1_nr = FONT_TEXT_2;
2914 int font2_nr = FONT_TEXT_1;
2916 int font2_nr = FONT_LEVEL_NUMBER;
2919 if (PANEL_DEACTIVATED(game.panel.level))
2923 DrawText(DX_LEVEL1, DY_LEVEL, int2str(value, 2), font1_nr);
2925 DrawText(DX_LEVEL2, DY_LEVEL, int2str(value, 3), font2_nr);
2928 void DrawGameValue_Keys(int key[MAX_NUM_KEYS])
2930 int base_key_graphic = EL_KEY_1;
2933 if (PANEL_DEACTIVATED(game.panel.keys))
2936 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2937 base_key_graphic = EL_EM_KEY_1;
2939 /* currently only 4 of 8 possible keys are displayed */
2940 for (i = 0; i < STD_NUM_KEYS; i++)
2942 int x = XX_KEYS + i * MINI_TILEX;
2946 DrawMiniGraphicExt(drawto, DX + x,DY + y, el2edimg(base_key_graphic + i));
2948 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
2949 DOOR_GFX_PAGEX5 + x, y, MINI_TILEX, MINI_TILEY, DX + x,DY + y);
2955 void DrawAllGameValues(int emeralds, int dynamite, int score, int time,
2958 int key[MAX_NUM_KEYS];
2961 /* prevent EM engine from updating time/score values parallel to GameWon() */
2962 if (level.game_engine_type == GAME_ENGINE_TYPE_EM &&
2963 local_player->LevelSolved)
2966 for (i = 0; i < MAX_NUM_KEYS; i++)
2967 key[i] = key_bits & (1 << i);
2969 DrawGameValue_Level(level_nr);
2971 DrawGameValue_Emeralds(emeralds);
2972 DrawGameValue_Dynamite(dynamite);
2973 DrawGameValue_Score(score);
2974 DrawGameValue_Time(time);
2976 DrawGameValue_Keys(key);
2979 void UpdateGameDoorValues()
2981 UpdateGameControlValues();
2984 void DrawGameDoorValues()
2986 DisplayGameControlValues();
2989 void DrawGameDoorValues_OLD()
2991 int time_value = (level.time == 0 ? TimePlayed : TimeLeft);
2992 int dynamite_value = 0;
2993 int score_value = (local_player->LevelSolved ? local_player->score_final :
2994 local_player->score);
2995 int gems_value = local_player->gems_still_needed;
2999 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
3001 DrawGameDoorValues_EM();
3006 if (game.centered_player_nr == -1)
3008 for (i = 0; i < MAX_PLAYERS; i++)
3010 for (j = 0; j < MAX_NUM_KEYS; j++)
3011 if (stored_player[i].key[j])
3012 key_bits |= (1 << j);
3014 dynamite_value += stored_player[i].inventory_size;
3019 int player_nr = game.centered_player_nr;
3021 for (i = 0; i < MAX_NUM_KEYS; i++)
3022 if (stored_player[player_nr].key[i])
3023 key_bits |= (1 << i);
3025 dynamite_value = stored_player[player_nr].inventory_size;
3028 DrawAllGameValues(gems_value, dynamite_value, score_value, time_value,
3034 =============================================================================
3036 -----------------------------------------------------------------------------
3037 initialize game engine due to level / tape version number
3038 =============================================================================
3041 static void InitGameEngine()
3043 int i, j, k, l, x, y;
3045 /* set game engine from tape file when re-playing, else from level file */
3046 game.engine_version = (tape.playing ? tape.engine_version :
3047 level.game_version);
3049 /* ---------------------------------------------------------------------- */
3050 /* set flags for bugs and changes according to active game engine version */
3051 /* ---------------------------------------------------------------------- */
3054 Summary of bugfix/change:
3055 Fixed handling for custom elements that change when pushed by the player.
3057 Fixed/changed in version:
3061 Before 3.1.0, custom elements that "change when pushing" changed directly
3062 after the player started pushing them (until then handled in "DigField()").
3063 Since 3.1.0, these custom elements are not changed until the "pushing"
3064 move of the element is finished (now handled in "ContinueMoving()").
3066 Affected levels/tapes:
3067 The first condition is generally needed for all levels/tapes before version
3068 3.1.0, which might use the old behaviour before it was changed; known tapes
3069 that are affected are some tapes from the level set "Walpurgis Gardens" by
3071 The second condition is an exception from the above case and is needed for
3072 the special case of tapes recorded with game (not engine!) version 3.1.0 or
3073 above (including some development versions of 3.1.0), but before it was
3074 known that this change would break tapes like the above and was fixed in
3075 3.1.1, so that the changed behaviour was active although the engine version
3076 while recording maybe was before 3.1.0. There is at least one tape that is
3077 affected by this exception, which is the tape for the one-level set "Bug
3078 Machine" by Juergen Bonhagen.
3081 game.use_change_when_pushing_bug =
3082 (game.engine_version < VERSION_IDENT(3,1,0,0) &&
3084 tape.game_version >= VERSION_IDENT(3,1,0,0) &&
3085 tape.game_version < VERSION_IDENT(3,1,1,0)));
3088 Summary of bugfix/change:
3089 Fixed handling for blocking the field the player leaves when moving.
3091 Fixed/changed in version:
3095 Before 3.1.1, when "block last field when moving" was enabled, the field
3096 the player is leaving when moving was blocked for the time of the move,
3097 and was directly unblocked afterwards. This resulted in the last field
3098 being blocked for exactly one less than the number of frames of one player
3099 move. Additionally, even when blocking was disabled, the last field was
3100 blocked for exactly one frame.
3101 Since 3.1.1, due to changes in player movement handling, the last field
3102 is not blocked at all when blocking is disabled. When blocking is enabled,
3103 the last field is blocked for exactly the number of frames of one player
3104 move. Additionally, if the player is Murphy, the hero of Supaplex, the
3105 last field is blocked for exactly one more than the number of frames of
3108 Affected levels/tapes:
3109 (!!! yet to be determined -- probably many !!!)
3112 game.use_block_last_field_bug =
3113 (game.engine_version < VERSION_IDENT(3,1,1,0));
3116 Summary of bugfix/change:
3117 Changed behaviour of CE changes with multiple changes per single frame.
3119 Fixed/changed in version:
3123 Before 3.2.0-6, only one single CE change was allowed in each engine frame.
3124 This resulted in race conditions where CEs seem to behave strange in some
3125 situations (where triggered CE changes were just skipped because there was
3126 already a CE change on that tile in the playfield in that engine frame).
3127 Since 3.2.0-6, this was changed to allow up to MAX_NUM_CHANGES_PER_FRAME.
3128 (The number of changes per frame must be limited in any case, because else
3129 it is easily possible to define CE changes that would result in an infinite
3130 loop, causing the whole game to freeze. The MAX_NUM_CHANGES_PER_FRAME value
3131 should be set large enough so that it would only be reached in cases where
3132 the corresponding CE change conditions run into a loop. Therefore, it seems
3133 to be reasonable to set MAX_NUM_CHANGES_PER_FRAME to the same value as the
3134 maximal number of change pages for custom elements.)
3136 Affected levels/tapes:
3140 #if USE_ONLY_ONE_CHANGE_PER_FRAME
3141 game.max_num_changes_per_frame = 1;
3143 game.max_num_changes_per_frame =
3144 (game.engine_version < VERSION_IDENT(3,2,0,6) ? 1 : 32);
3147 /* ---------------------------------------------------------------------- */
3149 /* default scan direction: scan playfield from top/left to bottom/right */
3150 InitPlayfieldScanMode(CA_ARG_SCAN_MODE_NORMAL);
3152 /* dynamically adjust element properties according to game engine version */
3153 InitElementPropertiesEngine(game.engine_version);
3156 printf("level %d: level version == %06d\n", level_nr, level.game_version);
3157 printf(" tape version == %06d [%s] [file: %06d]\n",
3158 tape.engine_version, (tape.playing ? "PLAYING" : "RECORDING"),
3160 printf(" => game.engine_version == %06d\n", game.engine_version);
3163 /* ---------- initialize player's initial move delay --------------------- */
3165 /* dynamically adjust player properties according to level information */
3166 for (i = 0; i < MAX_PLAYERS; i++)
3167 game.initial_move_delay_value[i] =
3168 get_move_delay_from_stepsize(level.initial_player_stepsize[i]);
3170 /* dynamically adjust player properties according to game engine version */
3171 for (i = 0; i < MAX_PLAYERS; i++)
3172 game.initial_move_delay[i] =
3173 (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
3174 game.initial_move_delay_value[i] : 0);
3176 /* ---------- initialize player's initial push delay --------------------- */
3178 /* dynamically adjust player properties according to game engine version */
3179 game.initial_push_delay_value =
3180 (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
3182 /* ---------- initialize changing elements ------------------------------- */
3184 /* initialize changing elements information */
3185 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3187 struct ElementInfo *ei = &element_info[i];
3189 /* this pointer might have been changed in the level editor */
3190 ei->change = &ei->change_page[0];
3192 if (!IS_CUSTOM_ELEMENT(i))
3194 ei->change->target_element = EL_EMPTY_SPACE;
3195 ei->change->delay_fixed = 0;
3196 ei->change->delay_random = 0;
3197 ei->change->delay_frames = 1;
3200 for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3202 ei->has_change_event[j] = FALSE;
3204 ei->event_page_nr[j] = 0;
3205 ei->event_page[j] = &ei->change_page[0];
3209 /* add changing elements from pre-defined list */
3210 for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
3212 struct ChangingElementInfo *ch_delay = &change_delay_list[i];
3213 struct ElementInfo *ei = &element_info[ch_delay->element];
3215 ei->change->target_element = ch_delay->target_element;
3216 ei->change->delay_fixed = ch_delay->change_delay;
3218 ei->change->pre_change_function = ch_delay->pre_change_function;
3219 ei->change->change_function = ch_delay->change_function;
3220 ei->change->post_change_function = ch_delay->post_change_function;
3222 ei->change->can_change = TRUE;
3223 ei->change->can_change_or_has_action = TRUE;
3225 ei->has_change_event[CE_DELAY] = TRUE;
3227 SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
3228 SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE_OR_HAS_ACTION, TRUE);
3231 /* ---------- initialize internal run-time variables ------------- */
3233 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3235 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3237 for (j = 0; j < ei->num_change_pages; j++)
3239 ei->change_page[j].can_change_or_has_action =
3240 (ei->change_page[j].can_change |
3241 ei->change_page[j].has_action);
3245 /* add change events from custom element configuration */
3246 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3248 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3250 for (j = 0; j < ei->num_change_pages; j++)
3252 if (!ei->change_page[j].can_change_or_has_action)
3255 for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3257 /* only add event page for the first page found with this event */
3258 if (ei->change_page[j].has_event[k] && !(ei->has_change_event[k]))
3260 ei->has_change_event[k] = TRUE;
3262 ei->event_page_nr[k] = j;
3263 ei->event_page[k] = &ei->change_page[j];
3269 /* ---------- initialize run-time trigger player and element ------------- */
3271 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3273 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3275 for (j = 0; j < ei->num_change_pages; j++)
3277 ei->change_page[j].actual_trigger_element = EL_EMPTY;
3278 ei->change_page[j].actual_trigger_player = EL_PLAYER_1;
3279 ei->change_page[j].actual_trigger_side = CH_SIDE_NONE;
3280 ei->change_page[j].actual_trigger_ce_value = 0;
3281 ei->change_page[j].actual_trigger_ce_score = 0;
3285 /* ---------- initialize trigger events ---------------------------------- */
3287 /* initialize trigger events information */
3288 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3289 for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3290 trigger_events[i][j] = FALSE;
3292 /* add trigger events from element change event properties */
3293 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3295 struct ElementInfo *ei = &element_info[i];
3297 for (j = 0; j < ei->num_change_pages; j++)
3299 if (!ei->change_page[j].can_change_or_has_action)
3302 if (ei->change_page[j].has_event[CE_BY_OTHER_ACTION])
3304 int trigger_element = ei->change_page[j].trigger_element;
3306 for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3308 if (ei->change_page[j].has_event[k])
3310 if (IS_GROUP_ELEMENT(trigger_element))
3312 struct ElementGroupInfo *group =
3313 element_info[trigger_element].group;
3315 for (l = 0; l < group->num_elements_resolved; l++)
3316 trigger_events[group->element_resolved[l]][k] = TRUE;
3318 else if (trigger_element == EL_ANY_ELEMENT)
3319 for (l = 0; l < MAX_NUM_ELEMENTS; l++)
3320 trigger_events[l][k] = TRUE;
3322 trigger_events[trigger_element][k] = TRUE;
3329 /* ---------- initialize push delay -------------------------------------- */
3331 /* initialize push delay values to default */
3332 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3334 if (!IS_CUSTOM_ELEMENT(i))
3336 /* set default push delay values (corrected since version 3.0.7-1) */
3337 if (game.engine_version < VERSION_IDENT(3,0,7,1))
3339 element_info[i].push_delay_fixed = 2;
3340 element_info[i].push_delay_random = 8;
3344 element_info[i].push_delay_fixed = 8;
3345 element_info[i].push_delay_random = 8;
3350 /* set push delay value for certain elements from pre-defined list */
3351 for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
3353 int e = push_delay_list[i].element;
3355 element_info[e].push_delay_fixed = push_delay_list[i].push_delay_fixed;
3356 element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
3359 /* set push delay value for Supaplex elements for newer engine versions */
3360 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
3362 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3364 if (IS_SP_ELEMENT(i))
3366 /* set SP push delay to just enough to push under a falling zonk */
3367 int delay = (game.engine_version >= VERSION_IDENT(3,1,1,0) ? 8 : 6);
3369 element_info[i].push_delay_fixed = delay;
3370 element_info[i].push_delay_random = 0;
3375 /* ---------- initialize move stepsize ----------------------------------- */
3377 /* initialize move stepsize values to default */
3378 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3379 if (!IS_CUSTOM_ELEMENT(i))
3380 element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
3382 /* set move stepsize value for certain elements from pre-defined list */
3383 for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
3385 int e = move_stepsize_list[i].element;
3387 element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
3390 /* ---------- initialize collect score ----------------------------------- */
3392 /* initialize collect score values for custom elements from initial value */
3393 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3394 if (IS_CUSTOM_ELEMENT(i))
3395 element_info[i].collect_score = element_info[i].collect_score_initial;
3397 /* ---------- initialize collect count ----------------------------------- */
3399 /* initialize collect count values for non-custom elements */
3400 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3401 if (!IS_CUSTOM_ELEMENT(i))
3402 element_info[i].collect_count_initial = 0;
3404 /* add collect count values for all elements from pre-defined list */
3405 for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
3406 element_info[collect_count_list[i].element].collect_count_initial =
3407 collect_count_list[i].count;
3409 /* ---------- initialize access direction -------------------------------- */
3411 /* initialize access direction values to default (access from every side) */
3412 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3413 if (!IS_CUSTOM_ELEMENT(i))
3414 element_info[i].access_direction = MV_ALL_DIRECTIONS;
3416 /* set access direction value for certain elements from pre-defined list */
3417 for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
3418 element_info[access_direction_list[i].element].access_direction =
3419 access_direction_list[i].direction;
3421 /* ---------- initialize explosion content ------------------------------- */
3422 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3424 if (IS_CUSTOM_ELEMENT(i))
3427 for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
3429 /* (content for EL_YAMYAM set at run-time with game.yamyam_content_nr) */
3431 element_info[i].content.e[x][y] =
3432 (i == EL_PLAYER_1 ? EL_EMERALD_YELLOW :
3433 i == EL_PLAYER_2 ? EL_EMERALD_RED :
3434 i == EL_PLAYER_3 ? EL_EMERALD :
3435 i == EL_PLAYER_4 ? EL_EMERALD_PURPLE :
3436 i == EL_MOLE ? EL_EMERALD_RED :
3437 i == EL_PENGUIN ? EL_EMERALD_PURPLE :
3438 i == EL_BUG ? (x == 1 && y == 1 ? EL_DIAMOND : EL_EMERALD) :
3439 i == EL_BD_BUTTERFLY ? EL_BD_DIAMOND :
3440 i == EL_SP_ELECTRON ? EL_SP_INFOTRON :
3441 i == EL_AMOEBA_TO_DIAMOND ? level.amoeba_content :
3442 i == EL_WALL_EMERALD ? EL_EMERALD :
3443 i == EL_WALL_DIAMOND ? EL_DIAMOND :
3444 i == EL_WALL_BD_DIAMOND ? EL_BD_DIAMOND :
3445 i == EL_WALL_EMERALD_YELLOW ? EL_EMERALD_YELLOW :
3446 i == EL_WALL_EMERALD_RED ? EL_EMERALD_RED :
3447 i == EL_WALL_EMERALD_PURPLE ? EL_EMERALD_PURPLE :
3448 i == EL_WALL_PEARL ? EL_PEARL :
3449 i == EL_WALL_CRYSTAL ? EL_CRYSTAL :
3454 /* ---------- initialize recursion detection ------------------------------ */
3455 recursion_loop_depth = 0;
3456 recursion_loop_detected = FALSE;
3457 recursion_loop_element = EL_UNDEFINED;
3459 /* ---------- initialize graphics engine ---------------------------------- */
3460 game.scroll_delay_value =
3461 (game.forced_scroll_delay_value != -1 ? game.forced_scroll_delay_value :
3462 setup.scroll_delay ? setup.scroll_delay_value : 0);
3463 game.scroll_delay_value =
3464 MIN(MAX(MIN_SCROLL_DELAY, game.scroll_delay_value), MAX_SCROLL_DELAY);
3467 int get_num_special_action(int element, int action_first, int action_last)
3469 int num_special_action = 0;
3472 for (i = action_first; i <= action_last; i++)
3474 boolean found = FALSE;
3476 for (j = 0; j < NUM_DIRECTIONS; j++)
3477 if (el_act_dir2img(element, i, j) !=
3478 el_act_dir2img(element, ACTION_DEFAULT, j))
3482 num_special_action++;
3487 return num_special_action;
3492 =============================================================================
3494 -----------------------------------------------------------------------------
3495 initialize and start new game
3496 =============================================================================
3501 boolean emulate_bd = TRUE; /* unless non-BOULDERDASH elements found */
3502 boolean emulate_sb = TRUE; /* unless non-SOKOBAN elements found */
3503 boolean emulate_sp = TRUE; /* unless non-SUPAPLEX elements found */
3505 boolean do_fading = (game_status == GAME_MODE_MAIN);
3509 game_status = GAME_MODE_PLAYING;
3512 InitGameControlValues();
3514 /* don't play tapes over network */
3515 network_playing = (options.network && !tape.playing);
3517 for (i = 0; i < MAX_PLAYERS; i++)
3519 struct PlayerInfo *player = &stored_player[i];
3521 player->index_nr = i;
3522 player->index_bit = (1 << i);
3523 player->element_nr = EL_PLAYER_1 + i;
3525 player->present = FALSE;
3526 player->active = FALSE;
3527 player->killed = FALSE;
3530 player->effective_action = 0;
3531 player->programmed_action = 0;
3534 player->score_final = 0;
3536 player->gems_still_needed = level.gems_needed;
3537 player->sokobanfields_still_needed = 0;
3538 player->lights_still_needed = 0;
3539 player->friends_still_needed = 0;
3541 for (j = 0; j < MAX_NUM_KEYS; j++)
3542 player->key[j] = FALSE;
3544 player->num_white_keys = 0;
3546 player->dynabomb_count = 0;
3547 player->dynabomb_size = 1;
3548 player->dynabombs_left = 0;
3549 player->dynabomb_xl = FALSE;
3551 player->MovDir = MV_NONE;
3554 player->GfxDir = MV_NONE;
3555 player->GfxAction = ACTION_DEFAULT;
3557 player->StepFrame = 0;
3559 player->use_murphy = FALSE;
3560 player->artwork_element =
3561 (level.use_artwork_element[i] ? level.artwork_element[i] :
3562 player->element_nr);
3564 player->block_last_field = FALSE; /* initialized in InitPlayerField() */
3565 player->block_delay_adjustment = 0; /* initialized in InitPlayerField() */
3567 player->gravity = level.initial_player_gravity[i];
3569 player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
3571 player->actual_frame_counter = 0;
3573 player->step_counter = 0;
3575 player->last_move_dir = MV_NONE;
3577 player->is_active = FALSE;
3579 player->is_waiting = FALSE;
3580 player->is_moving = FALSE;
3581 player->is_auto_moving = FALSE;
3582 player->is_digging = FALSE;
3583 player->is_snapping = FALSE;
3584 player->is_collecting = FALSE;
3585 player->is_pushing = FALSE;
3586 player->is_switching = FALSE;
3587 player->is_dropping = FALSE;
3588 player->is_dropping_pressed = FALSE;
3590 player->is_bored = FALSE;
3591 player->is_sleeping = FALSE;
3593 player->frame_counter_bored = -1;
3594 player->frame_counter_sleeping = -1;
3596 player->anim_delay_counter = 0;
3597 player->post_delay_counter = 0;
3599 player->dir_waiting = MV_NONE;
3600 player->action_waiting = ACTION_DEFAULT;
3601 player->last_action_waiting = ACTION_DEFAULT;
3602 player->special_action_bored = ACTION_DEFAULT;
3603 player->special_action_sleeping = ACTION_DEFAULT;
3605 player->switch_x = -1;
3606 player->switch_y = -1;
3608 player->drop_x = -1;
3609 player->drop_y = -1;
3611 player->show_envelope = 0;
3613 SetPlayerMoveSpeed(player, level.initial_player_stepsize[i], TRUE);
3615 player->push_delay = -1; /* initialized when pushing starts */
3616 player->push_delay_value = game.initial_push_delay_value;
3618 player->drop_delay = 0;
3619 player->drop_pressed_delay = 0;
3621 player->last_jx = -1;
3622 player->last_jy = -1;
3626 player->shield_normal_time_left = 0;
3627 player->shield_deadly_time_left = 0;
3629 player->inventory_infinite_element = EL_UNDEFINED;
3630 player->inventory_size = 0;
3632 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
3633 SnapField(player, 0, 0);
3635 player->LevelSolved = FALSE;
3636 player->GameOver = FALSE;
3638 player->LevelSolved_GameWon = FALSE;
3639 player->LevelSolved_GameEnd = FALSE;
3640 player->LevelSolved_PanelOff = FALSE;
3641 player->LevelSolved_SaveTape = FALSE;
3642 player->LevelSolved_SaveScore = FALSE;
3643 player->LevelSolved_CountingTime = 0;
3644 player->LevelSolved_CountingScore = 0;
3647 network_player_action_received = FALSE;
3649 #if defined(NETWORK_AVALIABLE)
3650 /* initial null action */
3651 if (network_playing)
3652 SendToServer_MovePlayer(MV_NONE);
3661 TimeLeft = level.time;
3664 ScreenMovDir = MV_NONE;
3668 ScrollStepSize = 0; /* will be correctly initialized by ScrollScreen() */
3670 AllPlayersGone = FALSE;
3672 game.yamyam_content_nr = 0;
3673 game.robot_wheel_active = FALSE;
3674 game.magic_wall_active = FALSE;
3675 game.magic_wall_time_left = 0;
3676 game.light_time_left = 0;
3677 game.timegate_time_left = 0;
3678 game.switchgate_pos = 0;
3679 game.wind_direction = level.wind_direction_initial;
3681 #if !USE_PLAYER_GRAVITY
3682 game.gravity = FALSE;
3683 game.explosions_delayed = TRUE;
3686 game.lenses_time_left = 0;
3687 game.magnify_time_left = 0;
3689 game.ball_state = level.ball_state_initial;
3690 game.ball_content_nr = 0;
3692 game.envelope_active = FALSE;
3694 /* set focus to local player for network games, else to all players */
3695 game.centered_player_nr = (network_playing ? local_player->index_nr : -1);
3696 game.centered_player_nr_next = game.centered_player_nr;
3697 game.set_centered_player = FALSE;
3699 if (network_playing && tape.recording)
3701 /* store client dependent player focus when recording network games */
3702 tape.centered_player_nr_next = game.centered_player_nr_next;
3703 tape.set_centered_player = TRUE;
3706 for (i = 0; i < NUM_BELTS; i++)
3708 game.belt_dir[i] = MV_NONE;
3709 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
3712 for (i = 0; i < MAX_NUM_AMOEBA; i++)
3713 AmoebaCnt[i] = AmoebaCnt2[i] = 0;
3715 SCAN_PLAYFIELD(x, y)
3717 Feld[x][y] = level.field[x][y];
3718 MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
3719 ChangeDelay[x][y] = 0;
3720 ChangePage[x][y] = -1;
3721 #if USE_NEW_CUSTOM_VALUE
3722 CustomValue[x][y] = 0; /* initialized in InitField() */
3724 Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
3726 WasJustMoving[x][y] = 0;
3727 WasJustFalling[x][y] = 0;
3728 CheckCollision[x][y] = 0;
3729 CheckImpact[x][y] = 0;
3731 Pushed[x][y] = FALSE;
3733 ChangeCount[x][y] = 0;
3734 ChangeEvent[x][y] = -1;
3736 ExplodePhase[x][y] = 0;
3737 ExplodeDelay[x][y] = 0;
3738 ExplodeField[x][y] = EX_TYPE_NONE;
3740 RunnerVisit[x][y] = 0;
3741 PlayerVisit[x][y] = 0;
3744 GfxRandom[x][y] = INIT_GFX_RANDOM();
3745 GfxElement[x][y] = EL_UNDEFINED;
3746 GfxAction[x][y] = ACTION_DEFAULT;
3747 GfxDir[x][y] = MV_NONE;
3750 SCAN_PLAYFIELD(x, y)
3752 if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
3754 if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
3756 if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
3759 InitField(x, y, TRUE);
3761 ResetGfxAnimation(x, y);
3766 for (i = 0; i < MAX_PLAYERS; i++)
3768 struct PlayerInfo *player = &stored_player[i];
3770 /* set number of special actions for bored and sleeping animation */
3771 player->num_special_action_bored =
3772 get_num_special_action(player->artwork_element,
3773 ACTION_BORING_1, ACTION_BORING_LAST);
3774 player->num_special_action_sleeping =
3775 get_num_special_action(player->artwork_element,
3776 ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
3779 game.emulation = (emulate_bd ? EMU_BOULDERDASH :
3780 emulate_sb ? EMU_SOKOBAN :
3781 emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
3783 #if USE_NEW_ALL_SLIPPERY
3784 /* initialize type of slippery elements */
3785 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3787 if (!IS_CUSTOM_ELEMENT(i))
3789 /* default: elements slip down either to the left or right randomly */
3790 element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
3792 /* SP style elements prefer to slip down on the left side */
3793 if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
3794 element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3796 /* BD style elements prefer to slip down on the left side */
3797 if (game.emulation == EMU_BOULDERDASH)
3798 element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3803 /* initialize explosion and ignition delay */
3804 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3806 if (!IS_CUSTOM_ELEMENT(i))
3809 int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
3810 game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
3811 game.emulation == EMU_SUPAPLEX ? 3 : 2);
3812 int last_phase = (num_phase + 1) * delay;
3813 int half_phase = (num_phase / 2) * delay;
3815 element_info[i].explosion_delay = last_phase - 1;
3816 element_info[i].ignition_delay = half_phase;
3818 if (i == EL_BLACK_ORB)
3819 element_info[i].ignition_delay = 1;
3823 if (element_info[i].explosion_delay < 1) /* !!! check again !!! */
3824 element_info[i].explosion_delay = 1;
3826 if (element_info[i].ignition_delay < 1) /* !!! check again !!! */
3827 element_info[i].ignition_delay = 1;
3831 /* correct non-moving belts to start moving left */
3832 for (i = 0; i < NUM_BELTS; i++)
3833 if (game.belt_dir[i] == MV_NONE)
3834 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
3836 /* check if any connected player was not found in playfield */
3837 for (i = 0; i < MAX_PLAYERS; i++)
3839 struct PlayerInfo *player = &stored_player[i];
3841 if (player->connected && !player->present)
3843 for (j = 0; j < MAX_PLAYERS; j++)
3845 struct PlayerInfo *some_player = &stored_player[j];
3846 int jx = some_player->jx, jy = some_player->jy;
3848 /* assign first free player found that is present in the playfield */
3849 if (some_player->present && !some_player->connected)
3851 player->present = TRUE;
3852 player->active = TRUE;
3854 some_player->present = FALSE;
3855 some_player->active = FALSE;
3857 player->artwork_element = some_player->artwork_element;
3859 player->block_last_field = some_player->block_last_field;
3860 player->block_delay_adjustment = some_player->block_delay_adjustment;
3862 StorePlayer[jx][jy] = player->element_nr;
3863 player->jx = player->last_jx = jx;
3864 player->jy = player->last_jy = jy;
3874 /* when playing a tape, eliminate all players who do not participate */
3876 for (i = 0; i < MAX_PLAYERS; i++)
3878 if (stored_player[i].active && !tape.player_participates[i])
3880 struct PlayerInfo *player = &stored_player[i];
3881 int jx = player->jx, jy = player->jy;
3883 player->active = FALSE;
3884 StorePlayer[jx][jy] = 0;
3885 Feld[jx][jy] = EL_EMPTY;
3889 else if (!options.network && !setup.team_mode) /* && !tape.playing */
3891 /* when in single player mode, eliminate all but the first active player */
3893 for (i = 0; i < MAX_PLAYERS; i++)
3895 if (stored_player[i].active)
3897 for (j = i + 1; j < MAX_PLAYERS; j++)
3899 if (stored_player[j].active)
3901 struct PlayerInfo *player = &stored_player[j];
3902 int jx = player->jx, jy = player->jy;
3904 player->active = FALSE;
3905 player->present = FALSE;
3907 StorePlayer[jx][jy] = 0;
3908 Feld[jx][jy] = EL_EMPTY;
3915 /* when recording the game, store which players take part in the game */
3918 for (i = 0; i < MAX_PLAYERS; i++)
3919 if (stored_player[i].active)
3920 tape.player_participates[i] = TRUE;
3925 for (i = 0; i < MAX_PLAYERS; i++)
3927 struct PlayerInfo *player = &stored_player[i];
3929 printf("Player %d: present == %d, connected == %d, active == %d.\n",
3934 if (local_player == player)
3935 printf("Player %d is local player.\n", i+1);
3939 if (BorderElement == EL_EMPTY)
3942 SBX_Right = lev_fieldx - SCR_FIELDX;
3944 SBY_Lower = lev_fieldy - SCR_FIELDY;
3949 SBX_Right = lev_fieldx - SCR_FIELDX + 1;
3951 SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
3954 if (lev_fieldx + (SBX_Left == -1 ? 2 : 0) <= SCR_FIELDX)
3955 SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
3957 if (lev_fieldy + (SBY_Upper == -1 ? 2 : 0) <= SCR_FIELDY)
3958 SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
3960 /* if local player not found, look for custom element that might create
3961 the player (make some assumptions about the right custom element) */
3962 if (!local_player->present)
3964 int start_x = 0, start_y = 0;
3965 int found_rating = 0;
3966 int found_element = EL_UNDEFINED;
3967 int player_nr = local_player->index_nr;
3969 SCAN_PLAYFIELD(x, y)
3971 int element = Feld[x][y];
3976 if (level.use_start_element[player_nr] &&
3977 level.start_element[player_nr] == element &&
3984 found_element = element;
3987 if (!IS_CUSTOM_ELEMENT(element))
3990 if (CAN_CHANGE(element))
3992 for (i = 0; i < element_info[element].num_change_pages; i++)
3994 /* check for player created from custom element as single target */
3995 content = element_info[element].change_page[i].target_element;
3996 is_player = ELEM_IS_PLAYER(content);
3998 if (is_player && (found_rating < 3 ||
3999 (found_rating == 3 && element < found_element)))
4005 found_element = element;
4010 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
4012 /* check for player created from custom element as explosion content */
4013 content = element_info[element].content.e[xx][yy];
4014 is_player = ELEM_IS_PLAYER(content);
4016 if (is_player && (found_rating < 2 ||
4017 (found_rating == 2 && element < found_element)))
4019 start_x = x + xx - 1;
4020 start_y = y + yy - 1;
4023 found_element = element;
4026 if (!CAN_CHANGE(element))
4029 for (i = 0; i < element_info[element].num_change_pages; i++)
4031 /* check for player created from custom element as extended target */
4033 element_info[element].change_page[i].target_content.e[xx][yy];
4035 is_player = ELEM_IS_PLAYER(content);
4037 if (is_player && (found_rating < 1 ||
4038 (found_rating == 1 && element < found_element)))
4040 start_x = x + xx - 1;
4041 start_y = y + yy - 1;
4044 found_element = element;
4050 scroll_x = (start_x < SBX_Left + MIDPOSX ? SBX_Left :
4051 start_x > SBX_Right + MIDPOSX ? SBX_Right :
4054 scroll_y = (start_y < SBY_Upper + MIDPOSY ? SBY_Upper :
4055 start_y > SBY_Lower + MIDPOSY ? SBY_Lower :
4060 scroll_x = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
4061 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
4062 local_player->jx - MIDPOSX);
4064 scroll_y = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
4065 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
4066 local_player->jy - MIDPOSY);
4069 /* do not use PLAYING mask for fading out from main screen */
4070 game_status = GAME_MODE_MAIN;
4074 if (!game.restart_level)
4075 CloseDoor(DOOR_CLOSE_1);
4078 if (level_editor_test_game)
4079 FadeSkipNextFadeIn();
4081 FadeSetEnterScreen();
4083 if (level_editor_test_game)
4084 fading = fading_none;
4086 fading = menu.destination;
4090 FadeOut(REDRAW_FIELD);
4093 FadeOut(REDRAW_FIELD);
4096 game_status = GAME_MODE_PLAYING;
4098 /* !!! FIX THIS (START) !!! */
4099 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
4101 InitGameEngine_EM();
4103 /* blit playfield from scroll buffer to normal back buffer for fading in */
4104 BlitScreenToBitmap_EM(backbuffer);
4111 /* after drawing the level, correct some elements */
4112 if (game.timegate_time_left == 0)
4113 CloseAllOpenTimegates();
4115 /* blit playfield from scroll buffer to normal back buffer for fading in */
4116 if (setup.soft_scrolling)
4117 BlitBitmap(fieldbuffer, backbuffer, FX, FY, SXSIZE, SYSIZE, SX, SY);
4119 redraw_mask |= REDRAW_FROM_BACKBUFFER;
4121 /* !!! FIX THIS (END) !!! */
4124 FadeIn(REDRAW_FIELD);
4127 FadeIn(REDRAW_FIELD);
4132 if (!game.restart_level)
4134 /* copy default game door content to main double buffer */
4135 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
4136 DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
4139 SetPanelBackground();
4140 SetDrawBackgroundMask(REDRAW_DOOR_1);
4142 UpdateGameDoorValues();
4143 DrawGameDoorValues();
4145 if (!game.restart_level)
4149 game_gadget[SOUND_CTRL_ID_MUSIC]->checked = setup.sound_music;
4150 game_gadget[SOUND_CTRL_ID_LOOPS]->checked = setup.sound_loops;
4151 game_gadget[SOUND_CTRL_ID_SIMPLE]->checked = setup.sound_simple;
4155 /* copy actual game door content to door double buffer for OpenDoor() */
4156 BlitBitmap(drawto, bitmap_db_door,
4157 DX, DY, DXSIZE, DYSIZE, DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1);
4159 OpenDoor(DOOR_OPEN_ALL);
4161 PlaySound(SND_GAME_STARTING);
4163 if (setup.sound_music)
4166 KeyboardAutoRepeatOffUnlessAutoplay();
4170 for (i = 0; i < MAX_PLAYERS; i++)
4171 printf("Player %d %sactive.\n",
4172 i + 1, (stored_player[i].active ? "" : "not "));
4183 game.restart_level = FALSE;
4186 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y)
4188 /* this is used for non-R'n'D game engines to update certain engine values */
4190 /* needed to determine if sounds are played within the visible screen area */
4191 scroll_x = actual_scroll_x;
4192 scroll_y = actual_scroll_y;
4195 void InitMovDir(int x, int y)
4197 int i, element = Feld[x][y];
4198 static int xy[4][2] =
4205 static int direction[3][4] =
4207 { MV_RIGHT, MV_UP, MV_LEFT, MV_DOWN },
4208 { MV_LEFT, MV_DOWN, MV_RIGHT, MV_UP },
4209 { MV_LEFT, MV_RIGHT, MV_UP, MV_DOWN }
4218 Feld[x][y] = EL_BUG;
4219 MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
4222 case EL_SPACESHIP_RIGHT:
4223 case EL_SPACESHIP_UP:
4224 case EL_SPACESHIP_LEFT:
4225 case EL_SPACESHIP_DOWN:
4226 Feld[x][y] = EL_SPACESHIP;
4227 MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
4230 case EL_BD_BUTTERFLY_RIGHT:
4231 case EL_BD_BUTTERFLY_UP:
4232 case EL_BD_BUTTERFLY_LEFT:
4233 case EL_BD_BUTTERFLY_DOWN:
4234 Feld[x][y] = EL_BD_BUTTERFLY;
4235 MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
4238 case EL_BD_FIREFLY_RIGHT:
4239 case EL_BD_FIREFLY_UP:
4240 case EL_BD_FIREFLY_LEFT:
4241 case EL_BD_FIREFLY_DOWN:
4242 Feld[x][y] = EL_BD_FIREFLY;
4243 MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
4246 case EL_PACMAN_RIGHT:
4248 case EL_PACMAN_LEFT:
4249 case EL_PACMAN_DOWN:
4250 Feld[x][y] = EL_PACMAN;
4251 MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
4254 case EL_YAMYAM_LEFT:
4255 case EL_YAMYAM_RIGHT:
4257 case EL_YAMYAM_DOWN:
4258 Feld[x][y] = EL_YAMYAM;
4259 MovDir[x][y] = direction[2][element - EL_YAMYAM_LEFT];
4262 case EL_SP_SNIKSNAK:
4263 MovDir[x][y] = MV_UP;
4266 case EL_SP_ELECTRON:
4267 MovDir[x][y] = MV_LEFT;
4274 Feld[x][y] = EL_MOLE;
4275 MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
4279 if (IS_CUSTOM_ELEMENT(element))
4281 struct ElementInfo *ei = &element_info[element];
4282 int move_direction_initial = ei->move_direction_initial;
4283 int move_pattern = ei->move_pattern;
4285 if (move_direction_initial == MV_START_PREVIOUS)
4287 if (MovDir[x][y] != MV_NONE)
4290 move_direction_initial = MV_START_AUTOMATIC;
4293 if (move_direction_initial == MV_START_RANDOM)
4294 MovDir[x][y] = 1 << RND(4);
4295 else if (move_direction_initial & MV_ANY_DIRECTION)
4296 MovDir[x][y] = move_direction_initial;
4297 else if (move_pattern == MV_ALL_DIRECTIONS ||
4298 move_pattern == MV_TURNING_LEFT ||
4299 move_pattern == MV_TURNING_RIGHT ||
4300 move_pattern == MV_TURNING_LEFT_RIGHT ||
4301 move_pattern == MV_TURNING_RIGHT_LEFT ||
4302 move_pattern == MV_TURNING_RANDOM)
4303 MovDir[x][y] = 1 << RND(4);
4304 else if (move_pattern == MV_HORIZONTAL)
4305 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
4306 else if (move_pattern == MV_VERTICAL)
4307 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
4308 else if (move_pattern & MV_ANY_DIRECTION)
4309 MovDir[x][y] = element_info[element].move_pattern;
4310 else if (move_pattern == MV_ALONG_LEFT_SIDE ||
4311 move_pattern == MV_ALONG_RIGHT_SIDE)
4313 /* use random direction as default start direction */
4314 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
4315 MovDir[x][y] = 1 << RND(4);
4317 for (i = 0; i < NUM_DIRECTIONS; i++)
4319 int x1 = x + xy[i][0];
4320 int y1 = y + xy[i][1];
4322 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4324 if (move_pattern == MV_ALONG_RIGHT_SIDE)
4325 MovDir[x][y] = direction[0][i];
4327 MovDir[x][y] = direction[1][i];
4336 MovDir[x][y] = 1 << RND(4);
4338 if (element != EL_BUG &&
4339 element != EL_SPACESHIP &&
4340 element != EL_BD_BUTTERFLY &&
4341 element != EL_BD_FIREFLY)
4344 for (i = 0; i < NUM_DIRECTIONS; i++)
4346 int x1 = x + xy[i][0];
4347 int y1 = y + xy[i][1];
4349 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4351 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
4353 MovDir[x][y] = direction[0][i];
4356 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
4357 element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4359 MovDir[x][y] = direction[1][i];
4368 GfxDir[x][y] = MovDir[x][y];
4371 void InitAmoebaNr(int x, int y)
4374 int group_nr = AmoebeNachbarNr(x, y);
4378 for (i = 1; i < MAX_NUM_AMOEBA; i++)
4380 if (AmoebaCnt[i] == 0)
4388 AmoebaNr[x][y] = group_nr;
4389 AmoebaCnt[group_nr]++;
4390 AmoebaCnt2[group_nr]++;
4393 static void PlayerWins(struct PlayerInfo *player)
4395 player->LevelSolved = TRUE;
4396 player->GameOver = TRUE;
4398 player->score_final = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
4399 level.native_em_level->lev->score : player->score);
4401 player->LevelSolved_CountingTime = (level.time == 0 ? TimePlayed : TimeLeft);
4402 player->LevelSolved_CountingScore = player->score_final;
4407 static int time, time_final;
4408 static int score, score_final;
4409 static int game_over_delay_1 = 0;
4410 static int game_over_delay_2 = 0;
4411 int game_over_delay_value_1 = 50;
4412 int game_over_delay_value_2 = 50;
4414 if (!local_player->LevelSolved_GameWon)
4418 /* do not start end game actions before the player stops moving (to exit) */
4419 if (local_player->MovPos)
4422 local_player->LevelSolved_GameWon = TRUE;
4423 local_player->LevelSolved_SaveTape = tape.recording;
4424 local_player->LevelSolved_SaveScore = !tape.playing;
4426 if (tape.auto_play) /* tape might already be stopped here */
4427 tape.auto_play_level_solved = TRUE;
4433 game_over_delay_1 = game_over_delay_value_1;
4434 game_over_delay_2 = game_over_delay_value_2;
4436 time = time_final = (level.time == 0 ? TimePlayed : TimeLeft);
4437 score = score_final = local_player->score_final;
4442 score_final += TimeLeft * level.score[SC_TIME_BONUS];
4444 else if (level.time == 0 && TimePlayed < 999)
4447 score_final += (999 - TimePlayed) * level.score[SC_TIME_BONUS];
4450 local_player->score_final = score_final;
4452 if (level_editor_test_game)
4455 score = score_final;
4458 local_player->LevelSolved_CountingTime = time;
4459 local_player->LevelSolved_CountingScore = score;
4461 game_panel_controls[GAME_PANEL_TIME].value = time;
4462 game_panel_controls[GAME_PANEL_SCORE].value = score;
4464 DisplayGameControlValues();
4466 DrawGameValue_Time(time);
4467 DrawGameValue_Score(score);
4471 if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
4473 if (ExitX >= 0 && ExitY >= 0) /* local player has left the level */
4475 /* close exit door after last player */
4476 if ((AllPlayersGone &&
4477 (Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
4478 Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN ||
4479 Feld[ExitX][ExitY] == EL_STEEL_EXIT_OPEN)) ||
4480 Feld[ExitX][ExitY] == EL_EM_EXIT_OPEN ||
4481 Feld[ExitX][ExitY] == EL_EM_STEEL_EXIT_OPEN)
4483 int element = Feld[ExitX][ExitY];
4486 if (element == EL_EM_EXIT_OPEN ||
4487 element == EL_EM_STEEL_EXIT_OPEN)
4494 Feld[ExitX][ExitY] =
4495 (element == EL_EXIT_OPEN ? EL_EXIT_CLOSING :
4496 element == EL_EM_EXIT_OPEN ? EL_EM_EXIT_CLOSING :
4497 element == EL_SP_EXIT_OPEN ? EL_SP_EXIT_CLOSING:
4498 element == EL_STEEL_EXIT_OPEN ? EL_STEEL_EXIT_CLOSING:
4499 EL_EM_STEEL_EXIT_CLOSING);
4501 PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING);
4505 /* player disappears */
4506 DrawLevelField(ExitX, ExitY);
4509 for (i = 0; i < MAX_PLAYERS; i++)
4511 struct PlayerInfo *player = &stored_player[i];
4513 if (player->present)
4515 RemovePlayer(player);
4517 /* player disappears */
4518 DrawLevelField(player->jx, player->jy);
4523 PlaySound(SND_GAME_WINNING);
4526 if (game_over_delay_1 > 0)
4528 game_over_delay_1--;
4533 if (time != time_final)
4535 int time_to_go = ABS(time_final - time);
4536 int time_count_dir = (time < time_final ? +1 : -1);
4537 int time_count_steps = (time_to_go > 100 && time_to_go % 10 == 0 ? 10 : 1);
4539 time += time_count_steps * time_count_dir;
4540 score += time_count_steps * level.score[SC_TIME_BONUS];
4543 local_player->LevelSolved_CountingTime = time;
4544 local_player->LevelSolved_CountingScore = score;
4546 game_panel_controls[GAME_PANEL_TIME].value = time;
4547 game_panel_controls[GAME_PANEL_SCORE].value = score;
4549 DisplayGameControlValues();
4551 DrawGameValue_Time(time);
4552 DrawGameValue_Score(score);
4555 if (time == time_final)
4556 StopSound(SND_GAME_LEVELTIME_BONUS);
4557 else if (setup.sound_loops)
4558 PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4560 PlaySound(SND_GAME_LEVELTIME_BONUS);
4565 local_player->LevelSolved_PanelOff = TRUE;
4567 if (game_over_delay_2 > 0)
4569 game_over_delay_2--;
4582 boolean raise_level = FALSE;
4584 local_player->LevelSolved_GameEnd = TRUE;
4586 CloseDoor(DOOR_CLOSE_1);
4588 if (local_player->LevelSolved_SaveTape)
4595 SaveTapeChecked(tape.level_nr); /* ask to save tape */
4597 SaveTape(tape.level_nr); /* ask to save tape */
4601 if (level_editor_test_game)
4603 game_status = GAME_MODE_MAIN;
4606 DrawAndFadeInMainMenu(REDRAW_FIELD);
4614 if (!local_player->LevelSolved_SaveScore)
4617 FadeOut(REDRAW_FIELD);
4620 game_status = GAME_MODE_MAIN;
4622 DrawAndFadeInMainMenu(REDRAW_FIELD);
4627 if (level_nr == leveldir_current->handicap_level)
4629 leveldir_current->handicap_level++;
4630 SaveLevelSetup_SeriesInfo();
4633 if (level_nr < leveldir_current->last_level)
4634 raise_level = TRUE; /* advance to next level */
4636 if ((hi_pos = NewHiScore()) >= 0)
4638 game_status = GAME_MODE_SCORES;
4640 DrawHallOfFame(hi_pos);
4651 FadeOut(REDRAW_FIELD);
4654 game_status = GAME_MODE_MAIN;
4662 DrawAndFadeInMainMenu(REDRAW_FIELD);
4671 LoadScore(level_nr);
4673 if (strEqual(setup.player_name, EMPTY_PLAYER_NAME) ||
4674 local_player->score_final < highscore[MAX_SCORE_ENTRIES - 1].Score)
4677 for (k = 0; k < MAX_SCORE_ENTRIES; k++)
4679 if (local_player->score_final > highscore[k].Score)
4681 /* player has made it to the hall of fame */
4683 if (k < MAX_SCORE_ENTRIES - 1)
4685 int m = MAX_SCORE_ENTRIES - 1;
4688 for (l = k; l < MAX_SCORE_ENTRIES; l++)
4689 if (strEqual(setup.player_name, highscore[l].Name))
4691 if (m == k) /* player's new highscore overwrites his old one */
4695 for (l = m; l > k; l--)
4697 strcpy(highscore[l].Name, highscore[l - 1].Name);
4698 highscore[l].Score = highscore[l - 1].Score;
4705 strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
4706 highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
4707 highscore[k].Score = local_player->score_final;
4713 else if (!strncmp(setup.player_name, highscore[k].Name,
4714 MAX_PLAYER_NAME_LEN))
4715 break; /* player already there with a higher score */
4721 SaveScore(level_nr);
4726 inline static int getElementMoveStepsizeExt(int x, int y, int direction)
4728 int element = Feld[x][y];
4729 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
4730 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
4731 int horiz_move = (dx != 0);
4732 int sign = (horiz_move ? dx : dy);
4733 int step = sign * element_info[element].move_stepsize;
4735 /* special values for move stepsize for spring and things on conveyor belt */
4738 if (CAN_FALL(element) &&
4739 y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
4740 step = sign * MOVE_STEPSIZE_NORMAL / 2;
4741 else if (element == EL_SPRING)
4742 step = sign * MOVE_STEPSIZE_NORMAL * 2;
4748 inline static int getElementMoveStepsize(int x, int y)
4750 return getElementMoveStepsizeExt(x, y, MovDir[x][y]);
4753 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
4755 if (player->GfxAction != action || player->GfxDir != dir)
4758 printf("Player frame reset! (%d => %d, %d => %d)\n",
4759 player->GfxAction, action, player->GfxDir, dir);
4762 player->GfxAction = action;
4763 player->GfxDir = dir;
4765 player->StepFrame = 0;
4769 #if USE_GFX_RESET_GFX_ANIMATION
4770 static void ResetGfxFrame(int x, int y, boolean redraw)
4772 int element = Feld[x][y];
4773 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
4774 int last_gfx_frame = GfxFrame[x][y];
4776 if (graphic_info[graphic].anim_global_sync)
4777 GfxFrame[x][y] = FrameCounter;
4778 else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
4779 GfxFrame[x][y] = CustomValue[x][y];
4780 else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
4781 GfxFrame[x][y] = element_info[element].collect_score;
4782 else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
4783 GfxFrame[x][y] = ChangeDelay[x][y];
4785 if (redraw && GfxFrame[x][y] != last_gfx_frame)
4786 DrawLevelGraphicAnimation(x, y, graphic);
4790 static void ResetGfxAnimation(int x, int y)
4792 GfxAction[x][y] = ACTION_DEFAULT;
4793 GfxDir[x][y] = MovDir[x][y];
4796 #if USE_GFX_RESET_GFX_ANIMATION
4797 ResetGfxFrame(x, y, FALSE);
4801 static void ResetRandomAnimationValue(int x, int y)
4803 GfxRandom[x][y] = INIT_GFX_RANDOM();
4806 void InitMovingField(int x, int y, int direction)
4808 int element = Feld[x][y];
4809 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
4810 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
4813 boolean is_moving_before, is_moving_after;
4815 boolean continues_moving = (WasJustMoving[x][y] && direction == MovDir[x][y]);
4818 /* check if element was/is moving or being moved before/after mode change */
4821 is_moving_before = (WasJustMoving[x][y] != 0);
4823 /* (!!! this does not work -- WasJustMoving is NOT a boolean value !!!) */
4824 is_moving_before = WasJustMoving[x][y];
4827 is_moving_before = (getElementMoveStepsizeExt(x, y, MovDir[x][y]) != 0);
4829 is_moving_after = (getElementMoveStepsizeExt(x, y, direction) != 0);
4831 /* reset animation only for moving elements which change direction of moving
4832 or which just started or stopped moving
4833 (else CEs with property "can move" / "not moving" are reset each frame) */
4834 #if USE_GFX_RESET_ONLY_WHEN_MOVING
4836 if (is_moving_before != is_moving_after ||
4837 direction != MovDir[x][y])
4838 ResetGfxAnimation(x, y);
4840 if ((is_moving_before || is_moving_after) && !continues_moving)
4841 ResetGfxAnimation(x, y);
4844 if (!continues_moving)
4845 ResetGfxAnimation(x, y);
4848 MovDir[x][y] = direction;
4849 GfxDir[x][y] = direction;
4851 #if USE_GFX_RESET_ONLY_WHEN_MOVING
4852 GfxAction[x][y] = (!is_moving_after ? ACTION_WAITING :
4853 direction == MV_DOWN && CAN_FALL(element) ?
4854 ACTION_FALLING : ACTION_MOVING);
4856 GfxAction[x][y] = (direction == MV_DOWN && CAN_FALL(element) ?
4857 ACTION_FALLING : ACTION_MOVING);
4860 /* this is needed for CEs with property "can move" / "not moving" */
4862 if (is_moving_after)
4864 if (Feld[newx][newy] == EL_EMPTY)
4865 Feld[newx][newy] = EL_BLOCKED;
4867 MovDir[newx][newy] = MovDir[x][y];
4869 #if USE_NEW_CUSTOM_VALUE
4870 CustomValue[newx][newy] = CustomValue[x][y];
4873 GfxFrame[newx][newy] = GfxFrame[x][y];
4874 GfxRandom[newx][newy] = GfxRandom[x][y];
4875 GfxAction[newx][newy] = GfxAction[x][y];
4876 GfxDir[newx][newy] = GfxDir[x][y];
4880 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
4882 int direction = MovDir[x][y];
4883 int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0);
4884 int newy = y + (direction & MV_UP ? -1 : direction & MV_DOWN ? +1 : 0);
4890 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
4892 int oldx = x, oldy = y;
4893 int direction = MovDir[x][y];
4895 if (direction == MV_LEFT)
4897 else if (direction == MV_RIGHT)
4899 else if (direction == MV_UP)
4901 else if (direction == MV_DOWN)
4904 *comes_from_x = oldx;
4905 *comes_from_y = oldy;
4908 int MovingOrBlocked2Element(int x, int y)
4910 int element = Feld[x][y];
4912 if (element == EL_BLOCKED)
4916 Blocked2Moving(x, y, &oldx, &oldy);
4917 return Feld[oldx][oldy];
4923 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
4925 /* like MovingOrBlocked2Element(), but if element is moving
4926 and (x,y) is the field the moving element is just leaving,
4927 return EL_BLOCKED instead of the element value */
4928 int element = Feld[x][y];
4930 if (IS_MOVING(x, y))
4932 if (element == EL_BLOCKED)
4936 Blocked2Moving(x, y, &oldx, &oldy);
4937 return Feld[oldx][oldy];
4946 static void RemoveField(int x, int y)
4948 Feld[x][y] = EL_EMPTY;
4954 #if USE_NEW_CUSTOM_VALUE
4955 CustomValue[x][y] = 0;
4959 ChangeDelay[x][y] = 0;
4960 ChangePage[x][y] = -1;
4961 Pushed[x][y] = FALSE;
4964 ExplodeField[x][y] = EX_TYPE_NONE;
4967 GfxElement[x][y] = EL_UNDEFINED;
4968 GfxAction[x][y] = ACTION_DEFAULT;
4969 GfxDir[x][y] = MV_NONE;
4972 void RemoveMovingField(int x, int y)
4974 int oldx = x, oldy = y, newx = x, newy = y;
4975 int element = Feld[x][y];
4976 int next_element = EL_UNDEFINED;
4978 if (element != EL_BLOCKED && !IS_MOVING(x, y))
4981 if (IS_MOVING(x, y))
4983 Moving2Blocked(x, y, &newx, &newy);
4985 if (Feld[newx][newy] != EL_BLOCKED)
4987 /* element is moving, but target field is not free (blocked), but
4988 already occupied by something different (example: acid pool);
4989 in this case, only remove the moving field, but not the target */
4991 RemoveField(oldx, oldy);
4993 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
4995 DrawLevelField(oldx, oldy);
5000 else if (element == EL_BLOCKED)
5002 Blocked2Moving(x, y, &oldx, &oldy);
5003 if (!IS_MOVING(oldx, oldy))
5007 if (element == EL_BLOCKED &&
5008 (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
5009 Feld[oldx][oldy] == EL_QUICKSAND_FAST_EMPTYING ||
5010 Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
5011 Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
5012 Feld[oldx][oldy] == EL_DC_MAGIC_WALL_EMPTYING ||
5013 Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
5014 next_element = get_next_element(Feld[oldx][oldy]);
5016 RemoveField(oldx, oldy);
5017 RemoveField(newx, newy);
5019 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5021 if (next_element != EL_UNDEFINED)
5022 Feld[oldx][oldy] = next_element;
5024 DrawLevelField(oldx, oldy);
5025 DrawLevelField(newx, newy);
5028 void DrawDynamite(int x, int y)
5030 int sx = SCREENX(x), sy = SCREENY(y);
5031 int graphic = el2img(Feld[x][y]);
5034 if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
5037 if (IS_WALKABLE_INSIDE(Back[x][y]))
5041 DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
5042 else if (Store[x][y])
5043 DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
5045 frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5047 if (Back[x][y] || Store[x][y])
5048 DrawGraphicThruMask(sx, sy, graphic, frame);
5050 DrawGraphic(sx, sy, graphic, frame);
5053 void CheckDynamite(int x, int y)
5055 if (MovDelay[x][y] != 0) /* dynamite is still waiting to explode */
5059 if (MovDelay[x][y] != 0)
5062 PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5068 StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5073 static void setMinimalPlayerBoundaries(int *sx1, int *sy1, int *sx2, int *sy2)
5075 boolean num_checked_players = 0;
5078 for (i = 0; i < MAX_PLAYERS; i++)
5080 if (stored_player[i].active)
5082 int sx = stored_player[i].jx;
5083 int sy = stored_player[i].jy;
5085 if (num_checked_players == 0)
5092 *sx1 = MIN(*sx1, sx);
5093 *sy1 = MIN(*sy1, sy);
5094 *sx2 = MAX(*sx2, sx);
5095 *sy2 = MAX(*sy2, sy);
5098 num_checked_players++;
5103 static boolean checkIfAllPlayersFitToScreen_RND()
5105 int sx1 = 0, sy1 = 0, sx2 = 0, sy2 = 0;
5107 setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5109 return (sx2 - sx1 < SCR_FIELDX &&
5110 sy2 - sy1 < SCR_FIELDY);
5113 static void setScreenCenteredToAllPlayers(int *sx, int *sy)
5115 int sx1 = scroll_x, sy1 = scroll_y, sx2 = scroll_x, sy2 = scroll_y;
5117 setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5119 *sx = (sx1 + sx2) / 2;
5120 *sy = (sy1 + sy2) / 2;
5123 void DrawRelocateScreen(int old_x, int old_y, int x, int y, int move_dir,
5124 boolean center_screen, boolean quick_relocation)
5126 boolean ffwd_delay = (tape.playing && tape.fast_forward);
5127 boolean no_delay = (tape.warp_forward);
5128 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5129 int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5131 if (quick_relocation)
5133 int offset = game.scroll_delay_value;
5135 if (!IN_VIS_FIELD(SCREENX(x), SCREENY(y)) || center_screen)
5137 if (!level.shifted_relocation || center_screen)
5139 /* quick relocation (without scrolling), with centering of screen */
5141 scroll_x = (x < SBX_Left + MIDPOSX ? SBX_Left :
5142 x > SBX_Right + MIDPOSX ? SBX_Right :
5145 scroll_y = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
5146 y > SBY_Lower + MIDPOSY ? SBY_Lower :
5151 /* quick relocation (without scrolling), but do not center screen */
5153 int center_scroll_x = (old_x < SBX_Left + MIDPOSX ? SBX_Left :
5154 old_x > SBX_Right + MIDPOSX ? SBX_Right :
5157 int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5158 old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5161 int offset_x = x + (scroll_x - center_scroll_x);
5162 int offset_y = y + (scroll_y - center_scroll_y);
5164 scroll_x = (offset_x < SBX_Left + MIDPOSX ? SBX_Left :
5165 offset_x > SBX_Right + MIDPOSX ? SBX_Right :
5166 offset_x - MIDPOSX);
5168 scroll_y = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5169 offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5170 offset_y - MIDPOSY);
5175 /* quick relocation (without scrolling), inside visible screen area */
5177 if ((move_dir == MV_LEFT && scroll_x > x - MIDPOSX + offset) ||
5178 (move_dir == MV_RIGHT && scroll_x < x - MIDPOSX - offset))
5179 scroll_x = x - MIDPOSX + (scroll_x < x - MIDPOSX ? -offset : +offset);
5181 if ((move_dir == MV_UP && scroll_y > y - MIDPOSY + offset) ||
5182 (move_dir == MV_DOWN && scroll_y < y - MIDPOSY - offset))
5183 scroll_y = y - MIDPOSY + (scroll_y < y - MIDPOSY ? -offset : +offset);
5185 /* don't scroll over playfield boundaries */
5186 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
5187 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
5189 /* don't scroll over playfield boundaries */
5190 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
5191 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
5194 RedrawPlayfield(TRUE, 0,0,0,0);
5199 int scroll_xx, scroll_yy;
5201 if (!level.shifted_relocation || center_screen)
5203 /* visible relocation (with scrolling), with centering of screen */
5205 scroll_xx = (x < SBX_Left + MIDPOSX ? SBX_Left :
5206 x > SBX_Right + MIDPOSX ? SBX_Right :
5209 scroll_yy = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
5210 y > SBY_Lower + MIDPOSY ? SBY_Lower :
5215 /* visible relocation (with scrolling), but do not center screen */
5217 int center_scroll_x = (old_x < SBX_Left + MIDPOSX ? SBX_Left :
5218 old_x > SBX_Right + MIDPOSX ? SBX_Right :
5221 int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5222 old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5225 int offset_x = x + (scroll_x - center_scroll_x);
5226 int offset_y = y + (scroll_y - center_scroll_y);
5228 scroll_xx = (offset_x < SBX_Left + MIDPOSX ? SBX_Left :
5229 offset_x > SBX_Right + MIDPOSX ? SBX_Right :
5230 offset_x - MIDPOSX);
5232 scroll_yy = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5233 offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5234 offset_y - MIDPOSY);
5239 /* visible relocation (with scrolling), with centering of screen */
5241 int scroll_xx = (x < SBX_Left + MIDPOSX ? SBX_Left :
5242 x > SBX_Right + MIDPOSX ? SBX_Right :
5245 int scroll_yy = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
5246 y > SBY_Lower + MIDPOSY ? SBY_Lower :
5250 ScrollScreen(NULL, SCROLL_GO_ON); /* scroll last frame to full tile */
5252 while (scroll_x != scroll_xx || scroll_y != scroll_yy)
5255 int fx = FX, fy = FY;
5257 dx = (scroll_xx < scroll_x ? +1 : scroll_xx > scroll_x ? -1 : 0);
5258 dy = (scroll_yy < scroll_y ? +1 : scroll_yy > scroll_y ? -1 : 0);
5260 if (dx == 0 && dy == 0) /* no scrolling needed at all */
5266 fx += dx * TILEX / 2;
5267 fy += dy * TILEY / 2;
5269 ScrollLevel(dx, dy);
5272 /* scroll in two steps of half tile size to make things smoother */
5273 BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
5275 Delay(wait_delay_value);
5277 /* scroll second step to align at full tile size */
5279 Delay(wait_delay_value);
5284 Delay(wait_delay_value);
5288 void RelocatePlayer(int jx, int jy, int el_player_raw)
5290 int el_player = GET_PLAYER_ELEMENT(el_player_raw);
5291 int player_nr = GET_PLAYER_NR(el_player);
5292 struct PlayerInfo *player = &stored_player[player_nr];
5293 boolean ffwd_delay = (tape.playing && tape.fast_forward);
5294 boolean no_delay = (tape.warp_forward);
5295 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5296 int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5297 int old_jx = player->jx;
5298 int old_jy = player->jy;
5299 int old_element = Feld[old_jx][old_jy];
5300 int element = Feld[jx][jy];
5301 boolean player_relocated = (old_jx != jx || old_jy != jy);
5303 int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
5304 int move_dir_vert = (jy < old_jy ? MV_UP : jy > old_jy ? MV_DOWN : 0);
5305 int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
5306 int enter_side_vert = MV_DIR_OPPOSITE(move_dir_vert);
5307 int leave_side_horiz = move_dir_horiz;
5308 int leave_side_vert = move_dir_vert;
5309 int enter_side = enter_side_horiz | enter_side_vert;
5310 int leave_side = leave_side_horiz | leave_side_vert;
5312 if (player->GameOver) /* do not reanimate dead player */
5315 if (!player_relocated) /* no need to relocate the player */
5318 if (IS_PLAYER(jx, jy)) /* player already placed at new position */
5320 RemoveField(jx, jy); /* temporarily remove newly placed player */
5321 DrawLevelField(jx, jy);
5324 if (player->present)
5326 while (player->MovPos)
5328 ScrollPlayer(player, SCROLL_GO_ON);
5329 ScrollScreen(NULL, SCROLL_GO_ON);
5331 AdvanceFrameAndPlayerCounters(player->index_nr);
5336 Delay(wait_delay_value);
5339 DrawPlayer(player); /* needed here only to cleanup last field */
5340 DrawLevelField(player->jx, player->jy); /* remove player graphic */
5342 player->is_moving = FALSE;
5345 if (IS_CUSTOM_ELEMENT(old_element))
5346 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
5348 player->index_bit, leave_side);
5350 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
5352 player->index_bit, leave_side);
5354 Feld[jx][jy] = el_player;
5355 InitPlayerField(jx, jy, el_player, TRUE);
5357 if (!ELEM_IS_PLAYER(element)) /* player may be set on walkable element */
5359 Feld[jx][jy] = element;
5360 InitField(jx, jy, FALSE);
5363 /* only visually relocate centered player */
5364 DrawRelocateScreen(old_jx, old_jy, player->jx, player->jy, player->MovDir,
5365 FALSE, level.instant_relocation);
5367 TestIfPlayerTouchesBadThing(jx, jy);
5368 TestIfPlayerTouchesCustomElement(jx, jy);
5370 if (IS_CUSTOM_ELEMENT(element))
5371 CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
5372 player->index_bit, enter_side);
5374 CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
5375 player->index_bit, enter_side);
5378 void Explode(int ex, int ey, int phase, int mode)
5384 /* !!! eliminate this variable !!! */
5385 int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
5387 if (game.explosions_delayed)
5389 ExplodeField[ex][ey] = mode;
5393 if (phase == EX_PHASE_START) /* initialize 'Store[][]' field */
5395 int center_element = Feld[ex][ey];
5396 int artwork_element, explosion_element; /* set these values later */
5399 /* --- This is only really needed (and now handled) in "Impact()". --- */
5400 /* do not explode moving elements that left the explode field in time */
5401 if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
5402 center_element == EL_EMPTY &&
5403 (mode == EX_TYPE_NORMAL || mode == EX_TYPE_CENTER))
5408 /* !!! at this place, the center element may be EL_BLOCKED !!! */
5409 if (mode == EX_TYPE_NORMAL ||
5410 mode == EX_TYPE_CENTER ||
5411 mode == EX_TYPE_CROSS)
5412 PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
5415 /* remove things displayed in background while burning dynamite */
5416 if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
5419 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
5421 /* put moving element to center field (and let it explode there) */
5422 center_element = MovingOrBlocked2Element(ex, ey);
5423 RemoveMovingField(ex, ey);
5424 Feld[ex][ey] = center_element;
5427 /* now "center_element" is finally determined -- set related values now */
5428 artwork_element = center_element; /* for custom player artwork */
5429 explosion_element = center_element; /* for custom player artwork */
5431 if (IS_PLAYER(ex, ey))
5433 int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
5435 artwork_element = stored_player[player_nr].artwork_element;
5437 if (level.use_explosion_element[player_nr])
5439 explosion_element = level.explosion_element[player_nr];
5440 artwork_element = explosion_element;
5445 if (mode == EX_TYPE_NORMAL ||
5446 mode == EX_TYPE_CENTER ||
5447 mode == EX_TYPE_CROSS)
5448 PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
5451 last_phase = element_info[explosion_element].explosion_delay + 1;
5453 for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
5455 int xx = x - ex + 1;
5456 int yy = y - ey + 1;
5459 if (!IN_LEV_FIELD(x, y) ||
5460 (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
5461 (mode == EX_TYPE_CROSS && (x != ex && y != ey)))
5464 element = Feld[x][y];
5466 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
5468 element = MovingOrBlocked2Element(x, y);
5470 if (!IS_EXPLOSION_PROOF(element))
5471 RemoveMovingField(x, y);
5474 /* indestructible elements can only explode in center (but not flames) */
5475 if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
5476 mode == EX_TYPE_BORDER)) ||
5477 element == EL_FLAMES)
5480 /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
5481 behaviour, for example when touching a yamyam that explodes to rocks
5482 with active deadly shield, a rock is created under the player !!! */
5483 /* (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8) */
5485 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
5486 (game.engine_version < VERSION_IDENT(3,1,0,0) ||
5487 (x == ex && y == ey && mode != EX_TYPE_BORDER)))
5489 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
5492 if (IS_ACTIVE_BOMB(element))
5494 /* re-activate things under the bomb like gate or penguin */
5495 Feld[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
5502 /* save walkable background elements while explosion on same tile */
5503 if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
5504 (x != ex || y != ey || mode == EX_TYPE_BORDER))
5505 Back[x][y] = element;
5507 /* ignite explodable elements reached by other explosion */
5508 if (element == EL_EXPLOSION)
5509 element = Store2[x][y];
5511 if (AmoebaNr[x][y] &&
5512 (element == EL_AMOEBA_FULL ||
5513 element == EL_BD_AMOEBA ||
5514 element == EL_AMOEBA_GROWING))
5516 AmoebaCnt[AmoebaNr[x][y]]--;
5517 AmoebaCnt2[AmoebaNr[x][y]]--;
5522 if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
5524 int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
5526 Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
5528 if (PLAYERINFO(ex, ey)->use_murphy)
5529 Store[x][y] = EL_EMPTY;
5532 /* !!! check this case -- currently needed for rnd_rado_negundo_v,
5533 !!! levels 015 018 019 020 021 022 023 026 027 028 !!! */
5534 else if (ELEM_IS_PLAYER(center_element))
5535 Store[x][y] = EL_EMPTY;
5536 else if (center_element == EL_YAMYAM)
5537 Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
5538 else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
5539 Store[x][y] = element_info[center_element].content.e[xx][yy];
5541 /* needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
5542 (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
5543 otherwise) -- FIX THIS !!! */
5544 else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
5545 Store[x][y] = element_info[element].content.e[1][1];
5547 else if (!CAN_EXPLODE(element))
5548 Store[x][y] = element_info[element].content.e[1][1];
5551 Store[x][y] = EL_EMPTY;
5553 if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
5554 center_element == EL_AMOEBA_TO_DIAMOND)
5555 Store2[x][y] = element;
5557 Feld[x][y] = EL_EXPLOSION;
5558 GfxElement[x][y] = artwork_element;
5560 ExplodePhase[x][y] = 1;
5561 ExplodeDelay[x][y] = last_phase;
5566 if (center_element == EL_YAMYAM)
5567 game.yamyam_content_nr =
5568 (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
5580 GfxFrame[x][y] = 0; /* restart explosion animation */
5582 last_phase = ExplodeDelay[x][y];
5584 ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
5588 /* activate this even in non-DEBUG version until cause for crash in
5589 getGraphicAnimationFrame() (see below) is found and eliminated */
5595 /* this can happen if the player leaves an explosion just in time */
5596 if (GfxElement[x][y] == EL_UNDEFINED)
5597 GfxElement[x][y] = EL_EMPTY;
5599 if (GfxElement[x][y] == EL_UNDEFINED)
5602 printf("Explode(): x = %d, y = %d: GfxElement == EL_UNDEFINED\n", x, y);
5603 printf("Explode(): This should never happen!\n");
5606 GfxElement[x][y] = EL_EMPTY;
5612 border_element = Store2[x][y];
5613 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5614 border_element = StorePlayer[x][y];
5616 if (phase == element_info[border_element].ignition_delay ||
5617 phase == last_phase)
5619 boolean border_explosion = FALSE;
5621 if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
5622 !PLAYER_EXPLOSION_PROTECTED(x, y))
5624 KillPlayerUnlessExplosionProtected(x, y);
5625 border_explosion = TRUE;
5627 else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
5629 Feld[x][y] = Store2[x][y];
5632 border_explosion = TRUE;
5634 else if (border_element == EL_AMOEBA_TO_DIAMOND)
5636 AmoebeUmwandeln(x, y);
5638 border_explosion = TRUE;
5641 /* if an element just explodes due to another explosion (chain-reaction),
5642 do not immediately end the new explosion when it was the last frame of
5643 the explosion (as it would be done in the following "if"-statement!) */
5644 if (border_explosion && phase == last_phase)
5648 if (phase == last_phase)
5652 element = Feld[x][y] = Store[x][y];
5653 Store[x][y] = Store2[x][y] = 0;
5654 GfxElement[x][y] = EL_UNDEFINED;
5656 /* player can escape from explosions and might therefore be still alive */
5657 if (element >= EL_PLAYER_IS_EXPLODING_1 &&
5658 element <= EL_PLAYER_IS_EXPLODING_4)
5660 int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
5661 int explosion_element = EL_PLAYER_1 + player_nr;
5662 int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
5663 int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
5665 if (level.use_explosion_element[player_nr])
5666 explosion_element = level.explosion_element[player_nr];
5668 Feld[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
5669 element_info[explosion_element].content.e[xx][yy]);
5672 /* restore probably existing indestructible background element */
5673 if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
5674 element = Feld[x][y] = Back[x][y];
5677 MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
5678 GfxDir[x][y] = MV_NONE;
5679 ChangeDelay[x][y] = 0;
5680 ChangePage[x][y] = -1;
5682 #if USE_NEW_CUSTOM_VALUE
5683 CustomValue[x][y] = 0;
5686 InitField_WithBug2(x, y, FALSE);
5688 DrawLevelField(x, y);
5690 TestIfElementTouchesCustomElement(x, y);
5692 if (GFX_CRUMBLED(element))
5693 DrawLevelFieldCrumbledSandNeighbours(x, y);
5695 if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
5696 StorePlayer[x][y] = 0;
5698 if (ELEM_IS_PLAYER(element))
5699 RelocatePlayer(x, y, element);
5701 else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
5703 int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
5704 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5707 DrawLevelFieldCrumbledSand(x, y);
5709 if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
5711 DrawLevelElement(x, y, Back[x][y]);
5712 DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
5714 else if (IS_WALKABLE_UNDER(Back[x][y]))
5716 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5717 DrawLevelElementThruMask(x, y, Back[x][y]);
5719 else if (!IS_WALKABLE_INSIDE(Back[x][y]))
5720 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5724 void DynaExplode(int ex, int ey)
5727 int dynabomb_element = Feld[ex][ey];
5728 int dynabomb_size = 1;
5729 boolean dynabomb_xl = FALSE;
5730 struct PlayerInfo *player;
5731 static int xy[4][2] =
5739 if (IS_ACTIVE_BOMB(dynabomb_element))
5741 player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
5742 dynabomb_size = player->dynabomb_size;
5743 dynabomb_xl = player->dynabomb_xl;
5744 player->dynabombs_left++;
5747 Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
5749 for (i = 0; i < NUM_DIRECTIONS; i++)
5751 for (j = 1; j <= dynabomb_size; j++)
5753 int x = ex + j * xy[i][0];
5754 int y = ey + j * xy[i][1];
5757 if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
5760 element = Feld[x][y];
5762 /* do not restart explosions of fields with active bombs */
5763 if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
5766 Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
5768 if (element != EL_EMPTY && element != EL_EXPLOSION &&
5769 !IS_DIGGABLE(element) && !dynabomb_xl)
5775 void Bang(int x, int y)
5777 int element = MovingOrBlocked2Element(x, y);
5778 int explosion_type = EX_TYPE_NORMAL;
5780 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5782 struct PlayerInfo *player = PLAYERINFO(x, y);
5784 element = Feld[x][y] = (player->use_murphy ? EL_SP_MURPHY :
5785 player->element_nr);
5787 if (level.use_explosion_element[player->index_nr])
5789 int explosion_element = level.explosion_element[player->index_nr];
5791 if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
5792 explosion_type = EX_TYPE_CROSS;
5793 else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
5794 explosion_type = EX_TYPE_CENTER;
5802 case EL_BD_BUTTERFLY:
5805 case EL_DARK_YAMYAM:
5809 RaiseScoreElement(element);
5812 case EL_DYNABOMB_PLAYER_1_ACTIVE:
5813 case EL_DYNABOMB_PLAYER_2_ACTIVE:
5814 case EL_DYNABOMB_PLAYER_3_ACTIVE:
5815 case EL_DYNABOMB_PLAYER_4_ACTIVE:
5816 case EL_DYNABOMB_INCREASE_NUMBER:
5817 case EL_DYNABOMB_INCREASE_SIZE:
5818 case EL_DYNABOMB_INCREASE_POWER:
5819 explosion_type = EX_TYPE_DYNA;
5822 case EL_DC_LANDMINE:
5824 case EL_EM_EXIT_OPEN:
5825 case EL_EM_STEEL_EXIT_OPEN:
5827 explosion_type = EX_TYPE_CENTER;
5832 case EL_LAMP_ACTIVE:
5833 case EL_AMOEBA_TO_DIAMOND:
5834 if (!IS_PLAYER(x, y)) /* penguin and player may be at same field */
5835 explosion_type = EX_TYPE_CENTER;
5839 if (element_info[element].explosion_type == EXPLODES_CROSS)
5840 explosion_type = EX_TYPE_CROSS;
5841 else if (element_info[element].explosion_type == EXPLODES_1X1)
5842 explosion_type = EX_TYPE_CENTER;
5846 if (explosion_type == EX_TYPE_DYNA)
5849 Explode(x, y, EX_PHASE_START, explosion_type);
5851 CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
5854 void SplashAcid(int x, int y)
5856 if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
5857 (!IN_LEV_FIELD(x - 1, y - 2) ||
5858 !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
5859 Feld[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
5861 if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
5862 (!IN_LEV_FIELD(x + 1, y - 2) ||
5863 !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
5864 Feld[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
5866 PlayLevelSound(x, y, SND_ACID_SPLASHING);
5869 static void InitBeltMovement()
5871 static int belt_base_element[4] =
5873 EL_CONVEYOR_BELT_1_LEFT,
5874 EL_CONVEYOR_BELT_2_LEFT,
5875 EL_CONVEYOR_BELT_3_LEFT,
5876 EL_CONVEYOR_BELT_4_LEFT
5878 static int belt_base_active_element[4] =
5880 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
5881 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
5882 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
5883 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
5888 /* set frame order for belt animation graphic according to belt direction */
5889 for (i = 0; i < NUM_BELTS; i++)
5893 for (j = 0; j < NUM_BELT_PARTS; j++)
5895 int element = belt_base_active_element[belt_nr] + j;
5896 int graphic_1 = el2img(element);
5897 int graphic_2 = el2panelimg(element);
5899 if (game.belt_dir[i] == MV_LEFT)
5901 graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
5902 graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
5906 graphic_info[graphic_1].anim_mode |= ANIM_REVERSE;
5907 graphic_info[graphic_2].anim_mode |= ANIM_REVERSE;
5912 SCAN_PLAYFIELD(x, y)
5914 int element = Feld[x][y];
5916 for (i = 0; i < NUM_BELTS; i++)
5918 if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
5920 int e_belt_nr = getBeltNrFromBeltElement(element);
5923 if (e_belt_nr == belt_nr)
5925 int belt_part = Feld[x][y] - belt_base_element[belt_nr];
5927 Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
5934 static void ToggleBeltSwitch(int x, int y)
5936 static int belt_base_element[4] =
5938 EL_CONVEYOR_BELT_1_LEFT,
5939 EL_CONVEYOR_BELT_2_LEFT,
5940 EL_CONVEYOR_BELT_3_LEFT,
5941 EL_CONVEYOR_BELT_4_LEFT
5943 static int belt_base_active_element[4] =
5945 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
5946 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
5947 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
5948 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
5950 static int belt_base_switch_element[4] =
5952 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
5953 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
5954 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
5955 EL_CONVEYOR_BELT_4_SWITCH_LEFT
5957 static int belt_move_dir[4] =
5965 int element = Feld[x][y];
5966 int belt_nr = getBeltNrFromBeltSwitchElement(element);
5967 int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
5968 int belt_dir = belt_move_dir[belt_dir_nr];
5971 if (!IS_BELT_SWITCH(element))
5974 game.belt_dir_nr[belt_nr] = belt_dir_nr;
5975 game.belt_dir[belt_nr] = belt_dir;
5977 if (belt_dir_nr == 3)
5980 /* set frame order for belt animation graphic according to belt direction */
5981 for (i = 0; i < NUM_BELT_PARTS; i++)
5983 int element = belt_base_active_element[belt_nr] + i;
5984 int graphic_1 = el2img(element);
5985 int graphic_2 = el2panelimg(element);
5987 if (belt_dir == MV_LEFT)
5989 graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
5990 graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
5994 graphic_info[graphic_1].anim_mode |= ANIM_REVERSE;
5995 graphic_info[graphic_2].anim_mode |= ANIM_REVERSE;
5999 SCAN_PLAYFIELD(xx, yy)
6001 int element = Feld[xx][yy];
6003 if (IS_BELT_SWITCH(element))
6005 int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
6007 if (e_belt_nr == belt_nr)
6009 Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
6010 DrawLevelField(xx, yy);
6013 else if (IS_BELT(element) && belt_dir != MV_NONE)
6015 int e_belt_nr = getBeltNrFromBeltElement(element);
6017 if (e_belt_nr == belt_nr)
6019 int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
6021 Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
6022 DrawLevelField(xx, yy);
6025 else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
6027 int e_belt_nr = getBeltNrFromBeltActiveElement(element);
6029 if (e_belt_nr == belt_nr)
6031 int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
6033 Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
6034 DrawLevelField(xx, yy);
6040 static void ToggleSwitchgateSwitch(int x, int y)
6044 game.switchgate_pos = !game.switchgate_pos;
6046 SCAN_PLAYFIELD(xx, yy)
6048 int element = Feld[xx][yy];
6050 #if !USE_BOTH_SWITCHGATE_SWITCHES
6051 if (element == EL_SWITCHGATE_SWITCH_UP ||
6052 element == EL_SWITCHGATE_SWITCH_DOWN)
6054 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
6055 DrawLevelField(xx, yy);
6057 else if (element == EL_DC_SWITCHGATE_SWITCH_UP ||
6058 element == EL_DC_SWITCHGATE_SWITCH_DOWN)
6060 Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
6061 DrawLevelField(xx, yy);
6064 if (element == EL_SWITCHGATE_SWITCH_UP)
6066 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_DOWN;
6067 DrawLevelField(xx, yy);
6069 else if (element == EL_SWITCHGATE_SWITCH_DOWN)
6071 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP;
6072 DrawLevelField(xx, yy);
6074 else if (element == EL_DC_SWITCHGATE_SWITCH_UP)
6076 Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_DOWN;
6077 DrawLevelField(xx, yy);
6079 else if (element == EL_DC_SWITCHGATE_SWITCH_DOWN)
6081 Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP;
6082 DrawLevelField(xx, yy);
6085 else if (element == EL_SWITCHGATE_OPEN ||
6086 element == EL_SWITCHGATE_OPENING)
6088 Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
6090 PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
6092 else if (element == EL_SWITCHGATE_CLOSED ||
6093 element == EL_SWITCHGATE_CLOSING)
6095 Feld[xx][yy] = EL_SWITCHGATE_OPENING;
6097 PlayLevelSoundAction(xx, yy, ACTION_OPENING);
6102 static int getInvisibleActiveFromInvisibleElement(int element)
6104 return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
6105 element == EL_INVISIBLE_WALL ? EL_INVISIBLE_WALL_ACTIVE :
6106 element == EL_INVISIBLE_SAND ? EL_INVISIBLE_SAND_ACTIVE :
6110 static int getInvisibleFromInvisibleActiveElement(int element)
6112 return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
6113 element == EL_INVISIBLE_WALL_ACTIVE ? EL_INVISIBLE_WALL :
6114 element == EL_INVISIBLE_SAND_ACTIVE ? EL_INVISIBLE_SAND :
6118 static void RedrawAllLightSwitchesAndInvisibleElements()
6122 SCAN_PLAYFIELD(x, y)
6124 int element = Feld[x][y];
6126 if (element == EL_LIGHT_SWITCH &&
6127 game.light_time_left > 0)
6129 Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
6130 DrawLevelField(x, y);
6132 else if (element == EL_LIGHT_SWITCH_ACTIVE &&
6133 game.light_time_left == 0)
6135 Feld[x][y] = EL_LIGHT_SWITCH;
6136 DrawLevelField(x, y);
6138 else if (element == EL_EMC_DRIPPER &&
6139 game.light_time_left > 0)
6141 Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
6142 DrawLevelField(x, y);
6144 else if (element == EL_EMC_DRIPPER_ACTIVE &&
6145 game.light_time_left == 0)
6147 Feld[x][y] = EL_EMC_DRIPPER;
6148 DrawLevelField(x, y);
6150 else if (element == EL_INVISIBLE_STEELWALL ||
6151 element == EL_INVISIBLE_WALL ||
6152 element == EL_INVISIBLE_SAND)
6154 if (game.light_time_left > 0)
6155 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
6157 DrawLevelField(x, y);
6159 /* uncrumble neighbour fields, if needed */
6160 if (element == EL_INVISIBLE_SAND)
6161 DrawLevelFieldCrumbledSandNeighbours(x, y);
6163 else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6164 element == EL_INVISIBLE_WALL_ACTIVE ||
6165 element == EL_INVISIBLE_SAND_ACTIVE)
6167 if (game.light_time_left == 0)
6168 Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
6170 DrawLevelField(x, y);
6172 /* re-crumble neighbour fields, if needed */
6173 if (element == EL_INVISIBLE_SAND)
6174 DrawLevelFieldCrumbledSandNeighbours(x, y);
6179 static void RedrawAllInvisibleElementsForLenses()
6183 SCAN_PLAYFIELD(x, y)
6185 int element = Feld[x][y];
6187 if (element == EL_EMC_DRIPPER &&
6188 game.lenses_time_left > 0)
6190 Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
6191 DrawLevelField(x, y);
6193 else if (element == EL_EMC_DRIPPER_ACTIVE &&
6194 game.lenses_time_left == 0)
6196 Feld[x][y] = EL_EMC_DRIPPER;
6197 DrawLevelField(x, y);
6199 else if (element == EL_INVISIBLE_STEELWALL ||
6200 element == EL_INVISIBLE_WALL ||
6201 element == EL_INVISIBLE_SAND)
6203 if (game.lenses_time_left > 0)
6204 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
6206 DrawLevelField(x, y);
6208 /* uncrumble neighbour fields, if needed */
6209 if (element == EL_INVISIBLE_SAND)
6210 DrawLevelFieldCrumbledSandNeighbours(x, y);
6212 else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6213 element == EL_INVISIBLE_WALL_ACTIVE ||
6214 element == EL_INVISIBLE_SAND_ACTIVE)
6216 if (game.lenses_time_left == 0)
6217 Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
6219 DrawLevelField(x, y);
6221 /* re-crumble neighbour fields, if needed */
6222 if (element == EL_INVISIBLE_SAND)
6223 DrawLevelFieldCrumbledSandNeighbours(x, y);
6228 static void RedrawAllInvisibleElementsForMagnifier()
6232 SCAN_PLAYFIELD(x, y)
6234 int element = Feld[x][y];
6236 if (element == EL_EMC_FAKE_GRASS &&
6237 game.magnify_time_left > 0)
6239 Feld[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
6240 DrawLevelField(x, y);
6242 else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
6243 game.magnify_time_left == 0)
6245 Feld[x][y] = EL_EMC_FAKE_GRASS;
6246 DrawLevelField(x, y);
6248 else if (IS_GATE_GRAY(element) &&
6249 game.magnify_time_left > 0)
6251 Feld[x][y] = (IS_RND_GATE_GRAY(element) ?
6252 element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
6253 IS_EM_GATE_GRAY(element) ?
6254 element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
6255 IS_EMC_GATE_GRAY(element) ?
6256 element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
6258 DrawLevelField(x, y);
6260 else if (IS_GATE_GRAY_ACTIVE(element) &&
6261 game.magnify_time_left == 0)
6263 Feld[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
6264 element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
6265 IS_EM_GATE_GRAY_ACTIVE(element) ?
6266 element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
6267 IS_EMC_GATE_GRAY_ACTIVE(element) ?
6268 element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
6270 DrawLevelField(x, y);
6275 static void ToggleLightSwitch(int x, int y)
6277 int element = Feld[x][y];
6279 game.light_time_left =
6280 (element == EL_LIGHT_SWITCH ?
6281 level.time_light * FRAMES_PER_SECOND : 0);
6283 RedrawAllLightSwitchesAndInvisibleElements();
6286 static void ActivateTimegateSwitch(int x, int y)
6290 game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
6292 SCAN_PLAYFIELD(xx, yy)
6294 int element = Feld[xx][yy];
6296 if (element == EL_TIMEGATE_CLOSED ||
6297 element == EL_TIMEGATE_CLOSING)
6299 Feld[xx][yy] = EL_TIMEGATE_OPENING;
6300 PlayLevelSound(xx, yy, SND_CLASS_TIMEGATE_OPENING);
6304 else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
6306 Feld[xx][yy] = EL_TIMEGATE_SWITCH;
6307 DrawLevelField(xx, yy);
6314 Feld[x][y] = (Feld[x][y] == EL_TIMEGATE_SWITCH ? EL_TIMEGATE_SWITCH_ACTIVE :
6315 EL_DC_TIMEGATE_SWITCH_ACTIVE);
6317 Feld[x][y] = EL_TIMEGATE_SWITCH_ACTIVE;
6321 void Impact(int x, int y)
6323 boolean last_line = (y == lev_fieldy - 1);
6324 boolean object_hit = FALSE;
6325 boolean impact = (last_line || object_hit);
6326 int element = Feld[x][y];
6327 int smashed = EL_STEELWALL;
6329 if (!last_line) /* check if element below was hit */
6331 if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
6334 object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
6335 MovDir[x][y + 1] != MV_DOWN ||
6336 MovPos[x][y + 1] <= TILEY / 2));
6338 /* do not smash moving elements that left the smashed field in time */
6339 if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
6340 ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
6343 #if USE_QUICKSAND_IMPACT_BUGFIX
6344 if (Feld[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
6346 RemoveMovingField(x, y + 1);
6347 Feld[x][y + 1] = EL_QUICKSAND_EMPTY;
6348 Feld[x][y + 2] = EL_ROCK;
6349 DrawLevelField(x, y + 2);
6354 if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTYING && object_hit == FALSE)
6356 RemoveMovingField(x, y + 1);
6357 Feld[x][y + 1] = EL_QUICKSAND_FAST_EMPTY;
6358 Feld[x][y + 2] = EL_ROCK;
6359 DrawLevelField(x, y + 2);
6366 smashed = MovingOrBlocked2Element(x, y + 1);
6368 impact = (last_line || object_hit);
6371 if (!last_line && smashed == EL_ACID) /* element falls into acid */
6373 SplashAcid(x, y + 1);
6377 /* !!! not sufficient for all cases -- see EL_PEARL below !!! */
6378 /* only reset graphic animation if graphic really changes after impact */
6380 el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
6382 ResetGfxAnimation(x, y);
6383 DrawLevelField(x, y);
6386 if (impact && CAN_EXPLODE_IMPACT(element))
6391 else if (impact && element == EL_PEARL &&
6392 smashed != EL_DC_MAGIC_WALL && smashed != EL_DC_MAGIC_WALL_ACTIVE)
6394 ResetGfxAnimation(x, y);
6396 Feld[x][y] = EL_PEARL_BREAKING;
6397 PlayLevelSound(x, y, SND_PEARL_BREAKING);
6400 else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
6402 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6407 if (impact && element == EL_AMOEBA_DROP)
6409 if (object_hit && IS_PLAYER(x, y + 1))
6410 KillPlayerUnlessEnemyProtected(x, y + 1);
6411 else if (object_hit && smashed == EL_PENGUIN)
6415 Feld[x][y] = EL_AMOEBA_GROWING;
6416 Store[x][y] = EL_AMOEBA_WET;
6418 ResetRandomAnimationValue(x, y);
6423 if (object_hit) /* check which object was hit */
6425 if ((CAN_PASS_MAGIC_WALL(element) &&
6426 (smashed == EL_MAGIC_WALL ||
6427 smashed == EL_BD_MAGIC_WALL)) ||
6428 (CAN_PASS_DC_MAGIC_WALL(element) &&
6429 smashed == EL_DC_MAGIC_WALL))
6432 int activated_magic_wall =
6433 (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
6434 smashed == EL_BD_MAGIC_WALL ? EL_BD_MAGIC_WALL_ACTIVE :
6435 EL_DC_MAGIC_WALL_ACTIVE);
6437 /* activate magic wall / mill */
6438 SCAN_PLAYFIELD(xx, yy)
6440 if (Feld[xx][yy] == smashed)
6441 Feld[xx][yy] = activated_magic_wall;
6444 game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
6445 game.magic_wall_active = TRUE;
6447 PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
6448 SND_MAGIC_WALL_ACTIVATING :
6449 smashed == EL_BD_MAGIC_WALL ?
6450 SND_BD_MAGIC_WALL_ACTIVATING :
6451 SND_DC_MAGIC_WALL_ACTIVATING));
6454 if (IS_PLAYER(x, y + 1))
6456 if (CAN_SMASH_PLAYER(element))
6458 KillPlayerUnlessEnemyProtected(x, y + 1);
6462 else if (smashed == EL_PENGUIN)
6464 if (CAN_SMASH_PLAYER(element))
6470 else if (element == EL_BD_DIAMOND)
6472 if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
6478 else if (((element == EL_SP_INFOTRON ||
6479 element == EL_SP_ZONK) &&
6480 (smashed == EL_SP_SNIKSNAK ||
6481 smashed == EL_SP_ELECTRON ||
6482 smashed == EL_SP_DISK_ORANGE)) ||
6483 (element == EL_SP_INFOTRON &&
6484 smashed == EL_SP_DISK_YELLOW))
6489 else if (CAN_SMASH_EVERYTHING(element))
6491 if (IS_CLASSIC_ENEMY(smashed) ||
6492 CAN_EXPLODE_SMASHED(smashed))
6497 else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
6499 if (smashed == EL_LAMP ||
6500 smashed == EL_LAMP_ACTIVE)
6505 else if (smashed == EL_NUT)
6507 Feld[x][y + 1] = EL_NUT_BREAKING;
6508 PlayLevelSound(x, y, SND_NUT_BREAKING);
6509 RaiseScoreElement(EL_NUT);
6512 else if (smashed == EL_PEARL)
6514 ResetGfxAnimation(x, y);
6516 Feld[x][y + 1] = EL_PEARL_BREAKING;
6517 PlayLevelSound(x, y, SND_PEARL_BREAKING);
6520 else if (smashed == EL_DIAMOND)
6522 Feld[x][y + 1] = EL_DIAMOND_BREAKING;
6523 PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
6526 else if (IS_BELT_SWITCH(smashed))
6528 ToggleBeltSwitch(x, y + 1);
6530 else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
6531 smashed == EL_SWITCHGATE_SWITCH_DOWN ||
6532 smashed == EL_DC_SWITCHGATE_SWITCH_UP ||
6533 smashed == EL_DC_SWITCHGATE_SWITCH_DOWN)
6535 ToggleSwitchgateSwitch(x, y + 1);
6537 else if (smashed == EL_LIGHT_SWITCH ||
6538 smashed == EL_LIGHT_SWITCH_ACTIVE)
6540 ToggleLightSwitch(x, y + 1);
6545 TestIfElementSmashesCustomElement(x, y, MV_DOWN);
6548 CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6550 CheckElementChangeBySide(x, y + 1, smashed, element,
6551 CE_SWITCHED, CH_SIDE_TOP);
6552 CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
6558 CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6563 /* play sound of magic wall / mill */
6565 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
6566 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ||
6567 Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE))
6569 if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
6570 PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
6571 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
6572 PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
6573 else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
6574 PlayLevelSound(x, y, SND_DC_MAGIC_WALL_FILLING);
6579 /* play sound of object that hits the ground */
6580 if (last_line || object_hit)
6581 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6584 inline static void TurnRoundExt(int x, int y)
6596 { 0, 0 }, { 0, 0 }, { 0, 0 },
6601 int left, right, back;
6605 { MV_DOWN, MV_UP, MV_RIGHT },
6606 { MV_UP, MV_DOWN, MV_LEFT },
6608 { MV_LEFT, MV_RIGHT, MV_DOWN },
6612 { MV_RIGHT, MV_LEFT, MV_UP }
6615 int element = Feld[x][y];
6616 int move_pattern = element_info[element].move_pattern;
6618 int old_move_dir = MovDir[x][y];
6619 int left_dir = turn[old_move_dir].left;
6620 int right_dir = turn[old_move_dir].right;
6621 int back_dir = turn[old_move_dir].back;
6623 int left_dx = move_xy[left_dir].dx, left_dy = move_xy[left_dir].dy;
6624 int right_dx = move_xy[right_dir].dx, right_dy = move_xy[right_dir].dy;
6625 int move_dx = move_xy[old_move_dir].dx, move_dy = move_xy[old_move_dir].dy;
6626 int back_dx = move_xy[back_dir].dx, back_dy = move_xy[back_dir].dy;
6628 int left_x = x + left_dx, left_y = y + left_dy;
6629 int right_x = x + right_dx, right_y = y + right_dy;
6630 int move_x = x + move_dx, move_y = y + move_dy;
6634 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
6636 TestIfBadThingTouchesOtherBadThing(x, y);
6638 if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
6639 MovDir[x][y] = right_dir;
6640 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6641 MovDir[x][y] = left_dir;
6643 if (element == EL_BUG && MovDir[x][y] != old_move_dir)
6645 else if (element == EL_BD_BUTTERFLY) /* && MovDir[x][y] == left_dir) */
6648 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
6650 TestIfBadThingTouchesOtherBadThing(x, y);
6652 if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
6653 MovDir[x][y] = left_dir;
6654 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6655 MovDir[x][y] = right_dir;
6657 if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
6659 else if (element == EL_BD_FIREFLY) /* && MovDir[x][y] == right_dir) */
6662 else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
6664 TestIfBadThingTouchesOtherBadThing(x, y);
6666 if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
6667 MovDir[x][y] = left_dir;
6668 else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
6669 MovDir[x][y] = right_dir;
6671 if (MovDir[x][y] != old_move_dir)
6674 else if (element == EL_YAMYAM)
6676 boolean can_turn_left = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
6677 boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
6679 if (can_turn_left && can_turn_right)
6680 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6681 else if (can_turn_left)
6682 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6683 else if (can_turn_right)
6684 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6686 MovDir[x][y] = back_dir;
6688 MovDelay[x][y] = 16 + 16 * RND(3);
6690 else if (element == EL_DARK_YAMYAM)
6692 boolean can_turn_left = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6694 boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6697 if (can_turn_left && can_turn_right)
6698 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6699 else if (can_turn_left)
6700 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6701 else if (can_turn_right)
6702 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6704 MovDir[x][y] = back_dir;
6706 MovDelay[x][y] = 16 + 16 * RND(3);
6708 else if (element == EL_PACMAN)
6710 boolean can_turn_left = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
6711 boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
6713 if (can_turn_left && can_turn_right)
6714 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6715 else if (can_turn_left)
6716 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6717 else if (can_turn_right)
6718 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6720 MovDir[x][y] = back_dir;
6722 MovDelay[x][y] = 6 + RND(40);
6724 else if (element == EL_PIG)
6726 boolean can_turn_left = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
6727 boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
6728 boolean can_move_on = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
6729 boolean should_turn_left, should_turn_right, should_move_on;
6731 int rnd = RND(rnd_value);
6733 should_turn_left = (can_turn_left &&
6735 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
6736 y + back_dy + left_dy)));
6737 should_turn_right = (can_turn_right &&
6739 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
6740 y + back_dy + right_dy)));
6741 should_move_on = (can_move_on &&
6744 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
6745 y + move_dy + left_dy) ||
6746 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
6747 y + move_dy + right_dy)));
6749 if (should_turn_left || should_turn_right || should_move_on)
6751 if (should_turn_left && should_turn_right && should_move_on)
6752 MovDir[x][y] = (rnd < rnd_value / 3 ? left_dir :
6753 rnd < 2 * rnd_value / 3 ? right_dir :
6755 else if (should_turn_left && should_turn_right)
6756 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6757 else if (should_turn_left && should_move_on)
6758 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
6759 else if (should_turn_right && should_move_on)
6760 MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
6761 else if (should_turn_left)
6762 MovDir[x][y] = left_dir;
6763 else if (should_turn_right)
6764 MovDir[x][y] = right_dir;
6765 else if (should_move_on)
6766 MovDir[x][y] = old_move_dir;
6768 else if (can_move_on && rnd > rnd_value / 8)
6769 MovDir[x][y] = old_move_dir;
6770 else if (can_turn_left && can_turn_right)
6771 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6772 else if (can_turn_left && rnd > rnd_value / 8)
6773 MovDir[x][y] = left_dir;
6774 else if (can_turn_right && rnd > rnd_value/8)
6775 MovDir[x][y] = right_dir;
6777 MovDir[x][y] = back_dir;
6779 xx = x + move_xy[MovDir[x][y]].dx;
6780 yy = y + move_xy[MovDir[x][y]].dy;
6782 if (!IN_LEV_FIELD(xx, yy) ||
6783 (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy])))
6784 MovDir[x][y] = old_move_dir;
6788 else if (element == EL_DRAGON)
6790 boolean can_turn_left = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
6791 boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
6792 boolean can_move_on = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
6794 int rnd = RND(rnd_value);
6796 if (can_move_on && rnd > rnd_value / 8)
6797 MovDir[x][y] = old_move_dir;
6798 else if (can_turn_left && can_turn_right)
6799 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6800 else if (can_turn_left && rnd > rnd_value / 8)
6801 MovDir[x][y] = left_dir;
6802 else if (can_turn_right && rnd > rnd_value / 8)
6803 MovDir[x][y] = right_dir;
6805 MovDir[x][y] = back_dir;
6807 xx = x + move_xy[MovDir[x][y]].dx;
6808 yy = y + move_xy[MovDir[x][y]].dy;
6810 if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
6811 MovDir[x][y] = old_move_dir;
6815 else if (element == EL_MOLE)
6817 boolean can_move_on =
6818 (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
6819 IS_AMOEBOID(Feld[move_x][move_y]) ||
6820 Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
6823 boolean can_turn_left =
6824 (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
6825 IS_AMOEBOID(Feld[left_x][left_y])));
6827 boolean can_turn_right =
6828 (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
6829 IS_AMOEBOID(Feld[right_x][right_y])));
6831 if (can_turn_left && can_turn_right)
6832 MovDir[x][y] = (RND(2) ? left_dir : right_dir);
6833 else if (can_turn_left)
6834 MovDir[x][y] = left_dir;
6836 MovDir[x][y] = right_dir;
6839 if (MovDir[x][y] != old_move_dir)
6842 else if (element == EL_BALLOON)
6844 MovDir[x][y] = game.wind_direction;
6847 else if (element == EL_SPRING)
6849 #if USE_NEW_SPRING_BUMPER
6850 if (MovDir[x][y] & MV_HORIZONTAL)
6852 if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
6853 !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
6855 Feld[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
6856 ResetGfxAnimation(move_x, move_y);
6857 DrawLevelField(move_x, move_y);
6859 MovDir[x][y] = back_dir;
6861 else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
6862 SPRING_CAN_ENTER_FIELD(element, x, y + 1))
6863 MovDir[x][y] = MV_NONE;
6866 if (MovDir[x][y] & MV_HORIZONTAL &&
6867 (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
6868 SPRING_CAN_ENTER_FIELD(element, x, y + 1)))
6869 MovDir[x][y] = MV_NONE;
6874 else if (element == EL_ROBOT ||
6875 element == EL_SATELLITE ||
6876 element == EL_PENGUIN ||
6877 element == EL_EMC_ANDROID)
6879 int attr_x = -1, attr_y = -1;
6890 for (i = 0; i < MAX_PLAYERS; i++)
6892 struct PlayerInfo *player = &stored_player[i];
6893 int jx = player->jx, jy = player->jy;
6895 if (!player->active)
6899 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
6907 if (element == EL_ROBOT && ZX >= 0 && ZY >= 0 &&
6908 (Feld[ZX][ZY] == EL_ROBOT_WHEEL_ACTIVE ||
6909 game.engine_version < VERSION_IDENT(3,1,0,0)))
6915 if (element == EL_PENGUIN)
6918 static int xy[4][2] =
6926 for (i = 0; i < NUM_DIRECTIONS; i++)
6928 int ex = x + xy[i][0];
6929 int ey = y + xy[i][1];
6931 if (IN_LEV_FIELD(ex, ey) && (Feld[ex][ey] == EL_EXIT_OPEN ||
6932 Feld[ex][ey] == EL_EM_EXIT_OPEN ||
6933 Feld[ex][ey] == EL_STEEL_EXIT_OPEN ||
6934 Feld[ex][ey] == EL_EM_STEEL_EXIT_OPEN))
6943 MovDir[x][y] = MV_NONE;
6945 MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
6946 else if (attr_x > x)
6947 MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
6949 MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
6950 else if (attr_y > y)
6951 MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
6953 if (element == EL_ROBOT)
6957 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6958 MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
6959 Moving2Blocked(x, y, &newx, &newy);
6961 if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
6962 MovDelay[x][y] = 8 + 8 * !RND(3);
6964 MovDelay[x][y] = 16;
6966 else if (element == EL_PENGUIN)
6972 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6974 boolean first_horiz = RND(2);
6975 int new_move_dir = MovDir[x][y];
6978 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6979 Moving2Blocked(x, y, &newx, &newy);
6981 if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
6985 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6986 Moving2Blocked(x, y, &newx, &newy);
6988 if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
6991 MovDir[x][y] = old_move_dir;
6995 else if (element == EL_SATELLITE)
7001 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7003 boolean first_horiz = RND(2);
7004 int new_move_dir = MovDir[x][y];
7007 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7008 Moving2Blocked(x, y, &newx, &newy);
7010 if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
7014 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7015 Moving2Blocked(x, y, &newx, &newy);
7017 if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
7020 MovDir[x][y] = old_move_dir;
7024 else if (element == EL_EMC_ANDROID)
7026 static int check_pos[16] =
7028 -1, /* 0 => (invalid) */
7029 7, /* 1 => MV_LEFT */
7030 3, /* 2 => MV_RIGHT */
7031 -1, /* 3 => (invalid) */
7033 0, /* 5 => MV_LEFT | MV_UP */
7034 2, /* 6 => MV_RIGHT | MV_UP */
7035 -1, /* 7 => (invalid) */
7036 5, /* 8 => MV_DOWN */
7037 6, /* 9 => MV_LEFT | MV_DOWN */
7038 4, /* 10 => MV_RIGHT | MV_DOWN */
7039 -1, /* 11 => (invalid) */
7040 -1, /* 12 => (invalid) */
7041 -1, /* 13 => (invalid) */
7042 -1, /* 14 => (invalid) */
7043 -1, /* 15 => (invalid) */
7051 { -1, -1, MV_LEFT | MV_UP },
7053 { +1, -1, MV_RIGHT | MV_UP },
7054 { +1, 0, MV_RIGHT },
7055 { +1, +1, MV_RIGHT | MV_DOWN },
7057 { -1, +1, MV_LEFT | MV_DOWN },
7060 int start_pos, check_order;
7061 boolean can_clone = FALSE;
7064 /* check if there is any free field around current position */
7065 for (i = 0; i < 8; i++)
7067 int newx = x + check_xy[i].dx;
7068 int newy = y + check_xy[i].dy;
7070 if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7078 if (can_clone) /* randomly find an element to clone */
7082 start_pos = check_pos[RND(8)];
7083 check_order = (RND(2) ? -1 : +1);
7085 for (i = 0; i < 8; i++)
7087 int pos_raw = start_pos + i * check_order;
7088 int pos = (pos_raw + 8) % 8;
7089 int newx = x + check_xy[pos].dx;
7090 int newy = y + check_xy[pos].dy;
7092 if (ANDROID_CAN_CLONE_FIELD(newx, newy))
7094 element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
7095 element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
7097 Store[x][y] = Feld[newx][newy];
7106 if (can_clone) /* randomly find a direction to move */
7110 start_pos = check_pos[RND(8)];
7111 check_order = (RND(2) ? -1 : +1);
7113 for (i = 0; i < 8; i++)
7115 int pos_raw = start_pos + i * check_order;
7116 int pos = (pos_raw + 8) % 8;
7117 int newx = x + check_xy[pos].dx;
7118 int newy = y + check_xy[pos].dy;
7119 int new_move_dir = check_xy[pos].dir;
7121 if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7123 MovDir[x][y] = new_move_dir;
7124 MovDelay[x][y] = level.android_clone_time * 8 + 1;
7133 if (can_clone) /* cloning and moving successful */
7136 /* cannot clone -- try to move towards player */
7138 start_pos = check_pos[MovDir[x][y] & 0x0f];
7139 check_order = (RND(2) ? -1 : +1);
7141 for (i = 0; i < 3; i++)
7143 /* first check start_pos, then previous/next or (next/previous) pos */
7144 int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
7145 int pos = (pos_raw + 8) % 8;
7146 int newx = x + check_xy[pos].dx;
7147 int newy = y + check_xy[pos].dy;
7148 int new_move_dir = check_xy[pos].dir;
7150 if (IS_PLAYER(newx, newy))
7153 if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7155 MovDir[x][y] = new_move_dir;
7156 MovDelay[x][y] = level.android_move_time * 8 + 1;
7163 else if (move_pattern == MV_TURNING_LEFT ||
7164 move_pattern == MV_TURNING_RIGHT ||
7165 move_pattern == MV_TURNING_LEFT_RIGHT ||
7166 move_pattern == MV_TURNING_RIGHT_LEFT ||
7167 move_pattern == MV_TURNING_RANDOM ||
7168 move_pattern == MV_ALL_DIRECTIONS)
7170 boolean can_turn_left =
7171 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
7172 boolean can_turn_right =
7173 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
7175 if (element_info[element].move_stepsize == 0) /* "not moving" */
7178 if (move_pattern == MV_TURNING_LEFT)
7179 MovDir[x][y] = left_dir;
7180 else if (move_pattern == MV_TURNING_RIGHT)
7181 MovDir[x][y] = right_dir;
7182 else if (move_pattern == MV_TURNING_LEFT_RIGHT)
7183 MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
7184 else if (move_pattern == MV_TURNING_RIGHT_LEFT)
7185 MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
7186 else if (move_pattern == MV_TURNING_RANDOM)
7187 MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
7188 can_turn_right && !can_turn_left ? right_dir :
7189 RND(2) ? left_dir : right_dir);
7190 else if (can_turn_left && can_turn_right)
7191 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7192 else if (can_turn_left)
7193 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7194 else if (can_turn_right)
7195 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7197 MovDir[x][y] = back_dir;
7199 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7201 else if (move_pattern == MV_HORIZONTAL ||
7202 move_pattern == MV_VERTICAL)
7204 if (move_pattern & old_move_dir)
7205 MovDir[x][y] = back_dir;
7206 else if (move_pattern == MV_HORIZONTAL)
7207 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
7208 else if (move_pattern == MV_VERTICAL)
7209 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
7211 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7213 else if (move_pattern & MV_ANY_DIRECTION)
7215 MovDir[x][y] = move_pattern;
7216 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7218 else if (move_pattern & MV_WIND_DIRECTION)
7220 MovDir[x][y] = game.wind_direction;
7221 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7223 else if (move_pattern == MV_ALONG_LEFT_SIDE)
7225 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
7226 MovDir[x][y] = left_dir;
7227 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7228 MovDir[x][y] = right_dir;
7230 if (MovDir[x][y] != old_move_dir)
7231 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7233 else if (move_pattern == MV_ALONG_RIGHT_SIDE)
7235 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
7236 MovDir[x][y] = right_dir;
7237 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7238 MovDir[x][y] = left_dir;
7240 if (MovDir[x][y] != old_move_dir)
7241 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7243 else if (move_pattern == MV_TOWARDS_PLAYER ||
7244 move_pattern == MV_AWAY_FROM_PLAYER)
7246 int attr_x = -1, attr_y = -1;
7248 boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
7259 for (i = 0; i < MAX_PLAYERS; i++)
7261 struct PlayerInfo *player = &stored_player[i];
7262 int jx = player->jx, jy = player->jy;
7264 if (!player->active)
7268 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7276 MovDir[x][y] = MV_NONE;
7278 MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
7279 else if (attr_x > x)
7280 MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
7282 MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
7283 else if (attr_y > y)
7284 MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
7286 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7288 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7290 boolean first_horiz = RND(2);
7291 int new_move_dir = MovDir[x][y];
7293 if (element_info[element].move_stepsize == 0) /* "not moving" */
7295 first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
7296 MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7302 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7303 Moving2Blocked(x, y, &newx, &newy);
7305 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7309 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7310 Moving2Blocked(x, y, &newx, &newy);
7312 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7315 MovDir[x][y] = old_move_dir;
7318 else if (move_pattern == MV_WHEN_PUSHED ||
7319 move_pattern == MV_WHEN_DROPPED)
7321 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7322 MovDir[x][y] = MV_NONE;
7326 else if (move_pattern & MV_MAZE_RUNNER_STYLE)
7328 static int test_xy[7][2] =
7338 static int test_dir[7] =
7348 boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
7349 int move_preference = -1000000; /* start with very low preference */
7350 int new_move_dir = MV_NONE;
7351 int start_test = RND(4);
7354 for (i = 0; i < NUM_DIRECTIONS; i++)
7356 int move_dir = test_dir[start_test + i];
7357 int move_dir_preference;
7359 xx = x + test_xy[start_test + i][0];
7360 yy = y + test_xy[start_test + i][1];
7362 if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
7363 (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
7365 new_move_dir = move_dir;
7370 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
7373 move_dir_preference = -1 * RunnerVisit[xx][yy];
7374 if (hunter_mode && PlayerVisit[xx][yy] > 0)
7375 move_dir_preference = PlayerVisit[xx][yy];
7377 if (move_dir_preference > move_preference)
7379 /* prefer field that has not been visited for the longest time */
7380 move_preference = move_dir_preference;
7381 new_move_dir = move_dir;
7383 else if (move_dir_preference == move_preference &&
7384 move_dir == old_move_dir)
7386 /* prefer last direction when all directions are preferred equally */
7387 move_preference = move_dir_preference;
7388 new_move_dir = move_dir;
7392 MovDir[x][y] = new_move_dir;
7393 if (old_move_dir != new_move_dir)
7394 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7398 static void TurnRound(int x, int y)
7400 int direction = MovDir[x][y];
7404 GfxDir[x][y] = MovDir[x][y];
7406 if (direction != MovDir[x][y])
7410 GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
7412 ResetGfxFrame(x, y, FALSE);
7415 static boolean JustBeingPushed(int x, int y)
7419 for (i = 0; i < MAX_PLAYERS; i++)
7421 struct PlayerInfo *player = &stored_player[i];
7423 if (player->active && player->is_pushing && player->MovPos)
7425 int next_jx = player->jx + (player->jx - player->last_jx);
7426 int next_jy = player->jy + (player->jy - player->last_jy);
7428 if (x == next_jx && y == next_jy)
7436 void StartMoving(int x, int y)
7438 boolean started_moving = FALSE; /* some elements can fall _and_ move */
7439 int element = Feld[x][y];
7444 if (MovDelay[x][y] == 0)
7445 GfxAction[x][y] = ACTION_DEFAULT;
7447 if (CAN_FALL(element) && y < lev_fieldy - 1)
7449 if ((x > 0 && IS_PLAYER(x - 1, y)) ||
7450 (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
7451 if (JustBeingPushed(x, y))
7454 if (element == EL_QUICKSAND_FULL)
7456 if (IS_FREE(x, y + 1))
7458 InitMovingField(x, y, MV_DOWN);
7459 started_moving = TRUE;
7461 Feld[x][y] = EL_QUICKSAND_EMPTYING;
7462 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7463 if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7464 Store[x][y] = EL_ROCK;
7466 Store[x][y] = EL_ROCK;
7469 PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7471 else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7473 if (!MovDelay[x][y])
7474 MovDelay[x][y] = TILEY + 1;
7483 Feld[x][y] = EL_QUICKSAND_EMPTY;
7484 Feld[x][y + 1] = EL_QUICKSAND_FULL;
7485 Store[x][y + 1] = Store[x][y];
7488 PlayLevelSoundAction(x, y, ACTION_FILLING);
7491 else if (element == EL_QUICKSAND_FAST_FULL)
7493 if (IS_FREE(x, y + 1))
7495 InitMovingField(x, y, MV_DOWN);
7496 started_moving = TRUE;
7498 Feld[x][y] = EL_QUICKSAND_FAST_EMPTYING;
7499 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7500 if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7501 Store[x][y] = EL_ROCK;
7503 Store[x][y] = EL_ROCK;
7506 PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7508 else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7510 if (!MovDelay[x][y])
7511 MovDelay[x][y] = TILEY + 1;
7520 Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
7521 Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7522 Store[x][y + 1] = Store[x][y];
7525 PlayLevelSoundAction(x, y, ACTION_FILLING);
7528 else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7529 Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7531 InitMovingField(x, y, MV_DOWN);
7532 started_moving = TRUE;
7534 Feld[x][y] = EL_QUICKSAND_FILLING;
7535 Store[x][y] = element;
7537 PlayLevelSoundAction(x, y, ACTION_FILLING);
7539 else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7540 Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7542 InitMovingField(x, y, MV_DOWN);
7543 started_moving = TRUE;
7545 Feld[x][y] = EL_QUICKSAND_FAST_FILLING;
7546 Store[x][y] = element;
7548 PlayLevelSoundAction(x, y, ACTION_FILLING);
7550 else if (element == EL_MAGIC_WALL_FULL)
7552 if (IS_FREE(x, y + 1))
7554 InitMovingField(x, y, MV_DOWN);
7555 started_moving = TRUE;
7557 Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
7558 Store[x][y] = EL_CHANGED(Store[x][y]);
7560 else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
7562 if (!MovDelay[x][y])
7563 MovDelay[x][y] = TILEY/4 + 1;
7572 Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
7573 Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
7574 Store[x][y + 1] = EL_CHANGED(Store[x][y]);
7578 else if (element == EL_BD_MAGIC_WALL_FULL)
7580 if (IS_FREE(x, y + 1))
7582 InitMovingField(x, y, MV_DOWN);
7583 started_moving = TRUE;
7585 Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
7586 Store[x][y] = EL_CHANGED_BD(Store[x][y]);
7588 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
7590 if (!MovDelay[x][y])
7591 MovDelay[x][y] = TILEY/4 + 1;
7600 Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
7601 Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
7602 Store[x][y + 1] = EL_CHANGED_BD(Store[x][y]);
7606 else if (element == EL_DC_MAGIC_WALL_FULL)
7608 if (IS_FREE(x, y + 1))
7610 InitMovingField(x, y, MV_DOWN);
7611 started_moving = TRUE;
7613 Feld[x][y] = EL_DC_MAGIC_WALL_EMPTYING;
7614 Store[x][y] = EL_CHANGED_DC(Store[x][y]);
7616 else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
7618 if (!MovDelay[x][y])
7619 MovDelay[x][y] = TILEY/4 + 1;
7628 Feld[x][y] = EL_DC_MAGIC_WALL_ACTIVE;
7629 Feld[x][y + 1] = EL_DC_MAGIC_WALL_FULL;
7630 Store[x][y + 1] = EL_CHANGED_DC(Store[x][y]);
7634 else if ((CAN_PASS_MAGIC_WALL(element) &&
7635 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
7636 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)) ||
7637 (CAN_PASS_DC_MAGIC_WALL(element) &&
7638 (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)))
7641 InitMovingField(x, y, MV_DOWN);
7642 started_moving = TRUE;
7645 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
7646 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ? EL_BD_MAGIC_WALL_FILLING :
7647 EL_DC_MAGIC_WALL_FILLING);
7648 Store[x][y] = element;
7650 else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
7652 SplashAcid(x, y + 1);
7654 InitMovingField(x, y, MV_DOWN);
7655 started_moving = TRUE;
7657 Store[x][y] = EL_ACID;
7660 #if USE_FIX_IMPACT_COLLISION
7661 (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7662 CheckImpact[x][y] && !IS_FREE(x, y + 1)) ||
7664 (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7665 CheckCollision[x][y] && !IS_FREE(x, y + 1)) ||
7667 (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
7668 CAN_FALL(element) && WasJustFalling[x][y] &&
7669 (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
7671 (game.engine_version < VERSION_IDENT(2,2,0,7) &&
7672 CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
7673 (Feld[x][y + 1] == EL_BLOCKED)))
7675 /* this is needed for a special case not covered by calling "Impact()"
7676 from "ContinueMoving()": if an element moves to a tile directly below
7677 another element which was just falling on that tile (which was empty
7678 in the previous frame), the falling element above would just stop
7679 instead of smashing the element below (in previous version, the above
7680 element was just checked for "moving" instead of "falling", resulting
7681 in incorrect smashes caused by horizontal movement of the above
7682 element; also, the case of the player being the element to smash was
7683 simply not covered here... :-/ ) */
7685 CheckCollision[x][y] = 0;
7686 CheckImpact[x][y] = 0;
7690 else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
7692 if (MovDir[x][y] == MV_NONE)
7694 InitMovingField(x, y, MV_DOWN);
7695 started_moving = TRUE;
7698 else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
7700 if (WasJustFalling[x][y]) /* prevent animation from being restarted */
7701 MovDir[x][y] = MV_DOWN;
7703 InitMovingField(x, y, MV_DOWN);
7704 started_moving = TRUE;
7706 else if (element == EL_AMOEBA_DROP)
7708 Feld[x][y] = EL_AMOEBA_GROWING;
7709 Store[x][y] = EL_AMOEBA_WET;
7711 else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
7712 (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
7713 !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
7714 element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
7716 boolean can_fall_left = (x > 0 && IS_FREE(x - 1, y) &&
7717 (IS_FREE(x - 1, y + 1) ||
7718 Feld[x - 1][y + 1] == EL_ACID));
7719 boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
7720 (IS_FREE(x + 1, y + 1) ||
7721 Feld[x + 1][y + 1] == EL_ACID));
7722 boolean can_fall_any = (can_fall_left || can_fall_right);
7723 boolean can_fall_both = (can_fall_left && can_fall_right);
7724 int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
7726 #if USE_NEW_ALL_SLIPPERY
7727 if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
7729 if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
7730 can_fall_right = FALSE;
7731 else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
7732 can_fall_left = FALSE;
7733 else if (slippery_type == SLIPPERY_ONLY_LEFT)
7734 can_fall_right = FALSE;
7735 else if (slippery_type == SLIPPERY_ONLY_RIGHT)
7736 can_fall_left = FALSE;
7738 can_fall_any = (can_fall_left || can_fall_right);
7739 can_fall_both = FALSE;
7742 if (can_fall_any && IS_CUSTOM_ELEMENT(Feld[x][y + 1]))
7744 if (slippery_type == SLIPPERY_ONLY_LEFT)
7745 can_fall_right = FALSE;
7746 else if (slippery_type == SLIPPERY_ONLY_RIGHT)
7747 can_fall_left = FALSE;
7748 else if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
7749 can_fall_right = FALSE;
7750 else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
7751 can_fall_left = FALSE;
7753 can_fall_any = (can_fall_left || can_fall_right);
7754 can_fall_both = (can_fall_left && can_fall_right);
7758 #if USE_NEW_ALL_SLIPPERY
7760 #if USE_NEW_SP_SLIPPERY
7761 /* !!! better use the same properties as for custom elements here !!! */
7762 else if (game.engine_version >= VERSION_IDENT(3,1,1,0) &&
7763 can_fall_both && IS_SP_ELEMENT(Feld[x][y + 1]))
7765 can_fall_right = FALSE; /* slip down on left side */
7766 can_fall_both = FALSE;
7771 #if USE_NEW_ALL_SLIPPERY
7774 if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
7775 can_fall_right = FALSE; /* slip down on left side */
7777 can_fall_left = !(can_fall_right = RND(2));
7779 can_fall_both = FALSE;
7784 if (game.emulation == EMU_BOULDERDASH ||
7785 element == EL_BD_ROCK || element == EL_BD_DIAMOND)
7786 can_fall_right = FALSE; /* slip down on left side */
7788 can_fall_left = !(can_fall_right = RND(2));
7790 can_fall_both = FALSE;
7796 /* if not determined otherwise, prefer left side for slipping down */
7797 InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
7798 started_moving = TRUE;
7802 else if (IS_BELT_ACTIVE(Feld[x][y + 1]) && !CAN_MOVE(element))
7804 else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
7807 boolean left_is_free = (x > 0 && IS_FREE(x - 1, y));
7808 boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
7809 int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
7810 int belt_dir = game.belt_dir[belt_nr];
7812 if ((belt_dir == MV_LEFT && left_is_free) ||
7813 (belt_dir == MV_RIGHT && right_is_free))
7815 int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
7817 InitMovingField(x, y, belt_dir);
7818 started_moving = TRUE;
7820 Pushed[x][y] = TRUE;
7821 Pushed[nextx][y] = TRUE;
7823 GfxAction[x][y] = ACTION_DEFAULT;
7827 MovDir[x][y] = 0; /* if element was moving, stop it */
7832 /* not "else if" because of elements that can fall and move (EL_SPRING) */
7834 if (CAN_MOVE(element) && !started_moving && MovDir[x][y] != MV_NONE)
7836 if (CAN_MOVE(element) && !started_moving)
7839 int move_pattern = element_info[element].move_pattern;
7844 if (MovDir[x][y] == MV_NONE)
7846 printf("StartMoving(): %d,%d: element %d ['%s'] not moving\n",
7847 x, y, element, element_info[element].token_name);
7848 printf("StartMoving(): This should never happen!\n");
7853 Moving2Blocked(x, y, &newx, &newy);
7855 if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
7858 if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7859 CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7861 WasJustMoving[x][y] = 0;
7862 CheckCollision[x][y] = 0;
7864 TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
7866 if (Feld[x][y] != element) /* element has changed */
7870 if (!MovDelay[x][y]) /* start new movement phase */
7872 /* all objects that can change their move direction after each step
7873 (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
7875 if (element != EL_YAMYAM &&
7876 element != EL_DARK_YAMYAM &&
7877 element != EL_PACMAN &&
7878 !(move_pattern & MV_ANY_DIRECTION) &&
7879 move_pattern != MV_TURNING_LEFT &&
7880 move_pattern != MV_TURNING_RIGHT &&
7881 move_pattern != MV_TURNING_LEFT_RIGHT &&
7882 move_pattern != MV_TURNING_RIGHT_LEFT &&
7883 move_pattern != MV_TURNING_RANDOM)
7887 if (MovDelay[x][y] && (element == EL_BUG ||
7888 element == EL_SPACESHIP ||
7889 element == EL_SP_SNIKSNAK ||
7890 element == EL_SP_ELECTRON ||
7891 element == EL_MOLE))
7892 DrawLevelField(x, y);
7896 if (MovDelay[x][y]) /* wait some time before next movement */
7900 if (element == EL_ROBOT ||
7901 element == EL_YAMYAM ||
7902 element == EL_DARK_YAMYAM)
7904 DrawLevelElementAnimationIfNeeded(x, y, element);
7905 PlayLevelSoundAction(x, y, ACTION_WAITING);
7907 else if (element == EL_SP_ELECTRON)
7908 DrawLevelElementAnimationIfNeeded(x, y, element);
7909 else if (element == EL_DRAGON)
7912 int dir = MovDir[x][y];
7913 int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
7914 int dy = (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
7915 int graphic = (dir == MV_LEFT ? IMG_FLAMES_1_LEFT :
7916 dir == MV_RIGHT ? IMG_FLAMES_1_RIGHT :
7917 dir == MV_UP ? IMG_FLAMES_1_UP :
7918 dir == MV_DOWN ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
7919 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
7921 GfxAction[x][y] = ACTION_ATTACKING;
7923 if (IS_PLAYER(x, y))
7924 DrawPlayerField(x, y);
7926 DrawLevelField(x, y);
7928 PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
7930 for (i = 1; i <= 3; i++)
7932 int xx = x + i * dx;
7933 int yy = y + i * dy;
7934 int sx = SCREENX(xx);
7935 int sy = SCREENY(yy);
7936 int flame_graphic = graphic + (i - 1);
7938 if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
7943 int flamed = MovingOrBlocked2Element(xx, yy);
7947 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
7949 else if (IS_MOVING(xx, yy) || IS_BLOCKED(xx, yy))
7950 RemoveMovingField(xx, yy);
7952 RemoveField(xx, yy);
7954 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
7957 RemoveMovingField(xx, yy);
7960 ChangeDelay[xx][yy] = 0;
7962 Feld[xx][yy] = EL_FLAMES;
7964 if (IN_SCR_FIELD(sx, sy))
7966 DrawLevelFieldCrumbledSand(xx, yy);
7967 DrawGraphic(sx, sy, flame_graphic, frame);
7972 if (Feld[xx][yy] == EL_FLAMES)
7973 Feld[xx][yy] = EL_EMPTY;
7974 DrawLevelField(xx, yy);
7979 if (MovDelay[x][y]) /* element still has to wait some time */
7981 PlayLevelSoundAction(x, y, ACTION_WAITING);
7987 /* now make next step */
7989 Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
7991 if (DONT_COLLIDE_WITH(element) &&
7992 IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
7993 !PLAYER_ENEMY_PROTECTED(newx, newy))
7995 TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
8000 else if (CAN_MOVE_INTO_ACID(element) &&
8001 IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID &&
8002 !IS_MV_DIAGONAL(MovDir[x][y]) &&
8003 (MovDir[x][y] == MV_DOWN ||
8004 game.engine_version >= VERSION_IDENT(3,1,0,0)))
8006 SplashAcid(newx, newy);
8007 Store[x][y] = EL_ACID;
8009 else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
8011 if (Feld[newx][newy] == EL_EXIT_OPEN ||
8012 Feld[newx][newy] == EL_EM_EXIT_OPEN ||
8013 Feld[newx][newy] == EL_STEEL_EXIT_OPEN ||
8014 Feld[newx][newy] == EL_EM_STEEL_EXIT_OPEN)
8017 DrawLevelField(x, y);
8019 PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
8020 if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
8021 DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
8023 local_player->friends_still_needed--;
8024 if (!local_player->friends_still_needed &&
8025 !local_player->GameOver && AllPlayersGone)
8026 PlayerWins(local_player);
8030 else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
8032 if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MP_MOVING)
8033 DrawLevelField(newx, newy);
8035 GfxDir[x][y] = MovDir[x][y] = MV_NONE;
8037 else if (!IS_FREE(newx, newy))
8039 GfxAction[x][y] = ACTION_WAITING;
8041 if (IS_PLAYER(x, y))
8042 DrawPlayerField(x, y);
8044 DrawLevelField(x, y);
8049 else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
8051 if (IS_FOOD_PIG(Feld[newx][newy]))
8053 if (IS_MOVING(newx, newy))
8054 RemoveMovingField(newx, newy);
8057 Feld[newx][newy] = EL_EMPTY;
8058 DrawLevelField(newx, newy);
8061 PlayLevelSound(x, y, SND_PIG_DIGGING);
8063 else if (!IS_FREE(newx, newy))
8065 if (IS_PLAYER(x, y))
8066 DrawPlayerField(x, y);
8068 DrawLevelField(x, y);
8073 else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
8075 if (Store[x][y] != EL_EMPTY)
8077 boolean can_clone = FALSE;
8080 /* check if element to clone is still there */
8081 for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
8083 if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == Store[x][y])
8091 /* cannot clone or target field not free anymore -- do not clone */
8092 if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8093 Store[x][y] = EL_EMPTY;
8096 if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8098 if (IS_MV_DIAGONAL(MovDir[x][y]))
8100 int diagonal_move_dir = MovDir[x][y];
8101 int stored = Store[x][y];
8102 int change_delay = 8;
8105 /* android is moving diagonally */
8107 CreateField(x, y, EL_DIAGONAL_SHRINKING);
8109 Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
8110 GfxElement[x][y] = EL_EMC_ANDROID;
8111 GfxAction[x][y] = ACTION_SHRINKING;
8112 GfxDir[x][y] = diagonal_move_dir;
8113 ChangeDelay[x][y] = change_delay;
8115 graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
8118 DrawLevelGraphicAnimation(x, y, graphic);
8119 PlayLevelSoundAction(x, y, ACTION_SHRINKING);
8121 if (Feld[newx][newy] == EL_ACID)
8123 SplashAcid(newx, newy);
8128 CreateField(newx, newy, EL_DIAGONAL_GROWING);
8130 Store[newx][newy] = EL_EMC_ANDROID;
8131 GfxElement[newx][newy] = EL_EMC_ANDROID;
8132 GfxAction[newx][newy] = ACTION_GROWING;
8133 GfxDir[newx][newy] = diagonal_move_dir;
8134 ChangeDelay[newx][newy] = change_delay;
8136 graphic = el_act_dir2img(GfxElement[newx][newy],
8137 GfxAction[newx][newy], GfxDir[newx][newy]);
8139 DrawLevelGraphicAnimation(newx, newy, graphic);
8140 PlayLevelSoundAction(newx, newy, ACTION_GROWING);
8146 Feld[newx][newy] = EL_EMPTY;
8147 DrawLevelField(newx, newy);
8149 PlayLevelSoundAction(x, y, ACTION_DIGGING);
8152 else if (!IS_FREE(newx, newy))
8155 if (IS_PLAYER(x, y))
8156 DrawPlayerField(x, y);
8158 DrawLevelField(x, y);
8164 else if (IS_CUSTOM_ELEMENT(element) &&
8165 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
8167 int new_element = Feld[newx][newy];
8169 if (!IS_FREE(newx, newy))
8171 int action = (IS_DIGGABLE(new_element) ? ACTION_DIGGING :
8172 IS_COLLECTIBLE(new_element) ? ACTION_COLLECTING :
8175 /* no element can dig solid indestructible elements */
8176 if (IS_INDESTRUCTIBLE(new_element) &&
8177 !IS_DIGGABLE(new_element) &&
8178 !IS_COLLECTIBLE(new_element))
8181 if (AmoebaNr[newx][newy] &&
8182 (new_element == EL_AMOEBA_FULL ||
8183 new_element == EL_BD_AMOEBA ||
8184 new_element == EL_AMOEBA_GROWING))
8186 AmoebaCnt[AmoebaNr[newx][newy]]--;
8187 AmoebaCnt2[AmoebaNr[newx][newy]]--;
8190 if (IS_MOVING(newx, newy))
8191 RemoveMovingField(newx, newy);
8194 RemoveField(newx, newy);
8195 DrawLevelField(newx, newy);
8198 /* if digged element was about to explode, prevent the explosion */
8199 ExplodeField[newx][newy] = EX_TYPE_NONE;
8201 PlayLevelSoundAction(x, y, action);
8204 Store[newx][newy] = EL_EMPTY;
8206 /* this makes it possible to leave the removed element again */
8207 if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
8208 Store[newx][newy] = new_element;
8210 if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
8212 int move_leave_element = element_info[element].move_leave_element;
8214 /* this makes it possible to leave the removed element again */
8215 Store[newx][newy] = (move_leave_element == EL_TRIGGER_ELEMENT ?
8216 new_element : move_leave_element);
8220 if (move_pattern & MV_MAZE_RUNNER_STYLE)
8222 RunnerVisit[x][y] = FrameCounter;
8223 PlayerVisit[x][y] /= 8; /* expire player visit path */
8226 else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
8228 if (!IS_FREE(newx, newy))
8230 if (IS_PLAYER(x, y))
8231 DrawPlayerField(x, y);
8233 DrawLevelField(x, y);
8239 boolean wanna_flame = !RND(10);
8240 int dx = newx - x, dy = newy - y;
8241 int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
8242 int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
8243 int element1 = (IN_LEV_FIELD(newx1, newy1) ?
8244 MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
8245 int element2 = (IN_LEV_FIELD(newx2, newy2) ?
8246 MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
8249 IS_CLASSIC_ENEMY(element1) ||
8250 IS_CLASSIC_ENEMY(element2)) &&
8251 element1 != EL_DRAGON && element2 != EL_DRAGON &&
8252 element1 != EL_FLAMES && element2 != EL_FLAMES)
8254 ResetGfxAnimation(x, y);
8255 GfxAction[x][y] = ACTION_ATTACKING;
8257 if (IS_PLAYER(x, y))
8258 DrawPlayerField(x, y);
8260 DrawLevelField(x, y);
8262 PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
8264 MovDelay[x][y] = 50;
8268 RemoveField(newx, newy);
8270 Feld[newx][newy] = EL_FLAMES;
8271 if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
8274 RemoveField(newx1, newy1);
8276 Feld[newx1][newy1] = EL_FLAMES;
8278 if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
8281 RemoveField(newx2, newy2);
8283 Feld[newx2][newy2] = EL_FLAMES;
8290 else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8291 Feld[newx][newy] == EL_DIAMOND)
8293 if (IS_MOVING(newx, newy))
8294 RemoveMovingField(newx, newy);
8297 Feld[newx][newy] = EL_EMPTY;
8298 DrawLevelField(newx, newy);
8301 PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
8303 else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8304 IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
8306 if (AmoebaNr[newx][newy])
8308 AmoebaCnt2[AmoebaNr[newx][newy]]--;
8309 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
8310 Feld[newx][newy] == EL_BD_AMOEBA)
8311 AmoebaCnt[AmoebaNr[newx][newy]]--;
8316 if (IS_MOVING(newx, newy) || IS_BLOCKED(newx, newy))
8318 RemoveMovingField(newx, newy);
8321 if (IS_MOVING(newx, newy))
8323 RemoveMovingField(newx, newy);
8328 Feld[newx][newy] = EL_EMPTY;
8329 DrawLevelField(newx, newy);
8332 PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
8334 else if ((element == EL_PACMAN || element == EL_MOLE)
8335 && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
8337 if (AmoebaNr[newx][newy])
8339 AmoebaCnt2[AmoebaNr[newx][newy]]--;
8340 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
8341 Feld[newx][newy] == EL_BD_AMOEBA)
8342 AmoebaCnt[AmoebaNr[newx][newy]]--;
8345 if (element == EL_MOLE)
8347 Feld[newx][newy] = EL_AMOEBA_SHRINKING;
8348 PlayLevelSound(x, y, SND_MOLE_DIGGING);
8350 ResetGfxAnimation(x, y);
8351 GfxAction[x][y] = ACTION_DIGGING;
8352 DrawLevelField(x, y);
8354 MovDelay[newx][newy] = 0; /* start amoeba shrinking delay */
8356 return; /* wait for shrinking amoeba */
8358 else /* element == EL_PACMAN */
8360 Feld[newx][newy] = EL_EMPTY;
8361 DrawLevelField(newx, newy);
8362 PlayLevelSound(x, y, SND_PACMAN_DIGGING);
8365 else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
8366 (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
8367 (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
8369 /* wait for shrinking amoeba to completely disappear */
8372 else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
8374 /* object was running against a wall */
8379 /* !!! NEW "CE_BLOCKED" STUFF !!! -- DOES NOT WORK YET... !!! */
8380 if (move_pattern & MV_ANY_DIRECTION &&
8381 move_pattern == MovDir[x][y])
8383 int blocking_element =
8384 (IN_LEV_FIELD(newx, newy) ? Feld[newx][newy] : BorderElement);
8386 CheckElementChangeBySide(x, y, element, blocking_element, CE_BLOCKED,
8389 element = Feld[x][y]; /* element might have changed */
8393 if (GFX_ELEMENT(element) != EL_SAND) /* !!! FIX THIS (crumble) !!! */
8394 DrawLevelElementAnimation(x, y, element);
8396 if (DONT_TOUCH(element))
8397 TestIfBadThingTouchesPlayer(x, y);
8402 InitMovingField(x, y, MovDir[x][y]);
8404 PlayLevelSoundAction(x, y, ACTION_MOVING);
8408 ContinueMoving(x, y);
8411 void ContinueMoving(int x, int y)
8413 int element = Feld[x][y];
8414 struct ElementInfo *ei = &element_info[element];
8415 int direction = MovDir[x][y];
8416 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
8417 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
8418 int newx = x + dx, newy = y + dy;
8419 int stored = Store[x][y];
8420 int stored_new = Store[newx][newy];
8421 boolean pushed_by_player = (Pushed[x][y] && IS_PLAYER(x, y));
8422 boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
8423 boolean last_line = (newy == lev_fieldy - 1);
8425 MovPos[x][y] += getElementMoveStepsize(x, y);
8427 if (pushed_by_player) /* special case: moving object pushed by player */
8428 MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
8430 if (ABS(MovPos[x][y]) < TILEX)
8433 int ee = Feld[x][y];
8434 int gg = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
8435 int ff = getGraphicAnimationFrame(gg, GfxFrame[x][y]);
8437 printf("::: %d.%d: moving %d ... [%d, %d, %d] [%d, %d, %d]\n",
8438 x, y, ABS(MovPos[x][y]),
8440 GfxAction[x][y], GfxDir[x][y], GfxFrame[x][y]);
8443 DrawLevelField(x, y);
8445 return; /* element is still moving */
8448 /* element reached destination field */
8450 Feld[x][y] = EL_EMPTY;
8451 Feld[newx][newy] = element;
8452 MovPos[x][y] = 0; /* force "not moving" for "crumbled sand" */
8454 if (Store[x][y] == EL_ACID) /* element is moving into acid pool */
8456 element = Feld[newx][newy] = EL_ACID;
8458 else if (element == EL_MOLE)
8460 Feld[x][y] = EL_SAND;
8462 DrawLevelFieldCrumbledSandNeighbours(x, y);
8464 else if (element == EL_QUICKSAND_FILLING)
8466 element = Feld[newx][newy] = get_next_element(element);
8467 Store[newx][newy] = Store[x][y];
8469 else if (element == EL_QUICKSAND_EMPTYING)
8471 Feld[x][y] = get_next_element(element);
8472 element = Feld[newx][newy] = Store[x][y];
8474 else if (element == EL_QUICKSAND_FAST_FILLING)
8476 element = Feld[newx][newy] = get_next_element(element);
8477 Store[newx][newy] = Store[x][y];
8479 else if (element == EL_QUICKSAND_FAST_EMPTYING)
8481 Feld[x][y] = get_next_element(element);
8482 element = Feld[newx][newy] = Store[x][y];
8484 else if (element == EL_MAGIC_WALL_FILLING)
8486 element = Feld[newx][newy] = get_next_element(element);
8487 if (!game.magic_wall_active)
8488 element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
8489 Store[newx][newy] = Store[x][y];
8491 else if (element == EL_MAGIC_WALL_EMPTYING)
8493 Feld[x][y] = get_next_element(element);
8494 if (!game.magic_wall_active)
8495 Feld[x][y] = EL_MAGIC_WALL_DEAD;
8496 element = Feld[newx][newy] = Store[x][y];
8498 #if USE_NEW_CUSTOM_VALUE
8499 InitField(newx, newy, FALSE);
8502 else if (element == EL_BD_MAGIC_WALL_FILLING)
8504 element = Feld[newx][newy] = get_next_element(element);
8505 if (!game.magic_wall_active)
8506 element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
8507 Store[newx][newy] = Store[x][y];
8509 else if (element == EL_BD_MAGIC_WALL_EMPTYING)
8511 Feld[x][y] = get_next_element(element);
8512 if (!game.magic_wall_active)
8513 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
8514 element = Feld[newx][newy] = Store[x][y];
8516 #if USE_NEW_CUSTOM_VALUE
8517 InitField(newx, newy, FALSE);
8520 else if (element == EL_DC_MAGIC_WALL_FILLING)
8522 element = Feld[newx][newy] = get_next_element(element);
8523 if (!game.magic_wall_active)
8524 element = Feld[newx][newy] = EL_DC_MAGIC_WALL_DEAD;
8525 Store[newx][newy] = Store[x][y];
8527 else if (element == EL_DC_MAGIC_WALL_EMPTYING)
8529 Feld[x][y] = get_next_element(element);
8530 if (!game.magic_wall_active)
8531 Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
8532 element = Feld[newx][newy] = Store[x][y];
8534 #if USE_NEW_CUSTOM_VALUE
8535 InitField(newx, newy, FALSE);
8538 else if (element == EL_AMOEBA_DROPPING)
8540 Feld[x][y] = get_next_element(element);
8541 element = Feld[newx][newy] = Store[x][y];
8543 else if (element == EL_SOKOBAN_OBJECT)
8546 Feld[x][y] = Back[x][y];
8548 if (Back[newx][newy])
8549 Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
8551 Back[x][y] = Back[newx][newy] = 0;
8554 Store[x][y] = EL_EMPTY;
8559 MovDelay[newx][newy] = 0;
8561 if (CAN_CHANGE_OR_HAS_ACTION(element))
8563 /* copy element change control values to new field */
8564 ChangeDelay[newx][newy] = ChangeDelay[x][y];
8565 ChangePage[newx][newy] = ChangePage[x][y];
8566 ChangeCount[newx][newy] = ChangeCount[x][y];
8567 ChangeEvent[newx][newy] = ChangeEvent[x][y];
8570 #if USE_NEW_CUSTOM_VALUE
8571 CustomValue[newx][newy] = CustomValue[x][y];
8574 ChangeDelay[x][y] = 0;
8575 ChangePage[x][y] = -1;
8576 ChangeCount[x][y] = 0;
8577 ChangeEvent[x][y] = -1;
8579 #if USE_NEW_CUSTOM_VALUE
8580 CustomValue[x][y] = 0;
8583 /* copy animation control values to new field */
8584 GfxFrame[newx][newy] = GfxFrame[x][y];
8585 GfxRandom[newx][newy] = GfxRandom[x][y]; /* keep same random value */
8586 GfxAction[newx][newy] = GfxAction[x][y]; /* keep action one frame */
8587 GfxDir[newx][newy] = GfxDir[x][y]; /* keep element direction */
8589 Pushed[x][y] = Pushed[newx][newy] = FALSE;
8591 /* some elements can leave other elements behind after moving */
8593 if (ei->move_leave_element != EL_EMPTY &&
8594 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
8595 (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
8597 if (IS_CUSTOM_ELEMENT(element) && ei->move_leave_element != EL_EMPTY &&
8598 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
8599 (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
8602 int move_leave_element = ei->move_leave_element;
8606 /* this makes it possible to leave the removed element again */
8607 if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
8608 move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
8610 /* this makes it possible to leave the removed element again */
8611 if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
8612 move_leave_element = stored;
8615 /* this makes it possible to leave the removed element again */
8616 if (ei->move_leave_type == LEAVE_TYPE_LIMITED &&
8617 ei->move_leave_element == EL_TRIGGER_ELEMENT)
8618 move_leave_element = stored;
8621 Feld[x][y] = move_leave_element;
8623 if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
8624 MovDir[x][y] = direction;
8626 InitField(x, y, FALSE);
8628 if (GFX_CRUMBLED(Feld[x][y]))
8629 DrawLevelFieldCrumbledSandNeighbours(x, y);
8631 if (ELEM_IS_PLAYER(move_leave_element))
8632 RelocatePlayer(x, y, move_leave_element);
8635 /* do this after checking for left-behind element */
8636 ResetGfxAnimation(x, y); /* reset animation values for old field */
8638 if (!CAN_MOVE(element) ||
8639 (CAN_FALL(element) && direction == MV_DOWN &&
8640 (element == EL_SPRING ||
8641 element_info[element].move_pattern == MV_WHEN_PUSHED ||
8642 element_info[element].move_pattern == MV_WHEN_DROPPED)))
8643 GfxDir[x][y] = MovDir[newx][newy] = 0;
8645 DrawLevelField(x, y);
8646 DrawLevelField(newx, newy);
8648 Stop[newx][newy] = TRUE; /* ignore this element until the next frame */
8650 /* prevent pushed element from moving on in pushed direction */
8651 if (pushed_by_player && CAN_MOVE(element) &&
8652 element_info[element].move_pattern & MV_ANY_DIRECTION &&
8653 !(element_info[element].move_pattern & direction))
8654 TurnRound(newx, newy);
8656 /* prevent elements on conveyor belt from moving on in last direction */
8657 if (pushed_by_conveyor && CAN_FALL(element) &&
8658 direction & MV_HORIZONTAL)
8659 MovDir[newx][newy] = 0;
8661 if (!pushed_by_player)
8663 int nextx = newx + dx, nexty = newy + dy;
8664 boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
8666 WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
8668 if (CAN_FALL(element) && direction == MV_DOWN)
8669 WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
8671 if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
8672 CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
8674 #if USE_FIX_IMPACT_COLLISION
8675 if (CAN_FALL(element) && direction == MV_DOWN && check_collision_again)
8676 CheckImpact[newx][newy] = CHECK_DELAY_IMPACT;
8680 if (DONT_TOUCH(element)) /* object may be nasty to player or others */
8682 TestIfBadThingTouchesPlayer(newx, newy);
8683 TestIfBadThingTouchesFriend(newx, newy);
8685 if (!IS_CUSTOM_ELEMENT(element))
8686 TestIfBadThingTouchesOtherBadThing(newx, newy);
8688 else if (element == EL_PENGUIN)
8689 TestIfFriendTouchesBadThing(newx, newy);
8691 /* give the player one last chance (one more frame) to move away */
8692 if (CAN_FALL(element) && direction == MV_DOWN &&
8693 (last_line || (!IS_FREE(x, newy + 1) &&
8694 (!IS_PLAYER(x, newy + 1) ||
8695 game.engine_version < VERSION_IDENT(3,1,1,0)))))
8698 if (pushed_by_player && !game.use_change_when_pushing_bug)
8700 int push_side = MV_DIR_OPPOSITE(direction);
8701 struct PlayerInfo *player = PLAYERINFO(x, y);
8703 CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
8704 player->index_bit, push_side);
8705 CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
8706 player->index_bit, push_side);
8709 if (element == EL_EMC_ANDROID && pushed_by_player) /* make another move */
8710 MovDelay[newx][newy] = 1;
8712 CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
8714 TestIfElementTouchesCustomElement(x, y); /* empty or new element */
8717 if (ChangePage[newx][newy] != -1) /* delayed change */
8719 int page = ChangePage[newx][newy];
8720 struct ElementChangeInfo *change = &ei->change_page[page];
8722 ChangePage[newx][newy] = -1;
8724 if (change->can_change)
8726 if (ChangeElement(newx, newy, element, page))
8728 if (change->post_change_function)
8729 change->post_change_function(newx, newy);
8733 if (change->has_action)
8734 ExecuteCustomElementAction(newx, newy, element, page);
8738 TestIfElementHitsCustomElement(newx, newy, direction);
8739 TestIfPlayerTouchesCustomElement(newx, newy);
8740 TestIfElementTouchesCustomElement(newx, newy);
8742 if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
8743 IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
8744 CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
8745 MV_DIR_OPPOSITE(direction));
8748 int AmoebeNachbarNr(int ax, int ay)
8751 int element = Feld[ax][ay];
8753 static int xy[4][2] =
8761 for (i = 0; i < NUM_DIRECTIONS; i++)
8763 int x = ax + xy[i][0];
8764 int y = ay + xy[i][1];
8766 if (!IN_LEV_FIELD(x, y))
8769 if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
8770 group_nr = AmoebaNr[x][y];
8776 void AmoebenVereinigen(int ax, int ay)
8778 int i, x, y, xx, yy;
8779 int new_group_nr = AmoebaNr[ax][ay];
8780 static int xy[4][2] =
8788 if (new_group_nr == 0)
8791 for (i = 0; i < NUM_DIRECTIONS; i++)
8796 if (!IN_LEV_FIELD(x, y))
8799 if ((Feld[x][y] == EL_AMOEBA_FULL ||
8800 Feld[x][y] == EL_BD_AMOEBA ||
8801 Feld[x][y] == EL_AMOEBA_DEAD) &&
8802 AmoebaNr[x][y] != new_group_nr)
8804 int old_group_nr = AmoebaNr[x][y];
8806 if (old_group_nr == 0)
8809 AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
8810 AmoebaCnt[old_group_nr] = 0;
8811 AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
8812 AmoebaCnt2[old_group_nr] = 0;
8814 SCAN_PLAYFIELD(xx, yy)
8816 if (AmoebaNr[xx][yy] == old_group_nr)
8817 AmoebaNr[xx][yy] = new_group_nr;
8823 void AmoebeUmwandeln(int ax, int ay)
8827 if (Feld[ax][ay] == EL_AMOEBA_DEAD)
8829 int group_nr = AmoebaNr[ax][ay];
8834 printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
8835 printf("AmoebeUmwandeln(): This should never happen!\n");
8840 SCAN_PLAYFIELD(x, y)
8842 if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
8845 Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
8849 PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
8850 SND_AMOEBA_TURNING_TO_GEM :
8851 SND_AMOEBA_TURNING_TO_ROCK));
8856 static int xy[4][2] =
8864 for (i = 0; i < NUM_DIRECTIONS; i++)
8869 if (!IN_LEV_FIELD(x, y))
8872 if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
8874 PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
8875 SND_AMOEBA_TURNING_TO_GEM :
8876 SND_AMOEBA_TURNING_TO_ROCK));
8883 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
8886 int group_nr = AmoebaNr[ax][ay];
8887 boolean done = FALSE;
8892 printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
8893 printf("AmoebeUmwandelnBD(): This should never happen!\n");
8898 SCAN_PLAYFIELD(x, y)
8900 if (AmoebaNr[x][y] == group_nr &&
8901 (Feld[x][y] == EL_AMOEBA_DEAD ||
8902 Feld[x][y] == EL_BD_AMOEBA ||
8903 Feld[x][y] == EL_AMOEBA_GROWING))
8906 Feld[x][y] = new_element;
8907 InitField(x, y, FALSE);
8908 DrawLevelField(x, y);
8914 PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
8915 SND_BD_AMOEBA_TURNING_TO_ROCK :
8916 SND_BD_AMOEBA_TURNING_TO_GEM));
8919 void AmoebeWaechst(int x, int y)
8921 static unsigned long sound_delay = 0;
8922 static unsigned long sound_delay_value = 0;
8924 if (!MovDelay[x][y]) /* start new growing cycle */
8928 if (DelayReached(&sound_delay, sound_delay_value))
8930 PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
8931 sound_delay_value = 30;
8935 if (MovDelay[x][y]) /* wait some time before growing bigger */
8938 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8940 int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
8941 6 - MovDelay[x][y]);
8943 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
8946 if (!MovDelay[x][y])
8948 Feld[x][y] = Store[x][y];
8950 DrawLevelField(x, y);
8955 void AmoebaDisappearing(int x, int y)
8957 static unsigned long sound_delay = 0;
8958 static unsigned long sound_delay_value = 0;
8960 if (!MovDelay[x][y]) /* start new shrinking cycle */
8964 if (DelayReached(&sound_delay, sound_delay_value))
8965 sound_delay_value = 30;
8968 if (MovDelay[x][y]) /* wait some time before shrinking */
8971 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8973 int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
8974 6 - MovDelay[x][y]);
8976 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
8979 if (!MovDelay[x][y])
8981 Feld[x][y] = EL_EMPTY;
8982 DrawLevelField(x, y);
8984 /* don't let mole enter this field in this cycle;
8985 (give priority to objects falling to this field from above) */
8991 void AmoebeAbleger(int ax, int ay)
8994 int element = Feld[ax][ay];
8995 int graphic = el2img(element);
8996 int newax = ax, neway = ay;
8997 boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
8998 static int xy[4][2] =
9006 if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
9008 Feld[ax][ay] = EL_AMOEBA_DEAD;
9009 DrawLevelField(ax, ay);
9013 if (IS_ANIMATED(graphic))
9014 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9016 if (!MovDelay[ax][ay]) /* start making new amoeba field */
9017 MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
9019 if (MovDelay[ax][ay]) /* wait some time before making new amoeba */
9022 if (MovDelay[ax][ay])
9026 if (can_drop) /* EL_AMOEBA_WET or EL_EMC_DRIPPER */
9029 int x = ax + xy[start][0];
9030 int y = ay + xy[start][1];
9032 if (!IN_LEV_FIELD(x, y))
9035 if (IS_FREE(x, y) ||
9036 CAN_GROW_INTO(Feld[x][y]) ||
9037 Feld[x][y] == EL_QUICKSAND_EMPTY ||
9038 Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
9044 if (newax == ax && neway == ay)
9047 else /* normal or "filled" (BD style) amoeba */
9050 boolean waiting_for_player = FALSE;
9052 for (i = 0; i < NUM_DIRECTIONS; i++)
9054 int j = (start + i) % 4;
9055 int x = ax + xy[j][0];
9056 int y = ay + xy[j][1];
9058 if (!IN_LEV_FIELD(x, y))
9061 if (IS_FREE(x, y) ||
9062 CAN_GROW_INTO(Feld[x][y]) ||
9063 Feld[x][y] == EL_QUICKSAND_EMPTY ||
9064 Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
9070 else if (IS_PLAYER(x, y))
9071 waiting_for_player = TRUE;
9074 if (newax == ax && neway == ay) /* amoeba cannot grow */
9076 if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
9078 Feld[ax][ay] = EL_AMOEBA_DEAD;
9079 DrawLevelField(ax, ay);
9080 AmoebaCnt[AmoebaNr[ax][ay]]--;
9082 if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0) /* amoeba is completely dead */
9084 if (element == EL_AMOEBA_FULL)
9085 AmoebeUmwandeln(ax, ay);
9086 else if (element == EL_BD_AMOEBA)
9087 AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
9092 else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
9094 /* amoeba gets larger by growing in some direction */
9096 int new_group_nr = AmoebaNr[ax][ay];
9099 if (new_group_nr == 0)
9101 printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
9102 printf("AmoebeAbleger(): This should never happen!\n");
9107 AmoebaNr[newax][neway] = new_group_nr;
9108 AmoebaCnt[new_group_nr]++;
9109 AmoebaCnt2[new_group_nr]++;
9111 /* if amoeba touches other amoeba(s) after growing, unify them */
9112 AmoebenVereinigen(newax, neway);
9114 if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
9116 AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
9122 if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
9123 (neway == lev_fieldy - 1 && newax != ax))
9125 Feld[newax][neway] = EL_AMOEBA_GROWING; /* creation of new amoeba */
9126 Store[newax][neway] = element;
9128 else if (neway == ay || element == EL_EMC_DRIPPER)
9130 Feld[newax][neway] = EL_AMOEBA_DROP; /* drop left/right of amoeba */
9132 PlayLevelSoundAction(newax, neway, ACTION_GROWING);
9136 InitMovingField(ax, ay, MV_DOWN); /* drop dripping from amoeba */
9137 Feld[ax][ay] = EL_AMOEBA_DROPPING;
9138 Store[ax][ay] = EL_AMOEBA_DROP;
9139 ContinueMoving(ax, ay);
9143 DrawLevelField(newax, neway);
9146 void Life(int ax, int ay)
9150 int element = Feld[ax][ay];
9151 int graphic = el2img(element);
9152 int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
9154 boolean changed = FALSE;
9156 if (IS_ANIMATED(graphic))
9157 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9162 if (!MovDelay[ax][ay]) /* start new "game of life" cycle */
9163 MovDelay[ax][ay] = life_time;
9165 if (MovDelay[ax][ay]) /* wait some time before next cycle */
9168 if (MovDelay[ax][ay])
9172 for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
9174 int xx = ax+x1, yy = ay+y1;
9177 if (!IN_LEV_FIELD(xx, yy))
9180 for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
9182 int x = xx+x2, y = yy+y2;
9184 if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
9187 if (((Feld[x][y] == element ||
9188 (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
9190 (IS_FREE(x, y) && Stop[x][y]))
9194 if (xx == ax && yy == ay) /* field in the middle */
9196 if (nachbarn < life_parameter[0] ||
9197 nachbarn > life_parameter[1])
9199 Feld[xx][yy] = EL_EMPTY;
9201 DrawLevelField(xx, yy);
9202 Stop[xx][yy] = TRUE;
9206 else if (IS_FREE(xx, yy) || CAN_GROW_INTO(Feld[xx][yy]))
9207 { /* free border field */
9208 if (nachbarn >= life_parameter[2] &&
9209 nachbarn <= life_parameter[3])
9211 Feld[xx][yy] = element;
9212 MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
9214 DrawLevelField(xx, yy);
9215 Stop[xx][yy] = TRUE;
9222 PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
9223 SND_GAME_OF_LIFE_GROWING);
9226 static void InitRobotWheel(int x, int y)
9228 ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
9231 static void RunRobotWheel(int x, int y)
9233 PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
9236 static void StopRobotWheel(int x, int y)
9238 if (ZX == x && ZY == y)
9242 game.robot_wheel_active = FALSE;
9246 static void InitTimegateWheel(int x, int y)
9248 ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
9251 static void RunTimegateWheel(int x, int y)
9253 PlayLevelSound(x, y, SND_CLASS_TIMEGATE_SWITCH_ACTIVE);
9256 static void InitMagicBallDelay(int x, int y)
9259 ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
9261 ChangeDelay[x][y] = level.ball_time * FRAMES_PER_SECOND + 1;
9265 static void ActivateMagicBall(int bx, int by)
9269 if (level.ball_random)
9271 int pos_border = RND(8); /* select one of the eight border elements */
9272 int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
9273 int xx = pos_content % 3;
9274 int yy = pos_content / 3;
9279 if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
9280 CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9284 for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
9286 int xx = x - bx + 1;
9287 int yy = y - by + 1;
9289 if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
9290 CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9294 game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
9297 void CheckExit(int x, int y)
9299 if (local_player->gems_still_needed > 0 ||
9300 local_player->sokobanfields_still_needed > 0 ||
9301 local_player->lights_still_needed > 0)
9303 int element = Feld[x][y];
9304 int graphic = el2img(element);
9306 if (IS_ANIMATED(graphic))
9307 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9312 if (AllPlayersGone) /* do not re-open exit door closed after last player */
9315 Feld[x][y] = EL_EXIT_OPENING;
9317 PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
9320 void CheckExitEM(int x, int y)
9322 if (local_player->gems_still_needed > 0 ||
9323 local_player->sokobanfields_still_needed > 0 ||
9324 local_player->lights_still_needed > 0)
9326 int element = Feld[x][y];
9327 int graphic = el2img(element);
9329 if (IS_ANIMATED(graphic))
9330 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9335 if (AllPlayersGone) /* do not re-open exit door closed after last player */
9338 Feld[x][y] = EL_EM_EXIT_OPENING;
9340 PlayLevelSoundNearest(x, y, SND_CLASS_EM_EXIT_OPENING);
9343 void CheckExitSteel(int x, int y)
9345 if (local_player->gems_still_needed > 0 ||
9346 local_player->sokobanfields_still_needed > 0 ||
9347 local_player->lights_still_needed > 0)
9349 int element = Feld[x][y];
9350 int graphic = el2img(element);
9352 if (IS_ANIMATED(graphic))
9353 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9358 if (AllPlayersGone) /* do not re-open exit door closed after last player */
9361 Feld[x][y] = EL_STEEL_EXIT_OPENING;
9363 PlayLevelSoundNearest(x, y, SND_CLASS_STEEL_EXIT_OPENING);
9366 void CheckExitSteelEM(int x, int y)
9368 if (local_player->gems_still_needed > 0 ||
9369 local_player->sokobanfields_still_needed > 0 ||
9370 local_player->lights_still_needed > 0)
9372 int element = Feld[x][y];
9373 int graphic = el2img(element);
9375 if (IS_ANIMATED(graphic))
9376 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9381 if (AllPlayersGone) /* do not re-open exit door closed after last player */
9384 Feld[x][y] = EL_EM_STEEL_EXIT_OPENING;
9386 PlayLevelSoundNearest(x, y, SND_CLASS_EM_STEEL_EXIT_OPENING);
9389 void CheckExitSP(int x, int y)
9391 if (local_player->gems_still_needed > 0)
9393 int element = Feld[x][y];
9394 int graphic = el2img(element);
9396 if (IS_ANIMATED(graphic))
9397 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9402 if (AllPlayersGone) /* do not re-open exit door closed after last player */
9405 Feld[x][y] = EL_SP_EXIT_OPENING;
9407 PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
9410 static void CloseAllOpenTimegates()
9414 SCAN_PLAYFIELD(x, y)
9416 int element = Feld[x][y];
9418 if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
9420 Feld[x][y] = EL_TIMEGATE_CLOSING;
9422 PlayLevelSoundAction(x, y, ACTION_CLOSING);
9427 void DrawTwinkleOnField(int x, int y)
9429 if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
9432 if (Feld[x][y] == EL_BD_DIAMOND)
9435 if (MovDelay[x][y] == 0) /* next animation frame */
9436 MovDelay[x][y] = 11 * !GetSimpleRandom(500);
9438 if (MovDelay[x][y] != 0) /* wait some time before next frame */
9442 if (setup.direct_draw && MovDelay[x][y])
9443 SetDrawtoField(DRAW_BUFFERED);
9445 DrawLevelElementAnimation(x, y, Feld[x][y]);
9447 if (MovDelay[x][y] != 0)
9449 int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
9450 10 - MovDelay[x][y]);
9452 DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
9454 if (setup.direct_draw)
9458 dest_x = FX + SCREENX(x) * TILEX;
9459 dest_y = FY + SCREENY(y) * TILEY;
9461 BlitBitmap(drawto_field, window,
9462 dest_x, dest_y, TILEX, TILEY, dest_x, dest_y);
9463 SetDrawtoField(DRAW_DIRECT);
9469 void MauerWaechst(int x, int y)
9473 if (!MovDelay[x][y]) /* next animation frame */
9474 MovDelay[x][y] = 3 * delay;
9476 if (MovDelay[x][y]) /* wait some time before next frame */
9480 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9482 int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
9483 int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
9485 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
9488 if (!MovDelay[x][y])
9490 if (MovDir[x][y] == MV_LEFT)
9492 if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
9493 DrawLevelField(x - 1, y);
9495 else if (MovDir[x][y] == MV_RIGHT)
9497 if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
9498 DrawLevelField(x + 1, y);
9500 else if (MovDir[x][y] == MV_UP)
9502 if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
9503 DrawLevelField(x, y - 1);
9507 if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
9508 DrawLevelField(x, y + 1);
9511 Feld[x][y] = Store[x][y];
9513 GfxDir[x][y] = MovDir[x][y] = MV_NONE;
9514 DrawLevelField(x, y);
9519 void MauerAbleger(int ax, int ay)
9521 int element = Feld[ax][ay];
9522 int graphic = el2img(element);
9523 boolean oben_frei = FALSE, unten_frei = FALSE;
9524 boolean links_frei = FALSE, rechts_frei = FALSE;
9525 boolean oben_massiv = FALSE, unten_massiv = FALSE;
9526 boolean links_massiv = FALSE, rechts_massiv = FALSE;
9527 boolean new_wall = FALSE;
9529 if (IS_ANIMATED(graphic))
9530 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9532 if (!MovDelay[ax][ay]) /* start building new wall */
9533 MovDelay[ax][ay] = 6;
9535 if (MovDelay[ax][ay]) /* wait some time before building new wall */
9538 if (MovDelay[ax][ay])
9542 if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9544 if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9546 if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9548 if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9551 if (element == EL_EXPANDABLE_WALL_VERTICAL ||
9552 element == EL_EXPANDABLE_WALL_ANY)
9556 Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
9557 Store[ax][ay-1] = element;
9558 GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9559 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9560 DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9561 IMG_EXPANDABLE_WALL_GROWING_UP, 0);
9566 Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
9567 Store[ax][ay+1] = element;
9568 GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9569 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9570 DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9571 IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
9576 if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9577 element == EL_EXPANDABLE_WALL_ANY ||
9578 element == EL_EXPANDABLE_WALL ||
9579 element == EL_BD_EXPANDABLE_WALL)
9583 Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
9584 Store[ax-1][ay] = element;
9585 GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9586 if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9587 DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9588 IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
9594 Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
9595 Store[ax+1][ay] = element;
9596 GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9597 if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9598 DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9599 IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
9604 if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
9605 DrawLevelField(ax, ay);
9607 if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
9609 if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
9610 unten_massiv = TRUE;
9611 if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
9612 links_massiv = TRUE;
9613 if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
9614 rechts_massiv = TRUE;
9616 if (((oben_massiv && unten_massiv) ||
9617 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9618 element == EL_EXPANDABLE_WALL) &&
9619 ((links_massiv && rechts_massiv) ||
9620 element == EL_EXPANDABLE_WALL_VERTICAL))
9621 Feld[ax][ay] = EL_WALL;
9624 PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9627 void MauerAblegerStahl(int ax, int ay)
9629 int element = Feld[ax][ay];
9630 int graphic = el2img(element);
9631 boolean oben_frei = FALSE, unten_frei = FALSE;
9632 boolean links_frei = FALSE, rechts_frei = FALSE;
9633 boolean oben_massiv = FALSE, unten_massiv = FALSE;
9634 boolean links_massiv = FALSE, rechts_massiv = FALSE;
9635 boolean new_wall = FALSE;
9637 if (IS_ANIMATED(graphic))
9638 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9640 if (!MovDelay[ax][ay]) /* start building new wall */
9641 MovDelay[ax][ay] = 6;
9643 if (MovDelay[ax][ay]) /* wait some time before building new wall */
9646 if (MovDelay[ax][ay])
9650 if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9652 if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9654 if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9656 if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9659 if (element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
9660 element == EL_EXPANDABLE_STEELWALL_ANY)
9664 Feld[ax][ay-1] = EL_EXPANDABLE_STEELWALL_GROWING;
9665 Store[ax][ay-1] = element;
9666 GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9667 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9668 DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9669 IMG_EXPANDABLE_STEELWALL_GROWING_UP, 0);
9674 Feld[ax][ay+1] = EL_EXPANDABLE_STEELWALL_GROWING;
9675 Store[ax][ay+1] = element;
9676 GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9677 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9678 DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9679 IMG_EXPANDABLE_STEELWALL_GROWING_DOWN, 0);
9684 if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
9685 element == EL_EXPANDABLE_STEELWALL_ANY)
9689 Feld[ax-1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9690 Store[ax-1][ay] = element;
9691 GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9692 if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9693 DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9694 IMG_EXPANDABLE_STEELWALL_GROWING_LEFT, 0);
9700 Feld[ax+1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9701 Store[ax+1][ay] = element;
9702 GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9703 if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9704 DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9705 IMG_EXPANDABLE_STEELWALL_GROWING_RIGHT, 0);
9710 if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
9712 if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
9713 unten_massiv = TRUE;
9714 if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
9715 links_massiv = TRUE;
9716 if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
9717 rechts_massiv = TRUE;
9719 if (((oben_massiv && unten_massiv) ||
9720 element == EL_EXPANDABLE_STEELWALL_HORIZONTAL) &&
9721 ((links_massiv && rechts_massiv) ||
9722 element == EL_EXPANDABLE_STEELWALL_VERTICAL))
9723 Feld[ax][ay] = EL_WALL;
9726 PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9729 void CheckForDragon(int x, int y)
9732 boolean dragon_found = FALSE;
9733 static int xy[4][2] =
9741 for (i = 0; i < NUM_DIRECTIONS; i++)
9743 for (j = 0; j < 4; j++)
9745 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9747 if (IN_LEV_FIELD(xx, yy) &&
9748 (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
9750 if (Feld[xx][yy] == EL_DRAGON)
9751 dragon_found = TRUE;
9760 for (i = 0; i < NUM_DIRECTIONS; i++)
9762 for (j = 0; j < 3; j++)
9764 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9766 if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
9768 Feld[xx][yy] = EL_EMPTY;
9769 DrawLevelField(xx, yy);
9778 static void InitBuggyBase(int x, int y)
9780 int element = Feld[x][y];
9781 int activating_delay = FRAMES_PER_SECOND / 4;
9784 (element == EL_SP_BUGGY_BASE ?
9785 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
9786 element == EL_SP_BUGGY_BASE_ACTIVATING ?
9788 element == EL_SP_BUGGY_BASE_ACTIVE ?
9789 1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
9792 static void WarnBuggyBase(int x, int y)
9795 static int xy[4][2] =
9803 for (i = 0; i < NUM_DIRECTIONS; i++)
9805 int xx = x + xy[i][0];
9806 int yy = y + xy[i][1];
9808 if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
9810 PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
9817 static void InitTrap(int x, int y)
9819 ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
9822 static void ActivateTrap(int x, int y)
9824 PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
9827 static void ChangeActiveTrap(int x, int y)
9829 int graphic = IMG_TRAP_ACTIVE;
9831 /* if new animation frame was drawn, correct crumbled sand border */
9832 if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
9833 DrawLevelFieldCrumbledSand(x, y);
9836 static int getSpecialActionElement(int element, int number, int base_element)
9838 return (element != EL_EMPTY ? element :
9839 number != -1 ? base_element + number - 1 :
9843 static int getModifiedActionNumber(int value_old, int operator, int operand,
9844 int value_min, int value_max)
9846 int value_new = (operator == CA_MODE_SET ? operand :
9847 operator == CA_MODE_ADD ? value_old + operand :
9848 operator == CA_MODE_SUBTRACT ? value_old - operand :
9849 operator == CA_MODE_MULTIPLY ? value_old * operand :
9850 operator == CA_MODE_DIVIDE ? value_old / MAX(1, operand) :
9851 operator == CA_MODE_MODULO ? value_old % MAX(1, operand) :
9854 return (value_new < value_min ? value_min :
9855 value_new > value_max ? value_max :
9859 static void ExecuteCustomElementAction(int x, int y, int element, int page)
9861 struct ElementInfo *ei = &element_info[element];
9862 struct ElementChangeInfo *change = &ei->change_page[page];
9863 int target_element = change->target_element;
9864 int action_type = change->action_type;
9865 int action_mode = change->action_mode;
9866 int action_arg = change->action_arg;
9869 if (!change->has_action)
9872 /* ---------- determine action paramater values -------------------------- */
9874 int level_time_value =
9875 (level.time > 0 ? TimeLeft :
9878 int action_arg_element =
9879 (action_arg == CA_ARG_PLAYER_TRIGGER ? change->actual_trigger_player :
9880 action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
9881 action_arg == CA_ARG_ELEMENT_TARGET ? change->target_element :
9884 int action_arg_direction =
9885 (action_arg >= CA_ARG_DIRECTION_LEFT &&
9886 action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
9887 action_arg == CA_ARG_DIRECTION_TRIGGER ?
9888 change->actual_trigger_side :
9889 action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
9890 MV_DIR_OPPOSITE(change->actual_trigger_side) :
9893 int action_arg_number_min =
9894 (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
9897 int action_arg_number_max =
9898 (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
9899 action_type == CA_SET_LEVEL_GEMS ? 999 :
9900 action_type == CA_SET_LEVEL_TIME ? 9999 :
9901 action_type == CA_SET_LEVEL_SCORE ? 99999 :
9902 action_type == CA_SET_CE_VALUE ? 9999 :
9903 action_type == CA_SET_CE_SCORE ? 9999 :
9906 int action_arg_number_reset =
9907 (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize[0] :
9908 action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
9909 action_type == CA_SET_LEVEL_TIME ? level.time :
9910 action_type == CA_SET_LEVEL_SCORE ? 0 :
9911 #if USE_NEW_CUSTOM_VALUE
9912 action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
9914 action_type == CA_SET_CE_VALUE ? ei->custom_value_initial :
9916 action_type == CA_SET_CE_SCORE ? 0 :
9919 int action_arg_number =
9920 (action_arg <= CA_ARG_MAX ? action_arg :
9921 action_arg >= CA_ARG_SPEED_NOT_MOVING &&
9922 action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
9923 action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
9924 action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
9925 action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
9926 action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
9927 #if USE_NEW_CUSTOM_VALUE
9928 action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
9930 action_arg == CA_ARG_NUMBER_CE_VALUE ? ei->custom_value_initial :
9932 action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
9933 action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
9934 action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
9935 action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? local_player->gems_still_needed :
9936 action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? local_player->score :
9937 action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
9938 action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
9939 action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
9940 action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
9941 action_arg == CA_ARG_ELEMENT_NR_TARGET ? change->target_element :
9942 action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
9945 int action_arg_number_old =
9946 (action_type == CA_SET_LEVEL_GEMS ? local_player->gems_still_needed :
9947 action_type == CA_SET_LEVEL_TIME ? TimeLeft :
9948 action_type == CA_SET_LEVEL_SCORE ? local_player->score :
9949 action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
9950 action_type == CA_SET_CE_SCORE ? ei->collect_score :
9953 int action_arg_number_new =
9954 getModifiedActionNumber(action_arg_number_old,
9955 action_mode, action_arg_number,
9956 action_arg_number_min, action_arg_number_max);
9958 int trigger_player_bits =
9959 (change->actual_trigger_player >= EL_PLAYER_1 &&
9960 change->actual_trigger_player <= EL_PLAYER_4 ?
9961 (1 << (change->actual_trigger_player - EL_PLAYER_1)) :
9964 int action_arg_player_bits =
9965 (action_arg >= CA_ARG_PLAYER_1 &&
9966 action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
9967 action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
9970 /* ---------- execute action -------------------------------------------- */
9972 switch (action_type)
9979 /* ---------- level actions ------------------------------------------- */
9981 case CA_RESTART_LEVEL:
9983 game.restart_level = TRUE;
9988 case CA_SHOW_ENVELOPE:
9990 int element = getSpecialActionElement(action_arg_element,
9991 action_arg_number, EL_ENVELOPE_1);
9993 if (IS_ENVELOPE(element))
9994 local_player->show_envelope = element;
9999 case CA_SET_LEVEL_TIME:
10001 if (level.time > 0) /* only modify limited time value */
10003 TimeLeft = action_arg_number_new;
10006 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
10008 DisplayGameControlValues();
10010 DrawGameValue_Time(TimeLeft);
10013 if (!TimeLeft && setup.time_limit)
10014 for (i = 0; i < MAX_PLAYERS; i++)
10015 KillPlayer(&stored_player[i]);
10021 case CA_SET_LEVEL_SCORE:
10023 local_player->score = action_arg_number_new;
10026 game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
10028 DisplayGameControlValues();
10030 DrawGameValue_Score(local_player->score);
10036 case CA_SET_LEVEL_GEMS:
10038 local_player->gems_still_needed = action_arg_number_new;
10041 game_panel_controls[GAME_PANEL_GEMS].value = local_player->gems_still_needed;
10043 DisplayGameControlValues();
10045 DrawGameValue_Emeralds(local_player->gems_still_needed);
10051 #if !USE_PLAYER_GRAVITY
10052 case CA_SET_LEVEL_GRAVITY:
10054 game.gravity = (action_arg == CA_ARG_GRAVITY_OFF ? FALSE :
10055 action_arg == CA_ARG_GRAVITY_ON ? TRUE :
10056 action_arg == CA_ARG_GRAVITY_TOGGLE ? !game.gravity :
10062 case CA_SET_LEVEL_WIND:
10064 game.wind_direction = action_arg_direction;
10069 /* ---------- player actions ------------------------------------------ */
10071 case CA_MOVE_PLAYER:
10073 /* automatically move to the next field in specified direction */
10074 for (i = 0; i < MAX_PLAYERS; i++)
10075 if (trigger_player_bits & (1 << i))
10076 stored_player[i].programmed_action = action_arg_direction;
10081 case CA_EXIT_PLAYER:
10083 for (i = 0; i < MAX_PLAYERS; i++)
10084 if (action_arg_player_bits & (1 << i))
10085 PlayerWins(&stored_player[i]);
10090 case CA_KILL_PLAYER:
10092 for (i = 0; i < MAX_PLAYERS; i++)
10093 if (action_arg_player_bits & (1 << i))
10094 KillPlayer(&stored_player[i]);
10099 case CA_SET_PLAYER_KEYS:
10101 int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
10102 int element = getSpecialActionElement(action_arg_element,
10103 action_arg_number, EL_KEY_1);
10105 if (IS_KEY(element))
10107 for (i = 0; i < MAX_PLAYERS; i++)
10109 if (trigger_player_bits & (1 << i))
10111 stored_player[i].key[KEY_NR(element)] = key_state;
10113 DrawGameDoorValues();
10121 case CA_SET_PLAYER_SPEED:
10123 for (i = 0; i < MAX_PLAYERS; i++)
10125 if (trigger_player_bits & (1 << i))
10127 int move_stepsize = TILEX / stored_player[i].move_delay_value;
10129 if (action_arg == CA_ARG_SPEED_FASTER &&
10130 stored_player[i].cannot_move)
10132 action_arg_number = STEPSIZE_VERY_SLOW;
10134 else if (action_arg == CA_ARG_SPEED_SLOWER ||
10135 action_arg == CA_ARG_SPEED_FASTER)
10137 action_arg_number = 2;
10138 action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
10141 else if (action_arg == CA_ARG_NUMBER_RESET)
10143 action_arg_number = level.initial_player_stepsize[i];
10147 getModifiedActionNumber(move_stepsize,
10150 action_arg_number_min,
10151 action_arg_number_max);
10153 SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
10160 case CA_SET_PLAYER_SHIELD:
10162 for (i = 0; i < MAX_PLAYERS; i++)
10164 if (trigger_player_bits & (1 << i))
10166 if (action_arg == CA_ARG_SHIELD_OFF)
10168 stored_player[i].shield_normal_time_left = 0;
10169 stored_player[i].shield_deadly_time_left = 0;
10171 else if (action_arg == CA_ARG_SHIELD_NORMAL)
10173 stored_player[i].shield_normal_time_left = 999999;
10175 else if (action_arg == CA_ARG_SHIELD_DEADLY)
10177 stored_player[i].shield_normal_time_left = 999999;
10178 stored_player[i].shield_deadly_time_left = 999999;
10186 #if USE_PLAYER_GRAVITY
10187 case CA_SET_PLAYER_GRAVITY:
10189 for (i = 0; i < MAX_PLAYERS; i++)
10191 if (trigger_player_bits & (1 << i))
10193 stored_player[i].gravity =
10194 (action_arg == CA_ARG_GRAVITY_OFF ? FALSE :
10195 action_arg == CA_ARG_GRAVITY_ON ? TRUE :
10196 action_arg == CA_ARG_GRAVITY_TOGGLE ? !stored_player[i].gravity :
10197 stored_player[i].gravity);
10205 case CA_SET_PLAYER_ARTWORK:
10207 for (i = 0; i < MAX_PLAYERS; i++)
10209 if (trigger_player_bits & (1 << i))
10211 int artwork_element = action_arg_element;
10213 if (action_arg == CA_ARG_ELEMENT_RESET)
10215 (level.use_artwork_element[i] ? level.artwork_element[i] :
10216 stored_player[i].element_nr);
10218 #if USE_GFX_RESET_PLAYER_ARTWORK
10219 if (stored_player[i].artwork_element != artwork_element)
10220 stored_player[i].Frame = 0;
10223 stored_player[i].artwork_element = artwork_element;
10225 SetPlayerWaiting(&stored_player[i], FALSE);
10227 /* set number of special actions for bored and sleeping animation */
10228 stored_player[i].num_special_action_bored =
10229 get_num_special_action(artwork_element,
10230 ACTION_BORING_1, ACTION_BORING_LAST);
10231 stored_player[i].num_special_action_sleeping =
10232 get_num_special_action(artwork_element,
10233 ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
10240 /* ---------- CE actions ---------------------------------------------- */
10242 case CA_SET_CE_VALUE:
10244 #if USE_NEW_CUSTOM_VALUE
10245 int last_ce_value = CustomValue[x][y];
10247 CustomValue[x][y] = action_arg_number_new;
10249 if (CustomValue[x][y] != last_ce_value)
10251 CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_CHANGES);
10252 CheckTriggeredElementChange(x, y, element, CE_VALUE_CHANGES_OF_X);
10254 if (CustomValue[x][y] == 0)
10256 CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
10257 CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
10265 case CA_SET_CE_SCORE:
10267 #if USE_NEW_CUSTOM_VALUE
10268 int last_ce_score = ei->collect_score;
10270 ei->collect_score = action_arg_number_new;
10272 if (ei->collect_score != last_ce_score)
10274 CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
10275 CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
10277 if (ei->collect_score == 0)
10281 CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
10282 CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
10285 This is a very special case that seems to be a mixture between
10286 CheckElementChange() and CheckTriggeredElementChange(): while
10287 the first one only affects single elements that are triggered
10288 directly, the second one affects multiple elements in the playfield
10289 that are triggered indirectly by another element. This is a third
10290 case: Changing the CE score always affects multiple identical CEs,
10291 so every affected CE must be checked, not only the single CE for
10292 which the CE score was changed in the first place (as every instance
10293 of that CE shares the same CE score, and therefore also can change)!
10295 SCAN_PLAYFIELD(xx, yy)
10297 if (Feld[xx][yy] == element)
10298 CheckElementChange(xx, yy, element, EL_UNDEFINED,
10299 CE_SCORE_GETS_ZERO);
10308 /* ---------- engine actions ------------------------------------------ */
10310 case CA_SET_ENGINE_SCAN_MODE:
10312 InitPlayfieldScanMode(action_arg);
10322 static void CreateFieldExt(int x, int y, int element, boolean is_change)
10324 int old_element = Feld[x][y];
10325 int new_element = GetElementFromGroupElement(element);
10326 int previous_move_direction = MovDir[x][y];
10327 #if USE_NEW_CUSTOM_VALUE
10328 int last_ce_value = CustomValue[x][y];
10330 boolean player_explosion_protected = PLAYER_EXPLOSION_PROTECTED(x, y);
10331 boolean new_element_is_player = ELEM_IS_PLAYER(new_element);
10332 boolean add_player_onto_element = (new_element_is_player &&
10333 #if USE_CODE_THAT_BREAKS_SNAKE_BITE
10334 /* this breaks SnakeBite when a snake is
10335 halfway through a door that closes */
10336 /* NOW FIXED AT LEVEL INIT IN files.c */
10337 new_element != EL_SOKOBAN_FIELD_PLAYER &&
10339 IS_WALKABLE(old_element));
10342 /* check if element under the player changes from accessible to unaccessible
10343 (needed for special case of dropping element which then changes) */
10344 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
10345 IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
10353 if (!add_player_onto_element)
10355 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
10356 RemoveMovingField(x, y);
10360 Feld[x][y] = new_element;
10362 #if !USE_GFX_RESET_GFX_ANIMATION
10363 ResetGfxAnimation(x, y);
10364 ResetRandomAnimationValue(x, y);
10367 if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
10368 MovDir[x][y] = previous_move_direction;
10370 #if USE_NEW_CUSTOM_VALUE
10371 if (element_info[new_element].use_last_ce_value)
10372 CustomValue[x][y] = last_ce_value;
10375 InitField_WithBug1(x, y, FALSE);
10377 new_element = Feld[x][y]; /* element may have changed */
10379 #if USE_GFX_RESET_GFX_ANIMATION
10380 ResetGfxAnimation(x, y);
10381 ResetRandomAnimationValue(x, y);
10384 DrawLevelField(x, y);
10386 if (GFX_CRUMBLED(new_element))
10387 DrawLevelFieldCrumbledSandNeighbours(x, y);
10391 /* check if element under the player changes from accessible to unaccessible
10392 (needed for special case of dropping element which then changes) */
10393 /* (must be checked after creating new element for walkable group elements) */
10394 #if USE_FIX_KILLED_BY_NON_WALKABLE
10395 if (IS_PLAYER(x, y) && !player_explosion_protected &&
10396 IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
10403 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
10404 IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
10413 /* "ChangeCount" not set yet to allow "entered by player" change one time */
10414 if (new_element_is_player)
10415 RelocatePlayer(x, y, new_element);
10418 ChangeCount[x][y]++; /* count number of changes in the same frame */
10420 TestIfBadThingTouchesPlayer(x, y);
10421 TestIfPlayerTouchesCustomElement(x, y);
10422 TestIfElementTouchesCustomElement(x, y);
10425 static void CreateField(int x, int y, int element)
10427 CreateFieldExt(x, y, element, FALSE);
10430 static void CreateElementFromChange(int x, int y, int element)
10432 element = GET_VALID_RUNTIME_ELEMENT(element);
10434 #if USE_STOP_CHANGED_ELEMENTS
10435 if (game.engine_version >= VERSION_IDENT(3,2,0,7))
10437 int old_element = Feld[x][y];
10439 /* prevent changed element from moving in same engine frame
10440 unless both old and new element can either fall or move */
10441 if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
10442 (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
10447 CreateFieldExt(x, y, element, TRUE);
10450 static boolean ChangeElement(int x, int y, int element, int page)
10452 struct ElementInfo *ei = &element_info[element];
10453 struct ElementChangeInfo *change = &ei->change_page[page];
10454 int ce_value = CustomValue[x][y];
10455 int ce_score = ei->collect_score;
10456 int target_element;
10457 int old_element = Feld[x][y];
10459 /* always use default change event to prevent running into a loop */
10460 if (ChangeEvent[x][y] == -1)
10461 ChangeEvent[x][y] = CE_DELAY;
10463 if (ChangeEvent[x][y] == CE_DELAY)
10465 /* reset actual trigger element, trigger player and action element */
10466 change->actual_trigger_element = EL_EMPTY;
10467 change->actual_trigger_player = EL_PLAYER_1;
10468 change->actual_trigger_side = CH_SIDE_NONE;
10469 change->actual_trigger_ce_value = 0;
10470 change->actual_trigger_ce_score = 0;
10473 /* do not change elements more than a specified maximum number of changes */
10474 if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
10477 ChangeCount[x][y]++; /* count number of changes in the same frame */
10479 if (change->explode)
10486 if (change->use_target_content)
10488 boolean complete_replace = TRUE;
10489 boolean can_replace[3][3];
10492 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10495 boolean is_walkable;
10496 boolean is_diggable;
10497 boolean is_collectible;
10498 boolean is_removable;
10499 boolean is_destructible;
10500 int ex = x + xx - 1;
10501 int ey = y + yy - 1;
10502 int content_element = change->target_content.e[xx][yy];
10505 can_replace[xx][yy] = TRUE;
10507 if (ex == x && ey == y) /* do not check changing element itself */
10510 if (content_element == EL_EMPTY_SPACE)
10512 can_replace[xx][yy] = FALSE; /* do not replace border with space */
10517 if (!IN_LEV_FIELD(ex, ey))
10519 can_replace[xx][yy] = FALSE;
10520 complete_replace = FALSE;
10527 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10528 e = MovingOrBlocked2Element(ex, ey);
10530 is_empty = (IS_FREE(ex, ey) ||
10531 (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
10533 is_walkable = (is_empty || IS_WALKABLE(e));
10534 is_diggable = (is_empty || IS_DIGGABLE(e));
10535 is_collectible = (is_empty || IS_COLLECTIBLE(e));
10536 is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
10537 is_removable = (is_diggable || is_collectible);
10539 can_replace[xx][yy] =
10540 (((change->replace_when == CP_WHEN_EMPTY && is_empty) ||
10541 (change->replace_when == CP_WHEN_WALKABLE && is_walkable) ||
10542 (change->replace_when == CP_WHEN_DIGGABLE && is_diggable) ||
10543 (change->replace_when == CP_WHEN_COLLECTIBLE && is_collectible) ||
10544 (change->replace_when == CP_WHEN_REMOVABLE && is_removable) ||
10545 (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
10546 !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
10548 if (!can_replace[xx][yy])
10549 complete_replace = FALSE;
10552 if (!change->only_if_complete || complete_replace)
10554 boolean something_has_changed = FALSE;
10556 if (change->only_if_complete && change->use_random_replace &&
10557 RND(100) < change->random_percentage)
10560 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10562 int ex = x + xx - 1;
10563 int ey = y + yy - 1;
10564 int content_element;
10566 if (can_replace[xx][yy] && (!change->use_random_replace ||
10567 RND(100) < change->random_percentage))
10569 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10570 RemoveMovingField(ex, ey);
10572 ChangeEvent[ex][ey] = ChangeEvent[x][y];
10574 content_element = change->target_content.e[xx][yy];
10575 target_element = GET_TARGET_ELEMENT(element, content_element, change,
10576 ce_value, ce_score);
10578 CreateElementFromChange(ex, ey, target_element);
10580 something_has_changed = TRUE;
10582 /* for symmetry reasons, freeze newly created border elements */
10583 if (ex != x || ey != y)
10584 Stop[ex][ey] = TRUE; /* no more moving in this frame */
10588 if (something_has_changed)
10590 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10591 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10597 target_element = GET_TARGET_ELEMENT(element, change->target_element, change,
10598 ce_value, ce_score);
10600 if (element == EL_DIAGONAL_GROWING ||
10601 element == EL_DIAGONAL_SHRINKING)
10603 target_element = Store[x][y];
10605 Store[x][y] = EL_EMPTY;
10608 CreateElementFromChange(x, y, target_element);
10610 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10611 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10614 /* this uses direct change before indirect change */
10615 CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
10620 #if USE_NEW_DELAYED_ACTION
10622 static void HandleElementChange(int x, int y, int page)
10624 int element = MovingOrBlocked2Element(x, y);
10625 struct ElementInfo *ei = &element_info[element];
10626 struct ElementChangeInfo *change = &ei->change_page[page];
10629 if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
10630 !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
10633 printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
10634 x, y, element, element_info[element].token_name);
10635 printf("HandleElementChange(): This should never happen!\n");
10640 /* this can happen with classic bombs on walkable, changing elements */
10641 if (!CAN_CHANGE_OR_HAS_ACTION(element))
10644 if (!CAN_CHANGE(Back[x][y])) /* prevent permanent repetition */
10645 ChangeDelay[x][y] = 0;
10651 if (ChangeDelay[x][y] == 0) /* initialize element change */
10653 ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
10655 if (change->can_change)
10658 /* !!! not clear why graphic animation should be reset at all here !!! */
10659 /* !!! UPDATE: but is needed for correct Snake Bite tail animation !!! */
10660 #if USE_GFX_RESET_WHEN_NOT_MOVING
10661 /* when a custom element is about to change (for example by change delay),
10662 do not reset graphic animation when the custom element is moving */
10663 if (!IS_MOVING(x, y))
10666 ResetGfxAnimation(x, y);
10667 ResetRandomAnimationValue(x, y);
10671 if (change->pre_change_function)
10672 change->pre_change_function(x, y);
10676 ChangeDelay[x][y]--;
10678 if (ChangeDelay[x][y] != 0) /* continue element change */
10680 if (change->can_change)
10682 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10684 if (IS_ANIMATED(graphic))
10685 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10687 if (change->change_function)
10688 change->change_function(x, y);
10691 else /* finish element change */
10693 if (ChangePage[x][y] != -1) /* remember page from delayed change */
10695 page = ChangePage[x][y];
10696 ChangePage[x][y] = -1;
10698 change = &ei->change_page[page];
10701 if (IS_MOVING(x, y)) /* never change a running system ;-) */
10703 ChangeDelay[x][y] = 1; /* try change after next move step */
10704 ChangePage[x][y] = page; /* remember page to use for change */
10709 if (change->can_change)
10711 if (ChangeElement(x, y, element, page))
10713 if (change->post_change_function)
10714 change->post_change_function(x, y);
10718 if (change->has_action)
10719 ExecuteCustomElementAction(x, y, element, page);
10725 static void HandleElementChange(int x, int y, int page)
10727 int element = MovingOrBlocked2Element(x, y);
10728 struct ElementInfo *ei = &element_info[element];
10729 struct ElementChangeInfo *change = &ei->change_page[page];
10732 if (!CAN_CHANGE(element) && !CAN_CHANGE(Back[x][y]))
10735 printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
10736 x, y, element, element_info[element].token_name);
10737 printf("HandleElementChange(): This should never happen!\n");
10742 /* this can happen with classic bombs on walkable, changing elements */
10743 if (!CAN_CHANGE(element))
10746 if (!CAN_CHANGE(Back[x][y])) /* prevent permanent repetition */
10747 ChangeDelay[x][y] = 0;
10753 if (ChangeDelay[x][y] == 0) /* initialize element change */
10755 ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
10757 ResetGfxAnimation(x, y);
10758 ResetRandomAnimationValue(x, y);
10760 if (change->pre_change_function)
10761 change->pre_change_function(x, y);
10764 ChangeDelay[x][y]--;
10766 if (ChangeDelay[x][y] != 0) /* continue element change */
10768 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10770 if (IS_ANIMATED(graphic))
10771 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10773 if (change->change_function)
10774 change->change_function(x, y);
10776 else /* finish element change */
10778 if (ChangePage[x][y] != -1) /* remember page from delayed change */
10780 page = ChangePage[x][y];
10781 ChangePage[x][y] = -1;
10783 change = &ei->change_page[page];
10786 if (IS_MOVING(x, y)) /* never change a running system ;-) */
10788 ChangeDelay[x][y] = 1; /* try change after next move step */
10789 ChangePage[x][y] = page; /* remember page to use for change */
10794 if (ChangeElement(x, y, element, page))
10796 if (change->post_change_function)
10797 change->post_change_function(x, y);
10804 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
10805 int trigger_element,
10807 int trigger_player,
10811 boolean change_done_any = FALSE;
10812 int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
10815 if (!(trigger_events[trigger_element][trigger_event]))
10819 printf("::: CheckTriggeredElementChangeExt %d ... [%d, %d, %d, '%s']\n",
10820 trigger_event, recursion_loop_depth, recursion_loop_detected,
10821 recursion_loop_element, EL_NAME(recursion_loop_element));
10824 RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
10826 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
10828 int element = EL_CUSTOM_START + i;
10829 boolean change_done = FALSE;
10832 if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
10833 !HAS_ANY_CHANGE_EVENT(element, trigger_event))
10836 for (p = 0; p < element_info[element].num_change_pages; p++)
10838 struct ElementChangeInfo *change = &element_info[element].change_page[p];
10840 if (change->can_change_or_has_action &&
10841 change->has_event[trigger_event] &&
10842 change->trigger_side & trigger_side &&
10843 change->trigger_player & trigger_player &&
10844 change->trigger_page & trigger_page_bits &&
10845 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
10847 change->actual_trigger_element = trigger_element;
10848 change->actual_trigger_player = EL_PLAYER_1 + log_2(trigger_player);
10849 change->actual_trigger_side = trigger_side;
10850 change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
10851 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10853 if ((change->can_change && !change_done) || change->has_action)
10857 SCAN_PLAYFIELD(x, y)
10859 if (Feld[x][y] == element)
10861 if (change->can_change && !change_done)
10863 ChangeDelay[x][y] = 1;
10864 ChangeEvent[x][y] = trigger_event;
10866 HandleElementChange(x, y, p);
10868 #if USE_NEW_DELAYED_ACTION
10869 else if (change->has_action)
10871 ExecuteCustomElementAction(x, y, element, p);
10872 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10875 if (change->has_action)
10877 ExecuteCustomElementAction(x, y, element, p);
10878 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10884 if (change->can_change)
10886 change_done = TRUE;
10887 change_done_any = TRUE;
10894 RECURSION_LOOP_DETECTION_END();
10896 return change_done_any;
10899 static boolean CheckElementChangeExt(int x, int y,
10901 int trigger_element,
10903 int trigger_player,
10906 boolean change_done = FALSE;
10909 if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
10910 !HAS_ANY_CHANGE_EVENT(element, trigger_event))
10913 if (Feld[x][y] == EL_BLOCKED)
10915 Blocked2Moving(x, y, &x, &y);
10916 element = Feld[x][y];
10920 /* check if element has already changed */
10921 if (Feld[x][y] != element)
10924 /* check if element has already changed or is about to change after moving */
10925 if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
10926 Feld[x][y] != element) ||
10928 (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
10929 (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
10930 ChangePage[x][y] != -1)))
10935 printf("::: CheckElementChangeExt %d ... [%d, %d, %d, '%s']\n",
10936 trigger_event, recursion_loop_depth, recursion_loop_detected,
10937 recursion_loop_element, EL_NAME(recursion_loop_element));
10940 RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
10942 for (p = 0; p < element_info[element].num_change_pages; p++)
10944 struct ElementChangeInfo *change = &element_info[element].change_page[p];
10946 /* check trigger element for all events where the element that is checked
10947 for changing interacts with a directly adjacent element -- this is
10948 different to element changes that affect other elements to change on the
10949 whole playfield (which is handeld by CheckTriggeredElementChangeExt()) */
10950 boolean check_trigger_element =
10951 (trigger_event == CE_TOUCHING_X ||
10952 trigger_event == CE_HITTING_X ||
10953 trigger_event == CE_HIT_BY_X ||
10955 /* this one was forgotten until 3.2.3 */
10956 trigger_event == CE_DIGGING_X);
10959 if (change->can_change_or_has_action &&
10960 change->has_event[trigger_event] &&
10961 change->trigger_side & trigger_side &&
10962 change->trigger_player & trigger_player &&
10963 (!check_trigger_element ||
10964 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
10966 change->actual_trigger_element = trigger_element;
10967 change->actual_trigger_player = EL_PLAYER_1 + log_2(trigger_player);
10968 change->actual_trigger_side = trigger_side;
10969 change->actual_trigger_ce_value = CustomValue[x][y];
10970 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10972 /* special case: trigger element not at (x,y) position for some events */
10973 if (check_trigger_element)
10985 { 0, 0 }, { 0, 0 }, { 0, 0 },
10989 int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
10990 int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
10992 change->actual_trigger_ce_value = CustomValue[xx][yy];
10993 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10996 if (change->can_change && !change_done)
10998 ChangeDelay[x][y] = 1;
10999 ChangeEvent[x][y] = trigger_event;
11001 HandleElementChange(x, y, p);
11003 change_done = TRUE;
11005 #if USE_NEW_DELAYED_ACTION
11006 else if (change->has_action)
11008 ExecuteCustomElementAction(x, y, element, p);
11009 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11012 if (change->has_action)
11014 ExecuteCustomElementAction(x, y, element, p);
11015 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11021 RECURSION_LOOP_DETECTION_END();
11023 return change_done;
11026 static void PlayPlayerSound(struct PlayerInfo *player)
11028 int jx = player->jx, jy = player->jy;
11029 int sound_element = player->artwork_element;
11030 int last_action = player->last_action_waiting;
11031 int action = player->action_waiting;
11033 if (player->is_waiting)
11035 if (action != last_action)
11036 PlayLevelSoundElementAction(jx, jy, sound_element, action);
11038 PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
11042 if (action != last_action)
11043 StopSound(element_info[sound_element].sound[last_action]);
11045 if (last_action == ACTION_SLEEPING)
11046 PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
11050 static void PlayAllPlayersSound()
11054 for (i = 0; i < MAX_PLAYERS; i++)
11055 if (stored_player[i].active)
11056 PlayPlayerSound(&stored_player[i]);
11059 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
11061 boolean last_waiting = player->is_waiting;
11062 int move_dir = player->MovDir;
11064 player->dir_waiting = move_dir;
11065 player->last_action_waiting = player->action_waiting;
11069 if (!last_waiting) /* not waiting -> waiting */
11071 player->is_waiting = TRUE;
11073 player->frame_counter_bored =
11075 game.player_boring_delay_fixed +
11076 GetSimpleRandom(game.player_boring_delay_random);
11077 player->frame_counter_sleeping =
11079 game.player_sleeping_delay_fixed +
11080 GetSimpleRandom(game.player_sleeping_delay_random);
11082 InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
11085 if (game.player_sleeping_delay_fixed +
11086 game.player_sleeping_delay_random > 0 &&
11087 player->anim_delay_counter == 0 &&
11088 player->post_delay_counter == 0 &&
11089 FrameCounter >= player->frame_counter_sleeping)
11090 player->is_sleeping = TRUE;
11091 else if (game.player_boring_delay_fixed +
11092 game.player_boring_delay_random > 0 &&
11093 FrameCounter >= player->frame_counter_bored)
11094 player->is_bored = TRUE;
11096 player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
11097 player->is_bored ? ACTION_BORING :
11100 if (player->is_sleeping && player->use_murphy)
11102 /* special case for sleeping Murphy when leaning against non-free tile */
11104 if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
11105 (Feld[player->jx - 1][player->jy] != EL_EMPTY &&
11106 !IS_MOVING(player->jx - 1, player->jy)))
11107 move_dir = MV_LEFT;
11108 else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
11109 (Feld[player->jx + 1][player->jy] != EL_EMPTY &&
11110 !IS_MOVING(player->jx + 1, player->jy)))
11111 move_dir = MV_RIGHT;
11113 player->is_sleeping = FALSE;
11115 player->dir_waiting = move_dir;
11118 if (player->is_sleeping)
11120 if (player->num_special_action_sleeping > 0)
11122 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11124 int last_special_action = player->special_action_sleeping;
11125 int num_special_action = player->num_special_action_sleeping;
11126 int special_action =
11127 (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
11128 last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
11129 last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
11130 last_special_action + 1 : ACTION_SLEEPING);
11131 int special_graphic =
11132 el_act_dir2img(player->artwork_element, special_action, move_dir);
11134 player->anim_delay_counter =
11135 graphic_info[special_graphic].anim_delay_fixed +
11136 GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
11137 player->post_delay_counter =
11138 graphic_info[special_graphic].post_delay_fixed +
11139 GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
11141 player->special_action_sleeping = special_action;
11144 if (player->anim_delay_counter > 0)
11146 player->action_waiting = player->special_action_sleeping;
11147 player->anim_delay_counter--;
11149 else if (player->post_delay_counter > 0)
11151 player->post_delay_counter--;
11155 else if (player->is_bored)
11157 if (player->num_special_action_bored > 0)
11159 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11161 int special_action =
11162 ACTION_BORING_1 + GetSimpleRandom(player->num_special_action_bored);
11163 int special_graphic =
11164 el_act_dir2img(player->artwork_element, special_action, move_dir);
11166 player->anim_delay_counter =
11167 graphic_info[special_graphic].anim_delay_fixed +
11168 GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
11169 player->post_delay_counter =
11170 graphic_info[special_graphic].post_delay_fixed +
11171 GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
11173 player->special_action_bored = special_action;
11176 if (player->anim_delay_counter > 0)
11178 player->action_waiting = player->special_action_bored;
11179 player->anim_delay_counter--;
11181 else if (player->post_delay_counter > 0)
11183 player->post_delay_counter--;
11188 else if (last_waiting) /* waiting -> not waiting */
11190 player->is_waiting = FALSE;
11191 player->is_bored = FALSE;
11192 player->is_sleeping = FALSE;
11194 player->frame_counter_bored = -1;
11195 player->frame_counter_sleeping = -1;
11197 player->anim_delay_counter = 0;
11198 player->post_delay_counter = 0;
11200 player->dir_waiting = player->MovDir;
11201 player->action_waiting = ACTION_DEFAULT;
11203 player->special_action_bored = ACTION_DEFAULT;
11204 player->special_action_sleeping = ACTION_DEFAULT;
11208 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
11210 boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
11211 int left = player_action & JOY_LEFT;
11212 int right = player_action & JOY_RIGHT;
11213 int up = player_action & JOY_UP;
11214 int down = player_action & JOY_DOWN;
11215 int button1 = player_action & JOY_BUTTON_1;
11216 int button2 = player_action & JOY_BUTTON_2;
11217 int dx = (left ? -1 : right ? 1 : 0);
11218 int dy = (up ? -1 : down ? 1 : 0);
11220 if (!player->active || tape.pausing)
11226 snapped = SnapField(player, dx, dy);
11230 dropped = DropElement(player);
11232 moved = MovePlayer(player, dx, dy);
11235 if (tape.single_step && tape.recording && !tape.pausing)
11237 if (button1 || (dropped && !moved))
11239 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
11240 SnapField(player, 0, 0); /* stop snapping */
11244 SetPlayerWaiting(player, FALSE);
11246 return player_action;
11250 /* no actions for this player (no input at player's configured device) */
11252 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
11253 SnapField(player, 0, 0);
11254 CheckGravityMovementWhenNotMoving(player);
11256 if (player->MovPos == 0)
11257 SetPlayerWaiting(player, TRUE);
11259 if (player->MovPos == 0) /* needed for tape.playing */
11260 player->is_moving = FALSE;
11262 player->is_dropping = FALSE;
11263 player->is_dropping_pressed = FALSE;
11264 player->drop_pressed_delay = 0;
11270 static void CheckLevelTime()
11274 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11276 if (level.native_em_level->lev->home == 0) /* all players at home */
11278 PlayerWins(local_player);
11280 AllPlayersGone = TRUE;
11282 level.native_em_level->lev->home = -1;
11285 if (level.native_em_level->ply[0]->alive == 0 &&
11286 level.native_em_level->ply[1]->alive == 0 &&
11287 level.native_em_level->ply[2]->alive == 0 &&
11288 level.native_em_level->ply[3]->alive == 0) /* all dead */
11289 AllPlayersGone = TRUE;
11292 if (TimeFrames >= FRAMES_PER_SECOND)
11297 for (i = 0; i < MAX_PLAYERS; i++)
11299 struct PlayerInfo *player = &stored_player[i];
11301 if (SHIELD_ON(player))
11303 player->shield_normal_time_left--;
11305 if (player->shield_deadly_time_left > 0)
11306 player->shield_deadly_time_left--;
11310 if (!local_player->LevelSolved && !level.use_step_counter)
11318 if (TimeLeft <= 10 && setup.time_limit)
11319 PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
11322 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
11324 DisplayGameControlValues();
11326 DrawGameValue_Time(TimeLeft);
11329 if (!TimeLeft && setup.time_limit)
11331 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11332 level.native_em_level->lev->killed_out_of_time = TRUE;
11334 for (i = 0; i < MAX_PLAYERS; i++)
11335 KillPlayer(&stored_player[i]);
11339 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
11341 game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
11343 DisplayGameControlValues();
11346 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
11347 DrawGameValue_Time(TimePlayed);
11350 level.native_em_level->lev->time =
11351 (level.time == 0 ? TimePlayed : TimeLeft);
11354 if (tape.recording || tape.playing)
11355 DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
11358 UpdateGameDoorValues();
11359 DrawGameDoorValues();
11362 void AdvanceFrameAndPlayerCounters(int player_nr)
11366 /* advance frame counters (global frame counter and time frame counter) */
11370 /* advance player counters (counters for move delay, move animation etc.) */
11371 for (i = 0; i < MAX_PLAYERS; i++)
11373 boolean advance_player_counters = (player_nr == -1 || player_nr == i);
11374 int move_delay_value = stored_player[i].move_delay_value;
11375 int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
11377 if (!advance_player_counters) /* not all players may be affected */
11380 #if USE_NEW_PLAYER_ANIM
11381 if (move_frames == 0) /* less than one move per game frame */
11383 int stepsize = TILEX / move_delay_value;
11384 int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
11385 int count = (stored_player[i].is_moving ?
11386 ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
11388 if (count % delay == 0)
11393 stored_player[i].Frame += move_frames;
11395 if (stored_player[i].MovPos != 0)
11396 stored_player[i].StepFrame += move_frames;
11398 if (stored_player[i].move_delay > 0)
11399 stored_player[i].move_delay--;
11401 /* due to bugs in previous versions, counter must count up, not down */
11402 if (stored_player[i].push_delay != -1)
11403 stored_player[i].push_delay++;
11405 if (stored_player[i].drop_delay > 0)
11406 stored_player[i].drop_delay--;
11408 if (stored_player[i].is_dropping_pressed)
11409 stored_player[i].drop_pressed_delay++;
11413 void StartGameActions(boolean init_network_game, boolean record_tape,
11416 unsigned long new_random_seed = InitRND(random_seed);
11419 TapeStartRecording(new_random_seed);
11421 #if defined(NETWORK_AVALIABLE)
11422 if (init_network_game)
11424 SendToServer_StartPlaying();
11435 static unsigned long game_frame_delay = 0;
11436 unsigned long game_frame_delay_value;
11437 byte *recorded_player_action;
11438 byte summarized_player_action = 0;
11439 byte tape_action[MAX_PLAYERS];
11442 /* detect endless loops, caused by custom element programming */
11443 if (recursion_loop_detected && recursion_loop_depth == 0)
11445 char *message = getStringCat3("Internal Error ! Element ",
11446 EL_NAME(recursion_loop_element),
11447 " caused endless loop ! Quit the game ?");
11449 Error(ERR_WARN, "element '%s' caused endless loop in game engine",
11450 EL_NAME(recursion_loop_element));
11452 RequestQuitGameExt(FALSE, level_editor_test_game, message);
11454 recursion_loop_detected = FALSE; /* if game should be continued */
11461 if (game.restart_level)
11462 StartGameActions(options.network, setup.autorecord, NEW_RANDOMIZE);
11464 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11466 if (level.native_em_level->lev->home == 0) /* all players at home */
11468 PlayerWins(local_player);
11470 AllPlayersGone = TRUE;
11472 level.native_em_level->lev->home = -1;
11475 if (level.native_em_level->ply[0]->alive == 0 &&
11476 level.native_em_level->ply[1]->alive == 0 &&
11477 level.native_em_level->ply[2]->alive == 0 &&
11478 level.native_em_level->ply[3]->alive == 0) /* all dead */
11479 AllPlayersGone = TRUE;
11482 if (local_player->LevelSolved && !local_player->LevelSolved_GameEnd)
11485 if (AllPlayersGone && !TAPE_IS_STOPPED(tape))
11488 if (game_status != GAME_MODE_PLAYING) /* status might have changed */
11491 game_frame_delay_value =
11492 (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
11494 if (tape.playing && tape.warp_forward && !tape.pausing)
11495 game_frame_delay_value = 0;
11497 /* ---------- main game synchronization point ---------- */
11499 WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11501 if (network_playing && !network_player_action_received)
11503 /* try to get network player actions in time */
11505 #if defined(NETWORK_AVALIABLE)
11506 /* last chance to get network player actions without main loop delay */
11507 HandleNetworking();
11510 /* game was quit by network peer */
11511 if (game_status != GAME_MODE_PLAYING)
11514 if (!network_player_action_received)
11515 return; /* failed to get network player actions in time */
11517 /* do not yet reset "network_player_action_received" (for tape.pausing) */
11523 /* at this point we know that we really continue executing the game */
11525 network_player_action_received = FALSE;
11527 /* when playing tape, read previously recorded player input from tape data */
11528 recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
11531 /* TapePlayAction() may return NULL when toggling to "pause before death" */
11536 if (tape.set_centered_player)
11538 game.centered_player_nr_next = tape.centered_player_nr_next;
11539 game.set_centered_player = TRUE;
11542 for (i = 0; i < MAX_PLAYERS; i++)
11544 summarized_player_action |= stored_player[i].action;
11546 if (!network_playing)
11547 stored_player[i].effective_action = stored_player[i].action;
11550 #if defined(NETWORK_AVALIABLE)
11551 if (network_playing)
11552 SendToServer_MovePlayer(summarized_player_action);
11555 if (!options.network && !setup.team_mode)
11556 local_player->effective_action = summarized_player_action;
11558 if (setup.team_mode && setup.input_on_focus && game.centered_player_nr != -1)
11560 for (i = 0; i < MAX_PLAYERS; i++)
11561 stored_player[i].effective_action =
11562 (i == game.centered_player_nr ? summarized_player_action : 0);
11565 if (recorded_player_action != NULL)
11566 for (i = 0; i < MAX_PLAYERS; i++)
11567 stored_player[i].effective_action = recorded_player_action[i];
11569 for (i = 0; i < MAX_PLAYERS; i++)
11571 tape_action[i] = stored_player[i].effective_action;
11573 /* (this can only happen in the R'n'D game engine) */
11574 if (tape.recording && tape_action[i] && !tape.player_participates[i])
11575 tape.player_participates[i] = TRUE; /* player just appeared from CE */
11578 /* only record actions from input devices, but not programmed actions */
11579 if (tape.recording)
11580 TapeRecordAction(tape_action);
11582 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11584 GameActions_EM_Main();
11592 void GameActions_EM_Main()
11594 byte effective_action[MAX_PLAYERS];
11595 boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11598 for (i = 0; i < MAX_PLAYERS; i++)
11599 effective_action[i] = stored_player[i].effective_action;
11601 GameActions_EM(effective_action, warp_mode);
11605 AdvanceFrameAndPlayerCounters(-1); /* advance counters for all players */
11608 void GameActions_RND()
11610 int magic_wall_x = 0, magic_wall_y = 0;
11611 int i, x, y, element, graphic;
11613 InitPlayfieldScanModeVars();
11615 #if USE_ONE_MORE_CHANGE_PER_FRAME
11616 if (game.engine_version >= VERSION_IDENT(3,2,0,7))
11618 SCAN_PLAYFIELD(x, y)
11620 ChangeCount[x][y] = 0;
11621 ChangeEvent[x][y] = -1;
11626 if (game.set_centered_player)
11628 boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
11630 /* switching to "all players" only possible if all players fit to screen */
11631 if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
11633 game.centered_player_nr_next = game.centered_player_nr;
11634 game.set_centered_player = FALSE;
11637 /* do not switch focus to non-existing (or non-active) player */
11638 if (game.centered_player_nr_next >= 0 &&
11639 !stored_player[game.centered_player_nr_next].active)
11641 game.centered_player_nr_next = game.centered_player_nr;
11642 game.set_centered_player = FALSE;
11646 if (game.set_centered_player &&
11647 ScreenMovPos == 0) /* screen currently aligned at tile position */
11651 if (game.centered_player_nr_next == -1)
11653 setScreenCenteredToAllPlayers(&sx, &sy);
11657 sx = stored_player[game.centered_player_nr_next].jx;
11658 sy = stored_player[game.centered_player_nr_next].jy;
11661 game.centered_player_nr = game.centered_player_nr_next;
11662 game.set_centered_player = FALSE;
11664 DrawRelocateScreen(0, 0, sx, sy, MV_NONE, TRUE, setup.quick_switch);
11665 DrawGameDoorValues();
11668 for (i = 0; i < MAX_PLAYERS; i++)
11670 int actual_player_action = stored_player[i].effective_action;
11673 /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
11674 - rnd_equinox_tetrachloride 048
11675 - rnd_equinox_tetrachloride_ii 096
11676 - rnd_emanuel_schmieg 002
11677 - doctor_sloan_ww 001, 020
11679 if (stored_player[i].MovPos == 0)
11680 CheckGravityMovement(&stored_player[i]);
11683 /* overwrite programmed action with tape action */
11684 if (stored_player[i].programmed_action)
11685 actual_player_action = stored_player[i].programmed_action;
11687 PlayerActions(&stored_player[i], actual_player_action);
11689 ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
11692 ScrollScreen(NULL, SCROLL_GO_ON);
11694 /* for backwards compatibility, the following code emulates a fixed bug that
11695 occured when pushing elements (causing elements that just made their last
11696 pushing step to already (if possible) make their first falling step in the
11697 same game frame, which is bad); this code is also needed to use the famous
11698 "spring push bug" which is used in older levels and might be wanted to be
11699 used also in newer levels, but in this case the buggy pushing code is only
11700 affecting the "spring" element and no other elements */
11702 if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
11704 for (i = 0; i < MAX_PLAYERS; i++)
11706 struct PlayerInfo *player = &stored_player[i];
11707 int x = player->jx;
11708 int y = player->jy;
11710 if (player->active && player->is_pushing && player->is_moving &&
11712 (game.engine_version < VERSION_IDENT(2,2,0,7) ||
11713 Feld[x][y] == EL_SPRING))
11715 ContinueMoving(x, y);
11717 /* continue moving after pushing (this is actually a bug) */
11718 if (!IS_MOVING(x, y))
11719 Stop[x][y] = FALSE;
11725 debug_print_timestamp(0, "start main loop profiling");
11728 SCAN_PLAYFIELD(x, y)
11730 ChangeCount[x][y] = 0;
11731 ChangeEvent[x][y] = -1;
11733 /* this must be handled before main playfield loop */
11734 if (Feld[x][y] == EL_PLAYER_IS_LEAVING)
11737 if (MovDelay[x][y] <= 0)
11741 #if USE_NEW_SNAP_DELAY
11742 if (Feld[x][y] == EL_ELEMENT_SNAPPING)
11745 if (MovDelay[x][y] <= 0)
11748 DrawLevelField(x, y);
11750 TestIfElementTouchesCustomElement(x, y); /* for empty space */
11756 if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
11758 printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
11759 printf("GameActions(): This should never happen!\n");
11761 ChangePage[x][y] = -1;
11765 Stop[x][y] = FALSE;
11766 if (WasJustMoving[x][y] > 0)
11767 WasJustMoving[x][y]--;
11768 if (WasJustFalling[x][y] > 0)
11769 WasJustFalling[x][y]--;
11770 if (CheckCollision[x][y] > 0)
11771 CheckCollision[x][y]--;
11772 if (CheckImpact[x][y] > 0)
11773 CheckImpact[x][y]--;
11777 /* reset finished pushing action (not done in ContinueMoving() to allow
11778 continuous pushing animation for elements with zero push delay) */
11779 if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
11781 ResetGfxAnimation(x, y);
11782 DrawLevelField(x, y);
11786 if (IS_BLOCKED(x, y))
11790 Blocked2Moving(x, y, &oldx, &oldy);
11791 if (!IS_MOVING(oldx, oldy))
11793 printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
11794 printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
11795 printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
11796 printf("GameActions(): This should never happen!\n");
11803 debug_print_timestamp(0, "- time for pre-main loop:");
11806 #if 0 // -------------------- !!! TEST ONLY !!! --------------------
11807 SCAN_PLAYFIELD(x, y)
11809 element = Feld[x][y];
11810 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11815 int element2 = element;
11816 int graphic2 = graphic;
11818 int element2 = Feld[x][y];
11819 int graphic2 = el_act_dir2img(element2, GfxAction[x][y], GfxDir[x][y]);
11821 int last_gfx_frame = GfxFrame[x][y];
11823 if (graphic_info[graphic2].anim_global_sync)
11824 GfxFrame[x][y] = FrameCounter;
11825 else if (ANIM_MODE(graphic2) == ANIM_CE_VALUE)
11826 GfxFrame[x][y] = CustomValue[x][y];
11827 else if (ANIM_MODE(graphic2) == ANIM_CE_SCORE)
11828 GfxFrame[x][y] = element_info[element2].collect_score;
11829 else if (ANIM_MODE(graphic2) == ANIM_CE_DELAY)
11830 GfxFrame[x][y] = ChangeDelay[x][y];
11832 if (redraw && GfxFrame[x][y] != last_gfx_frame)
11833 DrawLevelGraphicAnimation(x, y, graphic2);
11836 ResetGfxFrame(x, y, TRUE);
11840 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
11841 IS_NEXT_FRAME(GfxFrame[x][y], graphic))
11842 ResetRandomAnimationValue(x, y);
11846 SetRandomAnimationValue(x, y);
11850 PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
11853 #endif // -------------------- !!! TEST ONLY !!! --------------------
11856 debug_print_timestamp(0, "- time for TEST loop: -->");
11859 SCAN_PLAYFIELD(x, y)
11861 element = Feld[x][y];
11862 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11864 ResetGfxFrame(x, y, TRUE);
11866 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
11867 IS_NEXT_FRAME(GfxFrame[x][y], graphic))
11868 ResetRandomAnimationValue(x, y);
11870 SetRandomAnimationValue(x, y);
11872 PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
11874 if (IS_INACTIVE(element))
11876 if (IS_ANIMATED(graphic))
11877 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11882 /* this may take place after moving, so 'element' may have changed */
11883 if (IS_CHANGING(x, y) &&
11884 (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
11886 int page = element_info[element].event_page_nr[CE_DELAY];
11889 HandleElementChange(x, y, page);
11891 if (CAN_CHANGE(element))
11892 HandleElementChange(x, y, page);
11894 if (HAS_ACTION(element))
11895 ExecuteCustomElementAction(x, y, element, page);
11898 element = Feld[x][y];
11899 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11902 #if 0 // ---------------------------------------------------------------------
11904 if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
11908 element = Feld[x][y];
11909 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11911 if (IS_ANIMATED(graphic) &&
11912 !IS_MOVING(x, y) &&
11914 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11916 if (IS_GEM(element) || element == EL_SP_INFOTRON)
11917 DrawTwinkleOnField(x, y);
11919 else if (IS_MOVING(x, y))
11920 ContinueMoving(x, y);
11927 case EL_EM_EXIT_OPEN:
11928 case EL_SP_EXIT_OPEN:
11929 case EL_STEEL_EXIT_OPEN:
11930 case EL_EM_STEEL_EXIT_OPEN:
11931 case EL_SP_TERMINAL:
11932 case EL_SP_TERMINAL_ACTIVE:
11933 case EL_EXTRA_TIME:
11934 case EL_SHIELD_NORMAL:
11935 case EL_SHIELD_DEADLY:
11936 if (IS_ANIMATED(graphic))
11937 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11940 case EL_DYNAMITE_ACTIVE:
11941 case EL_EM_DYNAMITE_ACTIVE:
11942 case EL_DYNABOMB_PLAYER_1_ACTIVE:
11943 case EL_DYNABOMB_PLAYER_2_ACTIVE:
11944 case EL_DYNABOMB_PLAYER_3_ACTIVE:
11945 case EL_DYNABOMB_PLAYER_4_ACTIVE:
11946 case EL_SP_DISK_RED_ACTIVE:
11947 CheckDynamite(x, y);
11950 case EL_AMOEBA_GROWING:
11951 AmoebeWaechst(x, y);
11954 case EL_AMOEBA_SHRINKING:
11955 AmoebaDisappearing(x, y);
11958 #if !USE_NEW_AMOEBA_CODE
11959 case EL_AMOEBA_WET:
11960 case EL_AMOEBA_DRY:
11961 case EL_AMOEBA_FULL:
11963 case EL_EMC_DRIPPER:
11964 AmoebeAbleger(x, y);
11968 case EL_GAME_OF_LIFE:
11973 case EL_EXIT_CLOSED:
11977 case EL_EM_EXIT_CLOSED:
11981 case EL_STEEL_EXIT_CLOSED:
11982 CheckExitSteel(x, y);
11985 case EL_EM_STEEL_EXIT_CLOSED:
11986 CheckExitSteelEM(x, y);
11989 case EL_SP_EXIT_CLOSED:
11993 case EL_EXPANDABLE_WALL_GROWING:
11994 case EL_EXPANDABLE_STEELWALL_GROWING:
11995 MauerWaechst(x, y);
11998 case EL_EXPANDABLE_WALL:
11999 case EL_EXPANDABLE_WALL_HORIZONTAL:
12000 case EL_EXPANDABLE_WALL_VERTICAL:
12001 case EL_EXPANDABLE_WALL_ANY:
12002 case EL_BD_EXPANDABLE_WALL:
12003 MauerAbleger(x, y);
12006 case EL_EXPANDABLE_STEELWALL_HORIZONTAL:
12007 case EL_EXPANDABLE_STEELWALL_VERTICAL:
12008 case EL_EXPANDABLE_STEELWALL_ANY:
12009 MauerAblegerStahl(x, y);
12013 CheckForDragon(x, y);
12019 case EL_ELEMENT_SNAPPING:
12020 case EL_DIAGONAL_SHRINKING:
12021 case EL_DIAGONAL_GROWING:
12024 el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
12026 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12031 if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
12032 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12037 #else // ---------------------------------------------------------------------
12039 if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
12043 element = Feld[x][y];
12044 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12046 if (IS_ANIMATED(graphic) &&
12047 !IS_MOVING(x, y) &&
12049 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12051 if (IS_GEM(element) || element == EL_SP_INFOTRON)
12052 DrawTwinkleOnField(x, y);
12054 else if ((element == EL_ACID ||
12055 element == EL_EXIT_OPEN ||
12056 element == EL_EM_EXIT_OPEN ||
12057 element == EL_SP_EXIT_OPEN ||
12058 element == EL_STEEL_EXIT_OPEN ||
12059 element == EL_EM_STEEL_EXIT_OPEN ||
12060 element == EL_SP_TERMINAL ||
12061 element == EL_SP_TERMINAL_ACTIVE ||
12062 element == EL_EXTRA_TIME ||
12063 element == EL_SHIELD_NORMAL ||
12064 element == EL_SHIELD_DEADLY) &&
12065 IS_ANIMATED(graphic))
12066 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12067 else if (IS_MOVING(x, y))
12068 ContinueMoving(x, y);
12069 else if (IS_ACTIVE_BOMB(element))
12070 CheckDynamite(x, y);
12071 else if (element == EL_AMOEBA_GROWING)
12072 AmoebeWaechst(x, y);
12073 else if (element == EL_AMOEBA_SHRINKING)
12074 AmoebaDisappearing(x, y);
12076 #if !USE_NEW_AMOEBA_CODE
12077 else if (IS_AMOEBALIVE(element))
12078 AmoebeAbleger(x, y);
12081 else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
12083 else if (element == EL_EXIT_CLOSED)
12085 else if (element == EL_EM_EXIT_CLOSED)
12087 else if (element == EL_STEEL_EXIT_CLOSED)
12088 CheckExitSteel(x, y);
12089 else if (element == EL_EM_STEEL_EXIT_CLOSED)
12090 CheckExitSteelEM(x, y);
12091 else if (element == EL_SP_EXIT_CLOSED)
12093 else if (element == EL_EXPANDABLE_WALL_GROWING ||
12094 element == EL_EXPANDABLE_STEELWALL_GROWING)
12095 MauerWaechst(x, y);
12096 else if (element == EL_EXPANDABLE_WALL ||
12097 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
12098 element == EL_EXPANDABLE_WALL_VERTICAL ||
12099 element == EL_EXPANDABLE_WALL_ANY ||
12100 element == EL_BD_EXPANDABLE_WALL)
12101 MauerAbleger(x, y);
12102 else if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
12103 element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
12104 element == EL_EXPANDABLE_STEELWALL_ANY)
12105 MauerAblegerStahl(x, y);
12106 else if (element == EL_FLAMES)
12107 CheckForDragon(x, y);
12108 else if (element == EL_EXPLOSION)
12109 ; /* drawing of correct explosion animation is handled separately */
12110 else if (element == EL_ELEMENT_SNAPPING ||
12111 element == EL_DIAGONAL_SHRINKING ||
12112 element == EL_DIAGONAL_GROWING)
12114 graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
12116 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12118 else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
12119 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12121 #endif // ---------------------------------------------------------------------
12123 if (IS_BELT_ACTIVE(element))
12124 PlayLevelSoundAction(x, y, ACTION_ACTIVE);
12126 if (game.magic_wall_active)
12128 int jx = local_player->jx, jy = local_player->jy;
12130 /* play the element sound at the position nearest to the player */
12131 if ((element == EL_MAGIC_WALL_FULL ||
12132 element == EL_MAGIC_WALL_ACTIVE ||
12133 element == EL_MAGIC_WALL_EMPTYING ||
12134 element == EL_BD_MAGIC_WALL_FULL ||
12135 element == EL_BD_MAGIC_WALL_ACTIVE ||
12136 element == EL_BD_MAGIC_WALL_EMPTYING ||
12137 element == EL_DC_MAGIC_WALL_FULL ||
12138 element == EL_DC_MAGIC_WALL_ACTIVE ||
12139 element == EL_DC_MAGIC_WALL_EMPTYING) &&
12140 ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
12149 debug_print_timestamp(0, "- time for MAIN loop: -->");
12152 #if USE_NEW_AMOEBA_CODE
12153 /* new experimental amoeba growth stuff */
12154 if (!(FrameCounter % 8))
12156 static unsigned long random = 1684108901;
12158 for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
12160 x = RND(lev_fieldx);
12161 y = RND(lev_fieldy);
12162 element = Feld[x][y];
12164 if (!IS_PLAYER(x,y) &&
12165 (element == EL_EMPTY ||
12166 CAN_GROW_INTO(element) ||
12167 element == EL_QUICKSAND_EMPTY ||
12168 element == EL_QUICKSAND_FAST_EMPTY ||
12169 element == EL_ACID_SPLASH_LEFT ||
12170 element == EL_ACID_SPLASH_RIGHT))
12172 if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
12173 (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
12174 (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
12175 (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
12176 Feld[x][y] = EL_AMOEBA_DROP;
12179 random = random * 129 + 1;
12185 if (game.explosions_delayed)
12188 game.explosions_delayed = FALSE;
12190 SCAN_PLAYFIELD(x, y)
12192 element = Feld[x][y];
12194 if (ExplodeField[x][y])
12195 Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
12196 else if (element == EL_EXPLOSION)
12197 Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
12199 ExplodeField[x][y] = EX_TYPE_NONE;
12202 game.explosions_delayed = TRUE;
12205 if (game.magic_wall_active)
12207 if (!(game.magic_wall_time_left % 4))
12209 int element = Feld[magic_wall_x][magic_wall_y];
12211 if (element == EL_BD_MAGIC_WALL_FULL ||
12212 element == EL_BD_MAGIC_WALL_ACTIVE ||
12213 element == EL_BD_MAGIC_WALL_EMPTYING)
12214 PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
12215 else if (element == EL_DC_MAGIC_WALL_FULL ||
12216 element == EL_DC_MAGIC_WALL_ACTIVE ||
12217 element == EL_DC_MAGIC_WALL_EMPTYING)
12218 PlayLevelSound(magic_wall_x, magic_wall_y, SND_DC_MAGIC_WALL_ACTIVE);
12220 PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
12223 if (game.magic_wall_time_left > 0)
12225 game.magic_wall_time_left--;
12227 if (!game.magic_wall_time_left)
12229 SCAN_PLAYFIELD(x, y)
12231 element = Feld[x][y];
12233 if (element == EL_MAGIC_WALL_ACTIVE ||
12234 element == EL_MAGIC_WALL_FULL)
12236 Feld[x][y] = EL_MAGIC_WALL_DEAD;
12237 DrawLevelField(x, y);
12239 else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
12240 element == EL_BD_MAGIC_WALL_FULL)
12242 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
12243 DrawLevelField(x, y);
12245 else if (element == EL_DC_MAGIC_WALL_ACTIVE ||
12246 element == EL_DC_MAGIC_WALL_FULL)
12248 Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
12249 DrawLevelField(x, y);
12253 game.magic_wall_active = FALSE;
12258 if (game.light_time_left > 0)
12260 game.light_time_left--;
12262 if (game.light_time_left == 0)
12263 RedrawAllLightSwitchesAndInvisibleElements();
12266 if (game.timegate_time_left > 0)
12268 game.timegate_time_left--;
12270 if (game.timegate_time_left == 0)
12271 CloseAllOpenTimegates();
12274 if (game.lenses_time_left > 0)
12276 game.lenses_time_left--;
12278 if (game.lenses_time_left == 0)
12279 RedrawAllInvisibleElementsForLenses();
12282 if (game.magnify_time_left > 0)
12284 game.magnify_time_left--;
12286 if (game.magnify_time_left == 0)
12287 RedrawAllInvisibleElementsForMagnifier();
12290 for (i = 0; i < MAX_PLAYERS; i++)
12292 struct PlayerInfo *player = &stored_player[i];
12294 if (SHIELD_ON(player))
12296 if (player->shield_deadly_time_left)
12297 PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
12298 else if (player->shield_normal_time_left)
12299 PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
12306 PlayAllPlayersSound();
12308 if (options.debug) /* calculate frames per second */
12310 static unsigned long fps_counter = 0;
12311 static int fps_frames = 0;
12312 unsigned long fps_delay_ms = Counter() - fps_counter;
12316 if (fps_delay_ms >= 500) /* calculate fps every 0.5 seconds */
12318 global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
12321 fps_counter = Counter();
12324 redraw_mask |= REDRAW_FPS;
12327 AdvanceFrameAndPlayerCounters(-1); /* advance counters for all players */
12329 if (local_player->show_envelope != 0 && local_player->MovPos == 0)
12331 ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
12333 local_player->show_envelope = 0;
12337 debug_print_timestamp(0, "stop main loop profiling ");
12338 printf("----------------------------------------------------------\n");
12341 /* use random number generator in every frame to make it less predictable */
12342 if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12346 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
12348 int min_x = x, min_y = y, max_x = x, max_y = y;
12351 for (i = 0; i < MAX_PLAYERS; i++)
12353 int jx = stored_player[i].jx, jy = stored_player[i].jy;
12355 if (!stored_player[i].active || &stored_player[i] == player)
12358 min_x = MIN(min_x, jx);
12359 min_y = MIN(min_y, jy);
12360 max_x = MAX(max_x, jx);
12361 max_y = MAX(max_y, jy);
12364 return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
12367 static boolean AllPlayersInVisibleScreen()
12371 for (i = 0; i < MAX_PLAYERS; i++)
12373 int jx = stored_player[i].jx, jy = stored_player[i].jy;
12375 if (!stored_player[i].active)
12378 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12385 void ScrollLevel(int dx, int dy)
12388 static Bitmap *bitmap_db_field2 = NULL;
12389 int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
12396 /* !!! THIS IS APPARENTLY WRONG FOR PLAYER RELOCATION !!! */
12397 /* only horizontal XOR vertical scroll direction allowed */
12398 if ((dx == 0 && dy == 0) || (dx != 0 && dy != 0))
12403 if (bitmap_db_field2 == NULL)
12404 bitmap_db_field2 = CreateBitmap(FXSIZE, FYSIZE, DEFAULT_DEPTH);
12406 /* needed when blitting directly to same bitmap -- should not be needed with
12407 recent SDL libraries, but apparently does not work in 1.2.11 directly */
12408 BlitBitmap(drawto_field, bitmap_db_field2,
12409 FX + TILEX * (dx == -1) - softscroll_offset,
12410 FY + TILEY * (dy == -1) - softscroll_offset,
12411 SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
12412 SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
12413 FX + TILEX * (dx == 1) - softscroll_offset,
12414 FY + TILEY * (dy == 1) - softscroll_offset);
12415 BlitBitmap(bitmap_db_field2, drawto_field,
12416 FX + TILEX * (dx == 1) - softscroll_offset,
12417 FY + TILEY * (dy == 1) - softscroll_offset,
12418 SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
12419 SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
12420 FX + TILEX * (dx == 1) - softscroll_offset,
12421 FY + TILEY * (dy == 1) - softscroll_offset);
12426 /* !!! DOES NOT WORK FOR DIAGONAL PLAYER RELOCATION !!! */
12427 int xsize = (BX2 - BX1 + 1);
12428 int ysize = (BY2 - BY1 + 1);
12429 int start = (dx != 0 ? (dx == -1 ? BX1 : BX2) : (dy == -1 ? BY1 : BY2));
12430 int end = (dx != 0 ? (dx == -1 ? BX2 : BX1) : (dy == -1 ? BY2 : BY1));
12431 int step = (start < end ? +1 : -1);
12433 for (i = start; i != end; i += step)
12435 BlitBitmap(drawto_field, drawto_field,
12436 FX + TILEX * (dx != 0 ? i + step : 0),
12437 FY + TILEY * (dy != 0 ? i + step : 0),
12438 TILEX * (dx != 0 ? 1 : xsize),
12439 TILEY * (dy != 0 ? 1 : ysize),
12440 FX + TILEX * (dx != 0 ? i : 0),
12441 FY + TILEY * (dy != 0 ? i : 0));
12446 int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
12448 BlitBitmap(drawto_field, drawto_field,
12449 FX + TILEX * (dx == -1) - softscroll_offset,
12450 FY + TILEY * (dy == -1) - softscroll_offset,
12451 SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
12452 SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
12453 FX + TILEX * (dx == 1) - softscroll_offset,
12454 FY + TILEY * (dy == 1) - softscroll_offset);
12460 x = (dx == 1 ? BX1 : BX2);
12461 for (y = BY1; y <= BY2; y++)
12462 DrawScreenField(x, y);
12467 y = (dy == 1 ? BY1 : BY2);
12468 for (x = BX1; x <= BX2; x++)
12469 DrawScreenField(x, y);
12472 redraw_mask |= REDRAW_FIELD;
12475 static boolean canFallDown(struct PlayerInfo *player)
12477 int jx = player->jx, jy = player->jy;
12479 return (IN_LEV_FIELD(jx, jy + 1) &&
12480 (IS_FREE(jx, jy + 1) ||
12481 (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
12482 IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
12483 !IS_WALKABLE_INSIDE(Feld[jx][jy]));
12486 static boolean canPassField(int x, int y, int move_dir)
12488 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12489 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12490 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
12491 int nextx = x + dx;
12492 int nexty = y + dy;
12493 int element = Feld[x][y];
12495 return (IS_PASSABLE_FROM(element, opposite_dir) &&
12496 !CAN_MOVE(element) &&
12497 IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
12498 IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
12499 (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
12502 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
12504 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12505 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12506 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
12510 return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
12511 IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
12512 (IS_DIGGABLE(Feld[newx][newy]) ||
12513 IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
12514 canPassField(newx, newy, move_dir)));
12517 static void CheckGravityMovement(struct PlayerInfo *player)
12519 #if USE_PLAYER_GRAVITY
12520 if (player->gravity && !player->programmed_action)
12522 if (game.gravity && !player->programmed_action)
12525 int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
12526 int move_dir_vertical = player->effective_action & MV_VERTICAL;
12527 boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
12528 int jx = player->jx, jy = player->jy;
12529 boolean player_is_moving_to_valid_field =
12530 (!player_is_snapping &&
12531 (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
12532 canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
12533 boolean player_can_fall_down = canFallDown(player);
12535 if (player_can_fall_down &&
12536 !player_is_moving_to_valid_field)
12537 player->programmed_action = MV_DOWN;
12541 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
12543 return CheckGravityMovement(player);
12545 #if USE_PLAYER_GRAVITY
12546 if (player->gravity && !player->programmed_action)
12548 if (game.gravity && !player->programmed_action)
12551 int jx = player->jx, jy = player->jy;
12552 boolean field_under_player_is_free =
12553 (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
12554 boolean player_is_standing_on_valid_field =
12555 (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
12556 (IS_WALKABLE(Feld[jx][jy]) &&
12557 !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
12559 if (field_under_player_is_free && !player_is_standing_on_valid_field)
12560 player->programmed_action = MV_DOWN;
12565 MovePlayerOneStep()
12566 -----------------------------------------------------------------------------
12567 dx, dy: direction (non-diagonal) to try to move the player to
12568 real_dx, real_dy: direction as read from input device (can be diagonal)
12571 boolean MovePlayerOneStep(struct PlayerInfo *player,
12572 int dx, int dy, int real_dx, int real_dy)
12574 int jx = player->jx, jy = player->jy;
12575 int new_jx = jx + dx, new_jy = jy + dy;
12576 #if !USE_FIXED_DONT_RUN_INTO
12580 boolean player_can_move = !player->cannot_move;
12582 if (!player->active || (!dx && !dy))
12583 return MP_NO_ACTION;
12585 player->MovDir = (dx < 0 ? MV_LEFT :
12586 dx > 0 ? MV_RIGHT :
12588 dy > 0 ? MV_DOWN : MV_NONE);
12590 if (!IN_LEV_FIELD(new_jx, new_jy))
12591 return MP_NO_ACTION;
12593 if (!player_can_move)
12595 if (player->MovPos == 0)
12597 player->is_moving = FALSE;
12598 player->is_digging = FALSE;
12599 player->is_collecting = FALSE;
12600 player->is_snapping = FALSE;
12601 player->is_pushing = FALSE;
12606 if (!options.network && game.centered_player_nr == -1 &&
12607 !AllPlayersInSight(player, new_jx, new_jy))
12608 return MP_NO_ACTION;
12610 if (!options.network && !AllPlayersInSight(player, new_jx, new_jy))
12611 return MP_NO_ACTION;
12614 #if !USE_FIXED_DONT_RUN_INTO
12615 element = MovingOrBlocked2ElementIfNotLeaving(new_jx, new_jy);
12617 /* (moved to DigField()) */
12618 if (player_can_move && DONT_RUN_INTO(element))
12620 if (element == EL_ACID && dx == 0 && dy == 1)
12622 SplashAcid(new_jx, new_jy);
12623 Feld[jx][jy] = EL_PLAYER_1;
12624 InitMovingField(jx, jy, MV_DOWN);
12625 Store[jx][jy] = EL_ACID;
12626 ContinueMoving(jx, jy);
12627 BuryPlayer(player);
12630 TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
12636 can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
12637 if (can_move != MP_MOVING)
12640 /* check if DigField() has caused relocation of the player */
12641 if (player->jx != jx || player->jy != jy)
12642 return MP_NO_ACTION; /* <-- !!! CHECK THIS [-> MP_ACTION ?] !!! */
12644 StorePlayer[jx][jy] = 0;
12645 player->last_jx = jx;
12646 player->last_jy = jy;
12647 player->jx = new_jx;
12648 player->jy = new_jy;
12649 StorePlayer[new_jx][new_jy] = player->element_nr;
12651 if (player->move_delay_value_next != -1)
12653 player->move_delay_value = player->move_delay_value_next;
12654 player->move_delay_value_next = -1;
12658 (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
12660 player->step_counter++;
12662 PlayerVisit[jx][jy] = FrameCounter;
12664 #if USE_UFAST_PLAYER_EXIT_BUGFIX
12665 player->is_moving = TRUE;
12669 /* should better be called in MovePlayer(), but this breaks some tapes */
12670 ScrollPlayer(player, SCROLL_INIT);
12676 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
12678 int jx = player->jx, jy = player->jy;
12679 int old_jx = jx, old_jy = jy;
12680 int moved = MP_NO_ACTION;
12682 if (!player->active)
12687 if (player->MovPos == 0)
12689 player->is_moving = FALSE;
12690 player->is_digging = FALSE;
12691 player->is_collecting = FALSE;
12692 player->is_snapping = FALSE;
12693 player->is_pushing = FALSE;
12699 if (player->move_delay > 0)
12702 player->move_delay = -1; /* set to "uninitialized" value */
12704 /* store if player is automatically moved to next field */
12705 player->is_auto_moving = (player->programmed_action != MV_NONE);
12707 /* remove the last programmed player action */
12708 player->programmed_action = 0;
12710 if (player->MovPos)
12712 /* should only happen if pre-1.2 tape recordings are played */
12713 /* this is only for backward compatibility */
12715 int original_move_delay_value = player->move_delay_value;
12718 printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%ld]\n",
12722 /* scroll remaining steps with finest movement resolution */
12723 player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
12725 while (player->MovPos)
12727 ScrollPlayer(player, SCROLL_GO_ON);
12728 ScrollScreen(NULL, SCROLL_GO_ON);
12730 AdvanceFrameAndPlayerCounters(player->index_nr);
12736 player->move_delay_value = original_move_delay_value;
12739 player->is_active = FALSE;
12741 if (player->last_move_dir & MV_HORIZONTAL)
12743 if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
12744 moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
12748 if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
12749 moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
12752 #if USE_FIXED_BORDER_RUNNING_GFX
12753 if (!moved && !player->is_active)
12755 player->is_moving = FALSE;
12756 player->is_digging = FALSE;
12757 player->is_collecting = FALSE;
12758 player->is_snapping = FALSE;
12759 player->is_pushing = FALSE;
12767 if (moved & MP_MOVING && !ScreenMovPos &&
12768 (player->index_nr == game.centered_player_nr ||
12769 game.centered_player_nr == -1))
12771 if (moved & MP_MOVING && !ScreenMovPos &&
12772 (player == local_player || !options.network))
12775 int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
12776 int offset = game.scroll_delay_value;
12778 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12780 /* actual player has left the screen -- scroll in that direction */
12781 if (jx != old_jx) /* player has moved horizontally */
12782 scroll_x += (jx - old_jx);
12783 else /* player has moved vertically */
12784 scroll_y += (jy - old_jy);
12788 if (jx != old_jx) /* player has moved horizontally */
12790 if ((player->MovDir == MV_LEFT && scroll_x > jx - MIDPOSX + offset) ||
12791 (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
12792 scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
12794 /* don't scroll over playfield boundaries */
12795 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
12796 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
12798 /* don't scroll more than one field at a time */
12799 scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
12801 /* don't scroll against the player's moving direction */
12802 if ((player->MovDir == MV_LEFT && scroll_x > old_scroll_x) ||
12803 (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
12804 scroll_x = old_scroll_x;
12806 else /* player has moved vertically */
12808 if ((player->MovDir == MV_UP && scroll_y > jy - MIDPOSY + offset) ||
12809 (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
12810 scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
12812 /* don't scroll over playfield boundaries */
12813 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
12814 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
12816 /* don't scroll more than one field at a time */
12817 scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
12819 /* don't scroll against the player's moving direction */
12820 if ((player->MovDir == MV_UP && scroll_y > old_scroll_y) ||
12821 (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
12822 scroll_y = old_scroll_y;
12826 if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
12829 if (!options.network && game.centered_player_nr == -1 &&
12830 !AllPlayersInVisibleScreen())
12832 scroll_x = old_scroll_x;
12833 scroll_y = old_scroll_y;
12837 if (!options.network && !AllPlayersInVisibleScreen())
12839 scroll_x = old_scroll_x;
12840 scroll_y = old_scroll_y;
12845 ScrollScreen(player, SCROLL_INIT);
12846 ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
12851 player->StepFrame = 0;
12853 if (moved & MP_MOVING)
12855 if (old_jx != jx && old_jy == jy)
12856 player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
12857 else if (old_jx == jx && old_jy != jy)
12858 player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
12860 DrawLevelField(jx, jy); /* for "crumbled sand" */
12862 player->last_move_dir = player->MovDir;
12863 player->is_moving = TRUE;
12864 player->is_snapping = FALSE;
12865 player->is_switching = FALSE;
12866 player->is_dropping = FALSE;
12867 player->is_dropping_pressed = FALSE;
12868 player->drop_pressed_delay = 0;
12871 /* should better be called here than above, but this breaks some tapes */
12872 ScrollPlayer(player, SCROLL_INIT);
12877 CheckGravityMovementWhenNotMoving(player);
12879 player->is_moving = FALSE;
12881 /* at this point, the player is allowed to move, but cannot move right now
12882 (e.g. because of something blocking the way) -- ensure that the player
12883 is also allowed to move in the next frame (in old versions before 3.1.1,
12884 the player was forced to wait again for eight frames before next try) */
12886 if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12887 player->move_delay = 0; /* allow direct movement in the next frame */
12890 if (player->move_delay == -1) /* not yet initialized by DigField() */
12891 player->move_delay = player->move_delay_value;
12893 if (game.engine_version < VERSION_IDENT(3,0,7,0))
12895 TestIfPlayerTouchesBadThing(jx, jy);
12896 TestIfPlayerTouchesCustomElement(jx, jy);
12899 if (!player->active)
12900 RemovePlayer(player);
12905 void ScrollPlayer(struct PlayerInfo *player, int mode)
12907 int jx = player->jx, jy = player->jy;
12908 int last_jx = player->last_jx, last_jy = player->last_jy;
12909 int move_stepsize = TILEX / player->move_delay_value;
12911 #if USE_NEW_PLAYER_SPEED
12912 if (!player->active)
12915 if (player->MovPos == 0 && mode == SCROLL_GO_ON) /* player not moving */
12918 if (!player->active || player->MovPos == 0)
12922 if (mode == SCROLL_INIT)
12924 player->actual_frame_counter = FrameCounter;
12925 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
12927 if ((player->block_last_field || player->block_delay_adjustment > 0) &&
12928 Feld[last_jx][last_jy] == EL_EMPTY)
12930 int last_field_block_delay = 0; /* start with no blocking at all */
12931 int block_delay_adjustment = player->block_delay_adjustment;
12933 /* if player blocks last field, add delay for exactly one move */
12934 if (player->block_last_field)
12936 last_field_block_delay += player->move_delay_value;
12938 /* when blocking enabled, prevent moving up despite gravity */
12939 #if USE_PLAYER_GRAVITY
12940 if (player->gravity && player->MovDir == MV_UP)
12941 block_delay_adjustment = -1;
12943 if (game.gravity && player->MovDir == MV_UP)
12944 block_delay_adjustment = -1;
12948 /* add block delay adjustment (also possible when not blocking) */
12949 last_field_block_delay += block_delay_adjustment;
12951 Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
12952 MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
12955 #if USE_NEW_PLAYER_SPEED
12956 if (player->MovPos != 0) /* player has not yet reached destination */
12962 else if (!FrameReached(&player->actual_frame_counter, 1))
12965 #if USE_NEW_PLAYER_SPEED
12966 if (player->MovPos != 0)
12968 player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
12969 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
12971 /* before DrawPlayer() to draw correct player graphic for this case */
12972 if (player->MovPos == 0)
12973 CheckGravityMovement(player);
12976 player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
12977 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
12979 /* before DrawPlayer() to draw correct player graphic for this case */
12980 if (player->MovPos == 0)
12981 CheckGravityMovement(player);
12984 if (player->MovPos == 0) /* player reached destination field */
12986 if (player->move_delay_reset_counter > 0)
12988 player->move_delay_reset_counter--;
12990 if (player->move_delay_reset_counter == 0)
12992 /* continue with normal speed after quickly moving through gate */
12993 HALVE_PLAYER_SPEED(player);
12995 /* be able to make the next move without delay */
12996 player->move_delay = 0;
13000 player->last_jx = jx;
13001 player->last_jy = jy;
13003 if (Feld[jx][jy] == EL_EXIT_OPEN ||
13004 Feld[jx][jy] == EL_EM_EXIT_OPEN ||
13005 Feld[jx][jy] == EL_STEEL_EXIT_OPEN ||
13006 Feld[jx][jy] == EL_EM_STEEL_EXIT_OPEN ||
13007 Feld[jx][jy] == EL_SP_EXIT_OPEN ||
13008 Feld[jx][jy] == EL_SP_EXIT_OPENING) /* <-- special case */
13010 DrawPlayer(player); /* needed here only to cleanup last field */
13011 RemovePlayer(player);
13013 if (local_player->friends_still_needed == 0 ||
13014 IS_SP_ELEMENT(Feld[jx][jy]))
13015 PlayerWins(player);
13018 /* this breaks one level: "machine", level 000 */
13020 int move_direction = player->MovDir;
13021 int enter_side = MV_DIR_OPPOSITE(move_direction);
13022 int leave_side = move_direction;
13023 int old_jx = last_jx;
13024 int old_jy = last_jy;
13025 int old_element = Feld[old_jx][old_jy];
13026 int new_element = Feld[jx][jy];
13028 if (IS_CUSTOM_ELEMENT(old_element))
13029 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
13031 player->index_bit, leave_side);
13033 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
13034 CE_PLAYER_LEAVES_X,
13035 player->index_bit, leave_side);
13037 if (IS_CUSTOM_ELEMENT(new_element))
13038 CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
13039 player->index_bit, enter_side);
13041 CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
13042 CE_PLAYER_ENTERS_X,
13043 player->index_bit, enter_side);
13045 CheckTriggeredElementChangeBySide(jx, jy, player->element_nr,
13046 CE_MOVE_OF_X, move_direction);
13049 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13051 TestIfPlayerTouchesBadThing(jx, jy);
13052 TestIfPlayerTouchesCustomElement(jx, jy);
13054 /* needed because pushed element has not yet reached its destination,
13055 so it would trigger a change event at its previous field location */
13056 if (!player->is_pushing)
13057 TestIfElementTouchesCustomElement(jx, jy); /* for empty space */
13059 if (!player->active)
13060 RemovePlayer(player);
13063 if (!local_player->LevelSolved && level.use_step_counter)
13073 if (TimeLeft <= 10 && setup.time_limit)
13074 PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
13077 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
13079 DisplayGameControlValues();
13081 DrawGameValue_Time(TimeLeft);
13084 if (!TimeLeft && setup.time_limit)
13085 for (i = 0; i < MAX_PLAYERS; i++)
13086 KillPlayer(&stored_player[i]);
13089 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
13091 game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
13093 DisplayGameControlValues();
13096 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
13097 DrawGameValue_Time(TimePlayed);
13101 if (tape.single_step && tape.recording && !tape.pausing &&
13102 !player->programmed_action)
13103 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
13107 void ScrollScreen(struct PlayerInfo *player, int mode)
13109 static unsigned long screen_frame_counter = 0;
13111 if (mode == SCROLL_INIT)
13113 /* set scrolling step size according to actual player's moving speed */
13114 ScrollStepSize = TILEX / player->move_delay_value;
13116 screen_frame_counter = FrameCounter;
13117 ScreenMovDir = player->MovDir;
13118 ScreenMovPos = player->MovPos;
13119 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
13122 else if (!FrameReached(&screen_frame_counter, 1))
13127 ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
13128 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
13129 redraw_mask |= REDRAW_FIELD;
13132 ScreenMovDir = MV_NONE;
13135 void TestIfPlayerTouchesCustomElement(int x, int y)
13137 static int xy[4][2] =
13144 static int trigger_sides[4][2] =
13146 /* center side border side */
13147 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
13148 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
13149 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
13150 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
13152 static int touch_dir[4] =
13154 MV_LEFT | MV_RIGHT,
13159 int center_element = Feld[x][y]; /* should always be non-moving! */
13162 for (i = 0; i < NUM_DIRECTIONS; i++)
13164 int xx = x + xy[i][0];
13165 int yy = y + xy[i][1];
13166 int center_side = trigger_sides[i][0];
13167 int border_side = trigger_sides[i][1];
13168 int border_element;
13170 if (!IN_LEV_FIELD(xx, yy))
13173 if (IS_PLAYER(x, y))
13175 struct PlayerInfo *player = PLAYERINFO(x, y);
13177 if (game.engine_version < VERSION_IDENT(3,0,7,0))
13178 border_element = Feld[xx][yy]; /* may be moving! */
13179 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
13180 border_element = Feld[xx][yy];
13181 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
13182 border_element = MovingOrBlocked2Element(xx, yy);
13184 continue; /* center and border element do not touch */
13186 CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
13187 player->index_bit, border_side);
13188 CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
13189 CE_PLAYER_TOUCHES_X,
13190 player->index_bit, border_side);
13192 else if (IS_PLAYER(xx, yy))
13194 struct PlayerInfo *player = PLAYERINFO(xx, yy);
13196 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13198 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
13199 continue; /* center and border element do not touch */
13202 CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
13203 player->index_bit, center_side);
13204 CheckTriggeredElementChangeByPlayer(x, y, center_element,
13205 CE_PLAYER_TOUCHES_X,
13206 player->index_bit, center_side);
13212 #if USE_ELEMENT_TOUCHING_BUGFIX
13214 void TestIfElementTouchesCustomElement(int x, int y)
13216 static int xy[4][2] =
13223 static int trigger_sides[4][2] =
13225 /* center side border side */
13226 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
13227 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
13228 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
13229 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
13231 static int touch_dir[4] =
13233 MV_LEFT | MV_RIGHT,
13238 boolean change_center_element = FALSE;
13239 int center_element = Feld[x][y]; /* should always be non-moving! */
13240 int border_element_old[NUM_DIRECTIONS];
13243 for (i = 0; i < NUM_DIRECTIONS; i++)
13245 int xx = x + xy[i][0];
13246 int yy = y + xy[i][1];
13247 int border_element;
13249 border_element_old[i] = -1;
13251 if (!IN_LEV_FIELD(xx, yy))
13254 if (game.engine_version < VERSION_IDENT(3,0,7,0))
13255 border_element = Feld[xx][yy]; /* may be moving! */
13256 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
13257 border_element = Feld[xx][yy];
13258 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
13259 border_element = MovingOrBlocked2Element(xx, yy);
13261 continue; /* center and border element do not touch */
13263 border_element_old[i] = border_element;
13266 for (i = 0; i < NUM_DIRECTIONS; i++)
13268 int xx = x + xy[i][0];
13269 int yy = y + xy[i][1];
13270 int center_side = trigger_sides[i][0];
13271 int border_element = border_element_old[i];
13273 if (border_element == -1)
13276 /* check for change of border element */
13277 CheckElementChangeBySide(xx, yy, border_element, center_element,
13278 CE_TOUCHING_X, center_side);
13281 for (i = 0; i < NUM_DIRECTIONS; i++)
13283 int border_side = trigger_sides[i][1];
13284 int border_element = border_element_old[i];
13286 if (border_element == -1)
13289 /* check for change of center element (but change it only once) */
13290 if (!change_center_element)
13291 change_center_element =
13292 CheckElementChangeBySide(x, y, center_element, border_element,
13293 CE_TOUCHING_X, border_side);
13299 void TestIfElementTouchesCustomElement_OLD(int x, int y)
13301 static int xy[4][2] =
13308 static int trigger_sides[4][2] =
13310 /* center side border side */
13311 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
13312 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
13313 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
13314 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
13316 static int touch_dir[4] =
13318 MV_LEFT | MV_RIGHT,
13323 boolean change_center_element = FALSE;
13324 int center_element = Feld[x][y]; /* should always be non-moving! */
13327 for (i = 0; i < NUM_DIRECTIONS; i++)
13329 int xx = x + xy[i][0];
13330 int yy = y + xy[i][1];
13331 int center_side = trigger_sides[i][0];
13332 int border_side = trigger_sides[i][1];
13333 int border_element;
13335 if (!IN_LEV_FIELD(xx, yy))
13338 if (game.engine_version < VERSION_IDENT(3,0,7,0))
13339 border_element = Feld[xx][yy]; /* may be moving! */
13340 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
13341 border_element = Feld[xx][yy];
13342 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
13343 border_element = MovingOrBlocked2Element(xx, yy);
13345 continue; /* center and border element do not touch */
13347 /* check for change of center element (but change it only once) */
13348 if (!change_center_element)
13349 change_center_element =
13350 CheckElementChangeBySide(x, y, center_element, border_element,
13351 CE_TOUCHING_X, border_side);
13353 /* check for change of border element */
13354 CheckElementChangeBySide(xx, yy, border_element, center_element,
13355 CE_TOUCHING_X, center_side);
13361 void TestIfElementHitsCustomElement(int x, int y, int direction)
13363 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
13364 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
13365 int hitx = x + dx, hity = y + dy;
13366 int hitting_element = Feld[x][y];
13367 int touched_element;
13369 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
13372 touched_element = (IN_LEV_FIELD(hitx, hity) ?
13373 MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
13375 if (IN_LEV_FIELD(hitx, hity))
13377 int opposite_direction = MV_DIR_OPPOSITE(direction);
13378 int hitting_side = direction;
13379 int touched_side = opposite_direction;
13380 boolean object_hit = (!IS_MOVING(hitx, hity) ||
13381 MovDir[hitx][hity] != direction ||
13382 ABS(MovPos[hitx][hity]) <= TILEY / 2);
13388 CheckElementChangeBySide(x, y, hitting_element, touched_element,
13389 CE_HITTING_X, touched_side);
13391 CheckElementChangeBySide(hitx, hity, touched_element,
13392 hitting_element, CE_HIT_BY_X, hitting_side);
13394 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13395 CE_HIT_BY_SOMETHING, opposite_direction);
13399 /* "hitting something" is also true when hitting the playfield border */
13400 CheckElementChangeBySide(x, y, hitting_element, touched_element,
13401 CE_HITTING_SOMETHING, direction);
13405 void TestIfElementSmashesCustomElement(int x, int y, int direction)
13407 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
13408 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
13409 int hitx = x + dx, hity = y + dy;
13410 int hitting_element = Feld[x][y];
13411 int touched_element;
13413 boolean object_hit = (IN_LEV_FIELD(hitx, hity) &&
13414 !IS_FREE(hitx, hity) &&
13415 (!IS_MOVING(hitx, hity) ||
13416 MovDir[hitx][hity] != direction ||
13417 ABS(MovPos[hitx][hity]) <= TILEY / 2));
13420 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
13424 if (IN_LEV_FIELD(hitx, hity) && !object_hit)
13428 touched_element = (IN_LEV_FIELD(hitx, hity) ?
13429 MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
13431 CheckElementChangeBySide(x, y, hitting_element, touched_element,
13432 EP_CAN_SMASH_EVERYTHING, direction);
13434 if (IN_LEV_FIELD(hitx, hity))
13436 int opposite_direction = MV_DIR_OPPOSITE(direction);
13437 int hitting_side = direction;
13438 int touched_side = opposite_direction;
13440 int touched_element = MovingOrBlocked2Element(hitx, hity);
13443 boolean object_hit = (!IS_MOVING(hitx, hity) ||
13444 MovDir[hitx][hity] != direction ||
13445 ABS(MovPos[hitx][hity]) <= TILEY / 2);
13454 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13455 CE_SMASHED_BY_SOMETHING, opposite_direction);
13457 CheckElementChangeBySide(x, y, hitting_element, touched_element,
13458 CE_OTHER_IS_SMASHING, touched_side);
13460 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13461 CE_OTHER_GETS_SMASHED, hitting_side);
13467 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
13469 int i, kill_x = -1, kill_y = -1;
13471 int bad_element = -1;
13472 static int test_xy[4][2] =
13479 static int test_dir[4] =
13487 for (i = 0; i < NUM_DIRECTIONS; i++)
13489 int test_x, test_y, test_move_dir, test_element;
13491 test_x = good_x + test_xy[i][0];
13492 test_y = good_y + test_xy[i][1];
13494 if (!IN_LEV_FIELD(test_x, test_y))
13498 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13500 test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
13502 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13503 2nd case: DONT_TOUCH style bad thing does not move away from good thing
13505 if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
13506 (DONT_TOUCH(test_element) && test_move_dir != test_dir[i]))
13510 bad_element = test_element;
13516 if (kill_x != -1 || kill_y != -1)
13518 if (IS_PLAYER(good_x, good_y))
13520 struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
13522 if (player->shield_deadly_time_left > 0 &&
13523 !IS_INDESTRUCTIBLE(bad_element))
13524 Bang(kill_x, kill_y);
13525 else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
13526 KillPlayer(player);
13529 Bang(good_x, good_y);
13533 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
13535 int i, kill_x = -1, kill_y = -1;
13536 int bad_element = Feld[bad_x][bad_y];
13537 static int test_xy[4][2] =
13544 static int touch_dir[4] =
13546 MV_LEFT | MV_RIGHT,
13551 static int test_dir[4] =
13559 if (bad_element == EL_EXPLOSION) /* skip just exploding bad things */
13562 for (i = 0; i < NUM_DIRECTIONS; i++)
13564 int test_x, test_y, test_move_dir, test_element;
13566 test_x = bad_x + test_xy[i][0];
13567 test_y = bad_y + test_xy[i][1];
13568 if (!IN_LEV_FIELD(test_x, test_y))
13572 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13574 test_element = Feld[test_x][test_y];
13576 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13577 2nd case: DONT_TOUCH style bad thing does not move away from good thing
13579 if ((DONT_RUN_INTO(bad_element) && bad_move_dir == test_dir[i]) ||
13580 (DONT_TOUCH(bad_element) && test_move_dir != test_dir[i]))
13582 /* good thing is player or penguin that does not move away */
13583 if (IS_PLAYER(test_x, test_y))
13585 struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
13587 if (bad_element == EL_ROBOT && player->is_moving)
13588 continue; /* robot does not kill player if he is moving */
13590 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13592 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
13593 continue; /* center and border element do not touch */
13600 else if (test_element == EL_PENGUIN)
13609 if (kill_x != -1 || kill_y != -1)
13611 if (IS_PLAYER(kill_x, kill_y))
13613 struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
13615 if (player->shield_deadly_time_left > 0 &&
13616 !IS_INDESTRUCTIBLE(bad_element))
13617 Bang(bad_x, bad_y);
13618 else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
13619 KillPlayer(player);
13622 Bang(kill_x, kill_y);
13626 void TestIfPlayerTouchesBadThing(int x, int y)
13628 TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13631 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
13633 TestIfGoodThingHitsBadThing(x, y, move_dir);
13636 void TestIfBadThingTouchesPlayer(int x, int y)
13638 TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13641 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
13643 TestIfBadThingHitsGoodThing(x, y, move_dir);
13646 void TestIfFriendTouchesBadThing(int x, int y)
13648 TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13651 void TestIfBadThingTouchesFriend(int x, int y)
13653 TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13656 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
13658 int i, kill_x = bad_x, kill_y = bad_y;
13659 static int xy[4][2] =
13667 for (i = 0; i < NUM_DIRECTIONS; i++)
13671 x = bad_x + xy[i][0];
13672 y = bad_y + xy[i][1];
13673 if (!IN_LEV_FIELD(x, y))
13676 element = Feld[x][y];
13677 if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
13678 element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
13686 if (kill_x != bad_x || kill_y != bad_y)
13687 Bang(bad_x, bad_y);
13690 void KillPlayer(struct PlayerInfo *player)
13692 int jx = player->jx, jy = player->jy;
13694 if (!player->active)
13697 /* the following code was introduced to prevent an infinite loop when calling
13699 -> CheckTriggeredElementChangeExt()
13700 -> ExecuteCustomElementAction()
13702 -> (infinitely repeating the above sequence of function calls)
13703 which occurs when killing the player while having a CE with the setting
13704 "kill player X when explosion of <player X>"; the solution using a new
13705 field "player->killed" was chosen for backwards compatibility, although
13706 clever use of the fields "player->active" etc. would probably also work */
13708 if (player->killed)
13712 player->killed = TRUE;
13714 /* remove accessible field at the player's position */
13715 Feld[jx][jy] = EL_EMPTY;
13717 /* deactivate shield (else Bang()/Explode() would not work right) */
13718 player->shield_normal_time_left = 0;
13719 player->shield_deadly_time_left = 0;
13722 BuryPlayer(player);
13725 static void KillPlayerUnlessEnemyProtected(int x, int y)
13727 if (!PLAYER_ENEMY_PROTECTED(x, y))
13728 KillPlayer(PLAYERINFO(x, y));
13731 static void KillPlayerUnlessExplosionProtected(int x, int y)
13733 if (!PLAYER_EXPLOSION_PROTECTED(x, y))
13734 KillPlayer(PLAYERINFO(x, y));
13737 void BuryPlayer(struct PlayerInfo *player)
13739 int jx = player->jx, jy = player->jy;
13741 if (!player->active)
13744 PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
13745 PlayLevelSound(jx, jy, SND_GAME_LOSING);
13747 player->GameOver = TRUE;
13748 RemovePlayer(player);
13751 void RemovePlayer(struct PlayerInfo *player)
13753 int jx = player->jx, jy = player->jy;
13754 int i, found = FALSE;
13756 player->present = FALSE;
13757 player->active = FALSE;
13759 if (!ExplodeField[jx][jy])
13760 StorePlayer[jx][jy] = 0;
13762 if (player->is_moving)
13763 DrawLevelField(player->last_jx, player->last_jy);
13765 for (i = 0; i < MAX_PLAYERS; i++)
13766 if (stored_player[i].active)
13770 AllPlayersGone = TRUE;
13776 #if USE_NEW_SNAP_DELAY
13777 static void setFieldForSnapping(int x, int y, int element, int direction)
13779 struct ElementInfo *ei = &element_info[element];
13780 int direction_bit = MV_DIR_TO_BIT(direction);
13781 int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
13782 int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
13783 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
13785 Feld[x][y] = EL_ELEMENT_SNAPPING;
13786 MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
13788 ResetGfxAnimation(x, y);
13790 GfxElement[x][y] = element;
13791 GfxAction[x][y] = action;
13792 GfxDir[x][y] = direction;
13793 GfxFrame[x][y] = -1;
13798 =============================================================================
13799 checkDiagonalPushing()
13800 -----------------------------------------------------------------------------
13801 check if diagonal input device direction results in pushing of object
13802 (by checking if the alternative direction is walkable, diggable, ...)
13803 =============================================================================
13806 static boolean checkDiagonalPushing(struct PlayerInfo *player,
13807 int x, int y, int real_dx, int real_dy)
13809 int jx, jy, dx, dy, xx, yy;
13811 if (real_dx == 0 || real_dy == 0) /* no diagonal direction => push */
13814 /* diagonal direction: check alternative direction */
13819 xx = jx + (dx == 0 ? real_dx : 0);
13820 yy = jy + (dy == 0 ? real_dy : 0);
13822 return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
13826 =============================================================================
13828 -----------------------------------------------------------------------------
13829 x, y: field next to player (non-diagonal) to try to dig to
13830 real_dx, real_dy: direction as read from input device (can be diagonal)
13831 =============================================================================
13834 int DigField(struct PlayerInfo *player,
13835 int oldx, int oldy, int x, int y,
13836 int real_dx, int real_dy, int mode)
13838 boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
13839 boolean player_was_pushing = player->is_pushing;
13840 boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
13841 boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
13842 int jx = oldx, jy = oldy;
13843 int dx = x - jx, dy = y - jy;
13844 int nextx = x + dx, nexty = y + dy;
13845 int move_direction = (dx == -1 ? MV_LEFT :
13846 dx == +1 ? MV_RIGHT :
13848 dy == +1 ? MV_DOWN : MV_NONE);
13849 int opposite_direction = MV_DIR_OPPOSITE(move_direction);
13850 int dig_side = MV_DIR_OPPOSITE(move_direction);
13851 int old_element = Feld[jx][jy];
13852 #if USE_FIXED_DONT_RUN_INTO
13853 int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
13859 if (is_player) /* function can also be called by EL_PENGUIN */
13861 if (player->MovPos == 0)
13863 player->is_digging = FALSE;
13864 player->is_collecting = FALSE;
13867 if (player->MovPos == 0) /* last pushing move finished */
13868 player->is_pushing = FALSE;
13870 if (mode == DF_NO_PUSH) /* player just stopped pushing */
13872 player->is_switching = FALSE;
13873 player->push_delay = -1;
13875 return MP_NO_ACTION;
13879 #if !USE_FIXED_DONT_RUN_INTO
13880 if (IS_MOVING(x, y) || IS_PLAYER(x, y))
13881 return MP_NO_ACTION;
13884 if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
13885 old_element = Back[jx][jy];
13887 /* in case of element dropped at player position, check background */
13888 else if (Back[jx][jy] != EL_EMPTY &&
13889 game.engine_version >= VERSION_IDENT(2,2,0,0))
13890 old_element = Back[jx][jy];
13892 if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
13893 return MP_NO_ACTION; /* field has no opening in this direction */
13895 if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
13896 return MP_NO_ACTION; /* field has no opening in this direction */
13898 #if USE_FIXED_DONT_RUN_INTO
13899 if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
13903 Feld[jx][jy] = player->artwork_element;
13904 InitMovingField(jx, jy, MV_DOWN);
13905 Store[jx][jy] = EL_ACID;
13906 ContinueMoving(jx, jy);
13907 BuryPlayer(player);
13909 return MP_DONT_RUN_INTO;
13913 #if USE_FIXED_DONT_RUN_INTO
13914 if (player_can_move && DONT_RUN_INTO(element))
13916 TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
13918 return MP_DONT_RUN_INTO;
13922 #if USE_FIXED_DONT_RUN_INTO
13923 if (IS_MOVING(x, y) || IS_PLAYER(x, y))
13924 return MP_NO_ACTION;
13927 #if !USE_FIXED_DONT_RUN_INTO
13928 element = Feld[x][y];
13931 collect_count = element_info[element].collect_count_initial;
13933 if (!is_player && !IS_COLLECTIBLE(element)) /* penguin cannot collect it */
13934 return MP_NO_ACTION;
13936 if (game.engine_version < VERSION_IDENT(2,2,0,0))
13937 player_can_move = player_can_move_or_snap;
13939 if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
13940 game.engine_version >= VERSION_IDENT(2,2,0,0))
13942 CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
13943 player->index_bit, dig_side);
13944 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13945 player->index_bit, dig_side);
13947 if (element == EL_DC_LANDMINE)
13950 if (Feld[x][y] != element) /* field changed by snapping */
13953 return MP_NO_ACTION;
13956 #if USE_PLAYER_GRAVITY
13957 if (player->gravity && is_player && !player->is_auto_moving &&
13958 canFallDown(player) && move_direction != MV_DOWN &&
13959 !canMoveToValidFieldWithGravity(jx, jy, move_direction))
13960 return MP_NO_ACTION; /* player cannot walk here due to gravity */
13962 if (game.gravity && is_player && !player->is_auto_moving &&
13963 canFallDown(player) && move_direction != MV_DOWN &&
13964 !canMoveToValidFieldWithGravity(jx, jy, move_direction))
13965 return MP_NO_ACTION; /* player cannot walk here due to gravity */
13968 if (player_can_move &&
13969 IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
13971 int sound_element = SND_ELEMENT(element);
13972 int sound_action = ACTION_WALKING;
13974 if (IS_RND_GATE(element))
13976 if (!player->key[RND_GATE_NR(element)])
13977 return MP_NO_ACTION;
13979 else if (IS_RND_GATE_GRAY(element))
13981 if (!player->key[RND_GATE_GRAY_NR(element)])
13982 return MP_NO_ACTION;
13984 else if (IS_RND_GATE_GRAY_ACTIVE(element))
13986 if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
13987 return MP_NO_ACTION;
13989 else if (element == EL_EXIT_OPEN ||
13990 element == EL_EM_EXIT_OPEN ||
13991 element == EL_STEEL_EXIT_OPEN ||
13992 element == EL_EM_STEEL_EXIT_OPEN ||
13993 element == EL_SP_EXIT_OPEN ||
13994 element == EL_SP_EXIT_OPENING)
13996 sound_action = ACTION_PASSING; /* player is passing exit */
13998 else if (element == EL_EMPTY)
14000 sound_action = ACTION_MOVING; /* nothing to walk on */
14003 /* play sound from background or player, whatever is available */
14004 if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
14005 PlayLevelSoundElementAction(x, y, sound_element, sound_action);
14007 PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
14009 else if (player_can_move &&
14010 IS_PASSABLE(element) && canPassField(x, y, move_direction))
14012 if (!ACCESS_FROM(element, opposite_direction))
14013 return MP_NO_ACTION; /* field not accessible from this direction */
14015 if (CAN_MOVE(element)) /* only fixed elements can be passed! */
14016 return MP_NO_ACTION;
14018 if (IS_EM_GATE(element))
14020 if (!player->key[EM_GATE_NR(element)])
14021 return MP_NO_ACTION;
14023 else if (IS_EM_GATE_GRAY(element))
14025 if (!player->key[EM_GATE_GRAY_NR(element)])
14026 return MP_NO_ACTION;
14028 else if (IS_EM_GATE_GRAY_ACTIVE(element))
14030 if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
14031 return MP_NO_ACTION;
14033 else if (IS_EMC_GATE(element))
14035 if (!player->key[EMC_GATE_NR(element)])
14036 return MP_NO_ACTION;
14038 else if (IS_EMC_GATE_GRAY(element))
14040 if (!player->key[EMC_GATE_GRAY_NR(element)])
14041 return MP_NO_ACTION;
14043 else if (IS_EMC_GATE_GRAY_ACTIVE(element))
14045 if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
14046 return MP_NO_ACTION;
14048 else if (element == EL_DC_GATE_WHITE ||
14049 element == EL_DC_GATE_WHITE_GRAY ||
14050 element == EL_DC_GATE_WHITE_GRAY_ACTIVE)
14052 if (player->num_white_keys == 0)
14053 return MP_NO_ACTION;
14055 player->num_white_keys--;
14057 else if (IS_SP_PORT(element))
14059 if (element == EL_SP_GRAVITY_PORT_LEFT ||
14060 element == EL_SP_GRAVITY_PORT_RIGHT ||
14061 element == EL_SP_GRAVITY_PORT_UP ||
14062 element == EL_SP_GRAVITY_PORT_DOWN)
14063 #if USE_PLAYER_GRAVITY
14064 player->gravity = !player->gravity;
14066 game.gravity = !game.gravity;
14068 else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
14069 element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
14070 element == EL_SP_GRAVITY_ON_PORT_UP ||
14071 element == EL_SP_GRAVITY_ON_PORT_DOWN)
14072 #if USE_PLAYER_GRAVITY
14073 player->gravity = TRUE;
14075 game.gravity = TRUE;
14077 else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
14078 element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
14079 element == EL_SP_GRAVITY_OFF_PORT_UP ||
14080 element == EL_SP_GRAVITY_OFF_PORT_DOWN)
14081 #if USE_PLAYER_GRAVITY
14082 player->gravity = FALSE;
14084 game.gravity = FALSE;
14088 /* automatically move to the next field with double speed */
14089 player->programmed_action = move_direction;
14091 if (player->move_delay_reset_counter == 0)
14093 player->move_delay_reset_counter = 2; /* two double speed steps */
14095 DOUBLE_PLAYER_SPEED(player);
14098 PlayLevelSoundAction(x, y, ACTION_PASSING);
14100 else if (player_can_move_or_snap && IS_DIGGABLE(element))
14104 if (mode != DF_SNAP)
14106 GfxElement[x][y] = GFX_ELEMENT(element);
14107 player->is_digging = TRUE;
14110 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14112 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
14113 player->index_bit, dig_side);
14115 if (mode == DF_SNAP)
14117 #if USE_NEW_SNAP_DELAY
14118 if (level.block_snap_field)
14119 setFieldForSnapping(x, y, element, move_direction);
14121 TestIfElementTouchesCustomElement(x, y); /* for empty space */
14123 TestIfElementTouchesCustomElement(x, y); /* for empty space */
14126 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14127 player->index_bit, dig_side);
14130 else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
14134 if (is_player && mode != DF_SNAP)
14136 GfxElement[x][y] = element;
14137 player->is_collecting = TRUE;
14140 if (element == EL_SPEED_PILL)
14142 player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
14144 else if (element == EL_EXTRA_TIME && level.time > 0)
14146 TimeLeft += level.extra_time;
14149 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
14151 DisplayGameControlValues();
14153 DrawGameValue_Time(TimeLeft);
14156 else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
14158 player->shield_normal_time_left += level.shield_normal_time;
14159 if (element == EL_SHIELD_DEADLY)
14160 player->shield_deadly_time_left += level.shield_deadly_time;
14162 else if (element == EL_DYNAMITE ||
14163 element == EL_EM_DYNAMITE ||
14164 element == EL_SP_DISK_RED)
14166 if (player->inventory_size < MAX_INVENTORY_SIZE)
14167 player->inventory_element[player->inventory_size++] = element;
14169 DrawGameDoorValues();
14171 else if (element == EL_DYNABOMB_INCREASE_NUMBER)
14173 player->dynabomb_count++;
14174 player->dynabombs_left++;
14176 else if (element == EL_DYNABOMB_INCREASE_SIZE)
14178 player->dynabomb_size++;
14180 else if (element == EL_DYNABOMB_INCREASE_POWER)
14182 player->dynabomb_xl = TRUE;
14184 else if (IS_KEY(element))
14186 player->key[KEY_NR(element)] = TRUE;
14188 DrawGameDoorValues();
14190 else if (element == EL_DC_KEY_WHITE)
14192 player->num_white_keys++;
14194 /* display white keys? */
14195 /* DrawGameDoorValues(); */
14197 else if (IS_ENVELOPE(element))
14199 player->show_envelope = element;
14201 else if (element == EL_EMC_LENSES)
14203 game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
14205 RedrawAllInvisibleElementsForLenses();
14207 else if (element == EL_EMC_MAGNIFIER)
14209 game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
14211 RedrawAllInvisibleElementsForMagnifier();
14213 else if (IS_DROPPABLE(element) ||
14214 IS_THROWABLE(element)) /* can be collected and dropped */
14218 if (collect_count == 0)
14219 player->inventory_infinite_element = element;
14221 for (i = 0; i < collect_count; i++)
14222 if (player->inventory_size < MAX_INVENTORY_SIZE)
14223 player->inventory_element[player->inventory_size++] = element;
14225 DrawGameDoorValues();
14227 else if (collect_count > 0)
14229 local_player->gems_still_needed -= collect_count;
14230 if (local_player->gems_still_needed < 0)
14231 local_player->gems_still_needed = 0;
14234 game_panel_controls[GAME_PANEL_GEMS].value = local_player->gems_still_needed;
14236 DisplayGameControlValues();
14238 DrawGameValue_Emeralds(local_player->gems_still_needed);
14242 RaiseScoreElement(element);
14243 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
14246 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
14247 player->index_bit, dig_side);
14249 if (mode == DF_SNAP)
14251 #if USE_NEW_SNAP_DELAY
14252 if (level.block_snap_field)
14253 setFieldForSnapping(x, y, element, move_direction);
14255 TestIfElementTouchesCustomElement(x, y); /* for empty space */
14257 TestIfElementTouchesCustomElement(x, y); /* for empty space */
14260 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14261 player->index_bit, dig_side);
14264 else if (player_can_move_or_snap && IS_PUSHABLE(element))
14266 if (mode == DF_SNAP && element != EL_BD_ROCK)
14267 return MP_NO_ACTION;
14269 if (CAN_FALL(element) && dy)
14270 return MP_NO_ACTION;
14272 if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
14273 !(element == EL_SPRING && level.use_spring_bug))
14274 return MP_NO_ACTION;
14276 if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
14277 ((move_direction & MV_VERTICAL &&
14278 ((element_info[element].move_pattern & MV_LEFT &&
14279 IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
14280 (element_info[element].move_pattern & MV_RIGHT &&
14281 IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
14282 (move_direction & MV_HORIZONTAL &&
14283 ((element_info[element].move_pattern & MV_UP &&
14284 IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
14285 (element_info[element].move_pattern & MV_DOWN &&
14286 IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
14287 return MP_NO_ACTION;
14289 /* do not push elements already moving away faster than player */
14290 if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
14291 ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
14292 return MP_NO_ACTION;
14294 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
14296 if (player->push_delay_value == -1 || !player_was_pushing)
14297 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14299 else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
14301 if (player->push_delay_value == -1)
14302 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14304 else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
14306 if (!player->is_pushing)
14307 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14310 player->is_pushing = TRUE;
14311 player->is_active = TRUE;
14313 if (!(IN_LEV_FIELD(nextx, nexty) &&
14314 (IS_FREE(nextx, nexty) ||
14315 (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY &&
14316 IS_SB_ELEMENT(element)))))
14317 return MP_NO_ACTION;
14319 if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
14320 return MP_NO_ACTION;
14322 if (player->push_delay == -1) /* new pushing; restart delay */
14323 player->push_delay = 0;
14325 if (player->push_delay < player->push_delay_value &&
14326 !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
14327 element != EL_SPRING && element != EL_BALLOON)
14329 /* make sure that there is no move delay before next try to push */
14330 if (game.engine_version >= VERSION_IDENT(3,0,7,1))
14331 player->move_delay = 0;
14333 return MP_NO_ACTION;
14336 if (IS_SB_ELEMENT(element))
14338 if (element == EL_SOKOBAN_FIELD_FULL)
14340 Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
14341 local_player->sokobanfields_still_needed++;
14344 if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
14346 Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
14347 local_player->sokobanfields_still_needed--;
14350 Feld[x][y] = EL_SOKOBAN_OBJECT;
14352 if (Back[x][y] == Back[nextx][nexty])
14353 PlayLevelSoundAction(x, y, ACTION_PUSHING);
14354 else if (Back[x][y] != 0)
14355 PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
14358 PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
14361 if (local_player->sokobanfields_still_needed == 0 &&
14362 game.emulation == EMU_SOKOBAN)
14364 PlayerWins(player);
14366 PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
14370 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14372 InitMovingField(x, y, move_direction);
14373 GfxAction[x][y] = ACTION_PUSHING;
14375 if (mode == DF_SNAP)
14376 ContinueMoving(x, y);
14378 MovPos[x][y] = (dx != 0 ? dx : dy);
14380 Pushed[x][y] = TRUE;
14381 Pushed[nextx][nexty] = TRUE;
14383 if (game.engine_version < VERSION_IDENT(2,2,0,7))
14384 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14386 player->push_delay_value = -1; /* get new value later */
14388 /* check for element change _after_ element has been pushed */
14389 if (game.use_change_when_pushing_bug)
14391 CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
14392 player->index_bit, dig_side);
14393 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
14394 player->index_bit, dig_side);
14397 else if (IS_SWITCHABLE(element))
14399 if (PLAYER_SWITCHING(player, x, y))
14401 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14402 player->index_bit, dig_side);
14407 player->is_switching = TRUE;
14408 player->switch_x = x;
14409 player->switch_y = y;
14411 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
14413 if (element == EL_ROBOT_WHEEL)
14415 Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
14419 game.robot_wheel_active = TRUE;
14421 DrawLevelField(x, y);
14423 else if (element == EL_SP_TERMINAL)
14427 SCAN_PLAYFIELD(xx, yy)
14429 if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
14431 else if (Feld[xx][yy] == EL_SP_TERMINAL)
14432 Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
14435 else if (IS_BELT_SWITCH(element))
14437 ToggleBeltSwitch(x, y);
14439 else if (element == EL_SWITCHGATE_SWITCH_UP ||
14440 element == EL_SWITCHGATE_SWITCH_DOWN ||
14441 element == EL_DC_SWITCHGATE_SWITCH_UP ||
14442 element == EL_DC_SWITCHGATE_SWITCH_DOWN)
14444 ToggleSwitchgateSwitch(x, y);
14446 else if (element == EL_LIGHT_SWITCH ||
14447 element == EL_LIGHT_SWITCH_ACTIVE)
14449 ToggleLightSwitch(x, y);
14451 else if (element == EL_TIMEGATE_SWITCH ||
14452 element == EL_DC_TIMEGATE_SWITCH)
14454 ActivateTimegateSwitch(x, y);
14456 else if (element == EL_BALLOON_SWITCH_LEFT ||
14457 element == EL_BALLOON_SWITCH_RIGHT ||
14458 element == EL_BALLOON_SWITCH_UP ||
14459 element == EL_BALLOON_SWITCH_DOWN ||
14460 element == EL_BALLOON_SWITCH_NONE ||
14461 element == EL_BALLOON_SWITCH_ANY)
14463 game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT ? MV_LEFT :
14464 element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
14465 element == EL_BALLOON_SWITCH_UP ? MV_UP :
14466 element == EL_BALLOON_SWITCH_DOWN ? MV_DOWN :
14467 element == EL_BALLOON_SWITCH_NONE ? MV_NONE :
14470 else if (element == EL_LAMP)
14472 Feld[x][y] = EL_LAMP_ACTIVE;
14473 local_player->lights_still_needed--;
14475 ResetGfxAnimation(x, y);
14476 DrawLevelField(x, y);
14478 else if (element == EL_TIME_ORB_FULL)
14480 Feld[x][y] = EL_TIME_ORB_EMPTY;
14482 if (level.time > 0 || level.use_time_orb_bug)
14484 TimeLeft += level.time_orb_time;
14487 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
14489 DisplayGameControlValues();
14491 DrawGameValue_Time(TimeLeft);
14495 ResetGfxAnimation(x, y);
14496 DrawLevelField(x, y);
14498 else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
14499 element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14503 game.ball_state = !game.ball_state;
14505 SCAN_PLAYFIELD(xx, yy)
14507 int e = Feld[xx][yy];
14509 if (game.ball_state)
14511 if (e == EL_EMC_MAGIC_BALL)
14512 CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
14513 else if (e == EL_EMC_MAGIC_BALL_SWITCH)
14514 CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
14518 if (e == EL_EMC_MAGIC_BALL_ACTIVE)
14519 CreateField(xx, yy, EL_EMC_MAGIC_BALL);
14520 else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14521 CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
14526 CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14527 player->index_bit, dig_side);
14529 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14530 player->index_bit, dig_side);
14532 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14533 player->index_bit, dig_side);
14539 if (!PLAYER_SWITCHING(player, x, y))
14541 player->is_switching = TRUE;
14542 player->switch_x = x;
14543 player->switch_y = y;
14545 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
14546 player->index_bit, dig_side);
14547 CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14548 player->index_bit, dig_side);
14550 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
14551 player->index_bit, dig_side);
14552 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14553 player->index_bit, dig_side);
14556 CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
14557 player->index_bit, dig_side);
14558 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14559 player->index_bit, dig_side);
14561 return MP_NO_ACTION;
14564 player->push_delay = -1;
14566 if (is_player) /* function can also be called by EL_PENGUIN */
14568 if (Feld[x][y] != element) /* really digged/collected something */
14570 player->is_collecting = !player->is_digging;
14571 player->is_active = TRUE;
14578 boolean SnapField(struct PlayerInfo *player, int dx, int dy)
14580 int jx = player->jx, jy = player->jy;
14581 int x = jx + dx, y = jy + dy;
14582 int snap_direction = (dx == -1 ? MV_LEFT :
14583 dx == +1 ? MV_RIGHT :
14585 dy == +1 ? MV_DOWN : MV_NONE);
14586 boolean can_continue_snapping = (level.continuous_snapping &&
14587 WasJustFalling[x][y] < CHECK_DELAY_FALLING);
14589 if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
14592 if (!player->active || !IN_LEV_FIELD(x, y))
14600 if (player->MovPos == 0)
14601 player->is_pushing = FALSE;
14603 player->is_snapping = FALSE;
14605 if (player->MovPos == 0)
14607 player->is_moving = FALSE;
14608 player->is_digging = FALSE;
14609 player->is_collecting = FALSE;
14615 #if USE_NEW_CONTINUOUS_SNAPPING
14616 /* prevent snapping with already pressed snap key when not allowed */
14617 if (player->is_snapping && !can_continue_snapping)
14620 if (player->is_snapping)
14624 player->MovDir = snap_direction;
14626 if (player->MovPos == 0)
14628 player->is_moving = FALSE;
14629 player->is_digging = FALSE;
14630 player->is_collecting = FALSE;
14633 player->is_dropping = FALSE;
14634 player->is_dropping_pressed = FALSE;
14635 player->drop_pressed_delay = 0;
14637 if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
14640 player->is_snapping = TRUE;
14641 player->is_active = TRUE;
14643 if (player->MovPos == 0)
14645 player->is_moving = FALSE;
14646 player->is_digging = FALSE;
14647 player->is_collecting = FALSE;
14650 if (player->MovPos != 0) /* prevent graphic bugs in versions < 2.2.0 */
14651 DrawLevelField(player->last_jx, player->last_jy);
14653 DrawLevelField(x, y);
14658 boolean DropElement(struct PlayerInfo *player)
14660 int old_element, new_element;
14661 int dropx = player->jx, dropy = player->jy;
14662 int drop_direction = player->MovDir;
14663 int drop_side = drop_direction;
14665 int drop_element = get_next_dropped_element(player);
14667 int drop_element = (player->inventory_size > 0 ?
14668 player->inventory_element[player->inventory_size - 1] :
14669 player->inventory_infinite_element != EL_UNDEFINED ?
14670 player->inventory_infinite_element :
14671 player->dynabombs_left > 0 ?
14672 EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
14676 player->is_dropping_pressed = TRUE;
14678 /* do not drop an element on top of another element; when holding drop key
14679 pressed without moving, dropped element must move away before the next
14680 element can be dropped (this is especially important if the next element
14681 is dynamite, which can be placed on background for historical reasons) */
14682 if (PLAYER_DROPPING(player, dropx, dropy) && Feld[dropx][dropy] != EL_EMPTY)
14685 if (IS_THROWABLE(drop_element))
14687 dropx += GET_DX_FROM_DIR(drop_direction);
14688 dropy += GET_DY_FROM_DIR(drop_direction);
14690 if (!IN_LEV_FIELD(dropx, dropy))
14694 old_element = Feld[dropx][dropy]; /* old element at dropping position */
14695 new_element = drop_element; /* default: no change when dropping */
14697 /* check if player is active, not moving and ready to drop */
14698 if (!player->active || player->MovPos || player->drop_delay > 0)
14701 /* check if player has anything that can be dropped */
14702 if (new_element == EL_UNDEFINED)
14705 /* check if drop key was pressed long enough for EM style dynamite */
14706 if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
14709 /* check if anything can be dropped at the current position */
14710 if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
14713 /* collected custom elements can only be dropped on empty fields */
14714 if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
14717 if (old_element != EL_EMPTY)
14718 Back[dropx][dropy] = old_element; /* store old element on this field */
14720 ResetGfxAnimation(dropx, dropy);
14721 ResetRandomAnimationValue(dropx, dropy);
14723 if (player->inventory_size > 0 ||
14724 player->inventory_infinite_element != EL_UNDEFINED)
14726 if (player->inventory_size > 0)
14728 player->inventory_size--;
14730 DrawGameDoorValues();
14732 if (new_element == EL_DYNAMITE)
14733 new_element = EL_DYNAMITE_ACTIVE;
14734 else if (new_element == EL_EM_DYNAMITE)
14735 new_element = EL_EM_DYNAMITE_ACTIVE;
14736 else if (new_element == EL_SP_DISK_RED)
14737 new_element = EL_SP_DISK_RED_ACTIVE;
14740 Feld[dropx][dropy] = new_element;
14742 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
14743 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
14744 el2img(Feld[dropx][dropy]), 0);
14746 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
14748 /* needed if previous element just changed to "empty" in the last frame */
14749 ChangeCount[dropx][dropy] = 0; /* allow at least one more change */
14751 CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
14752 player->index_bit, drop_side);
14753 CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
14755 player->index_bit, drop_side);
14757 TestIfElementTouchesCustomElement(dropx, dropy);
14759 else /* player is dropping a dyna bomb */
14761 player->dynabombs_left--;
14763 Feld[dropx][dropy] = new_element;
14765 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
14766 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
14767 el2img(Feld[dropx][dropy]), 0);
14769 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
14772 if (Feld[dropx][dropy] == new_element) /* uninitialized unless CE change */
14773 InitField_WithBug1(dropx, dropy, FALSE);
14775 new_element = Feld[dropx][dropy]; /* element might have changed */
14777 if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
14778 element_info[new_element].move_pattern == MV_WHEN_DROPPED)
14780 int move_direction, nextx, nexty;
14782 if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
14783 MovDir[dropx][dropy] = drop_direction;
14785 move_direction = MovDir[dropx][dropy];
14786 nextx = dropx + GET_DX_FROM_DIR(move_direction);
14787 nexty = dropy + GET_DY_FROM_DIR(move_direction);
14789 ChangeCount[dropx][dropy] = 0; /* allow at least one more change */
14791 #if USE_FIX_IMPACT_COLLISION
14792 /* do not cause impact style collision by dropping elements that can fall */
14793 CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
14795 CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
14799 player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
14800 player->is_dropping = TRUE;
14802 player->drop_pressed_delay = 0;
14803 player->is_dropping_pressed = FALSE;
14805 player->drop_x = dropx;
14806 player->drop_y = dropy;
14811 /* ------------------------------------------------------------------------- */
14812 /* game sound playing functions */
14813 /* ------------------------------------------------------------------------- */
14815 static int *loop_sound_frame = NULL;
14816 static int *loop_sound_volume = NULL;
14818 void InitPlayLevelSound()
14820 int num_sounds = getSoundListSize();
14822 checked_free(loop_sound_frame);
14823 checked_free(loop_sound_volume);
14825 loop_sound_frame = checked_calloc(num_sounds * sizeof(int));
14826 loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
14829 static void PlayLevelSound(int x, int y, int nr)
14831 int sx = SCREENX(x), sy = SCREENY(y);
14832 int volume, stereo_position;
14833 int max_distance = 8;
14834 int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
14836 if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
14837 (!setup.sound_loops && IS_LOOP_SOUND(nr)))
14840 if (!IN_LEV_FIELD(x, y) ||
14841 sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
14842 sy < -max_distance || sy >= SCR_FIELDY + max_distance)
14845 volume = SOUND_MAX_VOLUME;
14847 if (!IN_SCR_FIELD(sx, sy))
14849 int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
14850 int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
14852 volume -= volume * (dx > dy ? dx : dy) / max_distance;
14855 stereo_position = (SOUND_MAX_LEFT +
14856 (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
14857 (SCR_FIELDX + 2 * max_distance));
14859 if (IS_LOOP_SOUND(nr))
14861 /* This assures that quieter loop sounds do not overwrite louder ones,
14862 while restarting sound volume comparison with each new game frame. */
14864 if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
14867 loop_sound_volume[nr] = volume;
14868 loop_sound_frame[nr] = FrameCounter;
14871 PlaySoundExt(nr, volume, stereo_position, type);
14874 static void PlayLevelSoundNearest(int x, int y, int sound_action)
14876 PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
14877 x > LEVELX(BX2) ? LEVELX(BX2) : x,
14878 y < LEVELY(BY1) ? LEVELY(BY1) :
14879 y > LEVELY(BY2) ? LEVELY(BY2) : y,
14883 static void PlayLevelSoundAction(int x, int y, int action)
14885 PlayLevelSoundElementAction(x, y, Feld[x][y], action);
14888 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
14890 int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
14892 if (sound_effect != SND_UNDEFINED)
14893 PlayLevelSound(x, y, sound_effect);
14896 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
14899 int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
14901 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14902 PlayLevelSound(x, y, sound_effect);
14905 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
14907 int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
14909 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14910 PlayLevelSound(x, y, sound_effect);
14913 static void StopLevelSoundActionIfLoop(int x, int y, int action)
14915 int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
14917 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14918 StopSound(sound_effect);
14921 static void PlayLevelMusic()
14923 if (levelset.music[level_nr] != MUS_UNDEFINED)
14924 PlayMusic(levelset.music[level_nr]); /* from config file */
14926 PlayMusic(MAP_NOCONF_MUSIC(level_nr)); /* from music dir */
14929 void PlayLevelSound_EM(int xx, int yy, int element_em, int sample)
14931 int element = (element_em > -1 ? map_element_EM_to_RND(element_em) : 0);
14932 int offset = (BorderElement == EL_STEELWALL ? 1 : 0);
14933 int x = xx - 1 - offset;
14934 int y = yy - 1 - offset;
14939 PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
14943 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14947 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14951 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14955 PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
14959 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14963 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14966 case SAMPLE_android_clone:
14967 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14970 case SAMPLE_android_move:
14971 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14974 case SAMPLE_spring:
14975 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14979 PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
14983 PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
14986 case SAMPLE_eater_eat:
14987 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14991 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14994 case SAMPLE_collect:
14995 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
14998 case SAMPLE_diamond:
14999 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15002 case SAMPLE_squash:
15003 /* !!! CHECK THIS !!! */
15005 PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
15007 PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
15011 case SAMPLE_wonderfall:
15012 PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
15016 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15020 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
15024 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
15028 PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
15032 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
15036 PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
15039 case SAMPLE_wonder:
15040 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
15044 PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
15047 case SAMPLE_exit_open:
15048 PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
15051 case SAMPLE_exit_leave:
15052 PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
15055 case SAMPLE_dynamite:
15056 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
15060 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
15064 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
15068 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
15072 PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
15076 PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
15080 PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
15084 PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
15090 void ChangeTime(int value)
15092 int *time = (level.time == 0 ? &TimePlayed : &TimeLeft);
15096 /* EMC game engine uses value from time counter of RND game engine */
15097 level.native_em_level->lev->time = *time;
15099 DrawGameValue_Time(*time);
15102 void RaiseScore(int value)
15104 /* EMC game engine and RND game engine have separate score counters */
15105 int *score = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
15106 &level.native_em_level->lev->score : &local_player->score);
15110 DrawGameValue_Score(*score);
15114 void RaiseScore(int value)
15116 local_player->score += value;
15119 game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
15121 DisplayGameControlValues();
15123 DrawGameValue_Score(local_player->score);
15127 void RaiseScoreElement(int element)
15132 case EL_BD_DIAMOND:
15133 case EL_EMERALD_YELLOW:
15134 case EL_EMERALD_RED:
15135 case EL_EMERALD_PURPLE:
15136 case EL_SP_INFOTRON:
15137 RaiseScore(level.score[SC_EMERALD]);
15140 RaiseScore(level.score[SC_DIAMOND]);
15143 RaiseScore(level.score[SC_CRYSTAL]);
15146 RaiseScore(level.score[SC_PEARL]);
15149 case EL_BD_BUTTERFLY:
15150 case EL_SP_ELECTRON:
15151 RaiseScore(level.score[SC_BUG]);
15154 case EL_BD_FIREFLY:
15155 case EL_SP_SNIKSNAK:
15156 RaiseScore(level.score[SC_SPACESHIP]);
15159 case EL_DARK_YAMYAM:
15160 RaiseScore(level.score[SC_YAMYAM]);
15163 RaiseScore(level.score[SC_ROBOT]);
15166 RaiseScore(level.score[SC_PACMAN]);
15169 RaiseScore(level.score[SC_NUT]);
15172 case EL_EM_DYNAMITE:
15173 case EL_SP_DISK_RED:
15174 case EL_DYNABOMB_INCREASE_NUMBER:
15175 case EL_DYNABOMB_INCREASE_SIZE:
15176 case EL_DYNABOMB_INCREASE_POWER:
15177 RaiseScore(level.score[SC_DYNAMITE]);
15179 case EL_SHIELD_NORMAL:
15180 case EL_SHIELD_DEADLY:
15181 RaiseScore(level.score[SC_SHIELD]);
15183 case EL_EXTRA_TIME:
15184 RaiseScore(level.extra_time_score);
15198 case EL_DC_KEY_WHITE:
15199 RaiseScore(level.score[SC_KEY]);
15202 RaiseScore(element_info[element].collect_score);
15207 void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message)
15209 if (skip_request || Request(message, REQ_ASK | REQ_STAY_CLOSED))
15211 #if defined(NETWORK_AVALIABLE)
15212 if (options.network)
15213 SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
15222 FadeSkipNextFadeIn();
15224 fading = fading_none;
15228 OpenDoor(DOOR_CLOSE_1);
15231 game_status = GAME_MODE_MAIN;
15234 DrawAndFadeInMainMenu(REDRAW_FIELD);
15242 FadeOut(REDRAW_FIELD);
15245 game_status = GAME_MODE_MAIN;
15247 DrawAndFadeInMainMenu(REDRAW_FIELD);
15251 else /* continue playing the game */
15253 if (tape.playing && tape.deactivate_display)
15254 TapeDeactivateDisplayOff(TRUE);
15256 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
15258 if (tape.playing && tape.deactivate_display)
15259 TapeDeactivateDisplayOn();
15263 void RequestQuitGame(boolean ask_if_really_quit)
15265 boolean quick_quit = (!ask_if_really_quit || level_editor_test_game);
15266 boolean skip_request = AllPlayersGone || quick_quit;
15268 RequestQuitGameExt(skip_request, quick_quit,
15269 "Do you really want to quit the game ?");
15273 /* ------------------------------------------------------------------------- */
15274 /* random generator functions */
15275 /* ------------------------------------------------------------------------- */
15277 unsigned int InitEngineRandom_RND(long seed)
15279 game.num_random_calls = 0;
15282 unsigned int rnd_seed = InitEngineRandom(seed);
15284 printf("::: START RND: %d\n", rnd_seed);
15289 return InitEngineRandom(seed);
15295 unsigned int RND(int max)
15299 game.num_random_calls++;
15301 return GetEngineRandom(max);
15308 /* ------------------------------------------------------------------------- */
15309 /* game engine snapshot handling functions */
15310 /* ------------------------------------------------------------------------- */
15312 #define ARGS_ADDRESS_AND_SIZEOF(x) (&(x)), (sizeof(x))
15314 struct EngineSnapshotInfo
15316 /* runtime values for custom element collect score */
15317 int collect_score[NUM_CUSTOM_ELEMENTS];
15319 /* runtime values for group element choice position */
15320 int choice_pos[NUM_GROUP_ELEMENTS];
15322 /* runtime values for belt position animations */
15323 int belt_graphic[4 * NUM_BELT_PARTS];
15324 int belt_anim_mode[4 * NUM_BELT_PARTS];
15327 struct EngineSnapshotNodeInfo
15334 static struct EngineSnapshotInfo engine_snapshot_rnd;
15335 static ListNode *engine_snapshot_list = NULL;
15336 static char *snapshot_level_identifier = NULL;
15337 static int snapshot_level_nr = -1;
15339 void FreeEngineSnapshot()
15341 while (engine_snapshot_list != NULL)
15342 deleteNodeFromList(&engine_snapshot_list, engine_snapshot_list->key,
15345 setString(&snapshot_level_identifier, NULL);
15346 snapshot_level_nr = -1;
15349 static void SaveEngineSnapshotValues_RND()
15351 static int belt_base_active_element[4] =
15353 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
15354 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
15355 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
15356 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
15360 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
15362 int element = EL_CUSTOM_START + i;
15364 engine_snapshot_rnd.collect_score[i] = element_info[element].collect_score;
15367 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15369 int element = EL_GROUP_START + i;
15371 engine_snapshot_rnd.choice_pos[i] = element_info[element].group->choice_pos;
15374 for (i = 0; i < 4; i++)
15376 for (j = 0; j < NUM_BELT_PARTS; j++)
15378 int element = belt_base_active_element[i] + j;
15379 int graphic = el2img(element);
15380 int anim_mode = graphic_info[graphic].anim_mode;
15382 engine_snapshot_rnd.belt_graphic[i * 4 + j] = graphic;
15383 engine_snapshot_rnd.belt_anim_mode[i * 4 + j] = anim_mode;
15388 static void LoadEngineSnapshotValues_RND()
15390 unsigned long num_random_calls = game.num_random_calls;
15393 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
15395 int element = EL_CUSTOM_START + i;
15397 element_info[element].collect_score = engine_snapshot_rnd.collect_score[i];
15400 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15402 int element = EL_GROUP_START + i;
15404 element_info[element].group->choice_pos = engine_snapshot_rnd.choice_pos[i];
15407 for (i = 0; i < 4; i++)
15409 for (j = 0; j < NUM_BELT_PARTS; j++)
15411 int graphic = engine_snapshot_rnd.belt_graphic[i * 4 + j];
15412 int anim_mode = engine_snapshot_rnd.belt_anim_mode[i * 4 + j];
15414 graphic_info[graphic].anim_mode = anim_mode;
15418 if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15420 InitRND(tape.random_seed);
15421 for (i = 0; i < num_random_calls; i++)
15425 if (game.num_random_calls != num_random_calls)
15427 Error(ERR_INFO, "number of random calls out of sync");
15428 Error(ERR_INFO, "number of random calls should be %d", num_random_calls);
15429 Error(ERR_INFO, "number of random calls is %d", game.num_random_calls);
15430 Error(ERR_EXIT, "this should not happen -- please debug");
15434 static void SaveEngineSnapshotBuffer(void *buffer, int size)
15436 struct EngineSnapshotNodeInfo *bi =
15437 checked_calloc(sizeof(struct EngineSnapshotNodeInfo));
15439 bi->buffer_orig = buffer;
15440 bi->buffer_copy = checked_malloc(size);
15443 memcpy(bi->buffer_copy, buffer, size);
15445 addNodeToList(&engine_snapshot_list, NULL, bi);
15448 void SaveEngineSnapshot()
15450 FreeEngineSnapshot(); /* free previous snapshot, if needed */
15452 if (level_editor_test_game) /* do not save snapshots from editor */
15455 /* copy some special values to a structure better suited for the snapshot */
15457 SaveEngineSnapshotValues_RND();
15458 SaveEngineSnapshotValues_EM();
15460 /* save values stored in special snapshot structure */
15462 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
15463 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
15465 /* save further RND engine values */
15467 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(stored_player));
15468 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(game));
15469 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(tape));
15471 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ZX));
15472 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ZY));
15473 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExitX));
15474 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExitY));
15476 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(FrameCounter));
15477 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimeFrames));
15478 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimePlayed));
15479 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimeLeft));
15480 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TapeTime));
15482 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenMovDir));
15483 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenMovPos));
15484 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenGfxPos));
15486 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScrollStepSize));
15488 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AllPlayersGone));
15490 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt));
15491 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2));
15493 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Feld));
15494 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovPos));
15495 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovDir));
15496 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovDelay));
15497 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeDelay));
15498 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangePage));
15499 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CustomValue));
15500 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Store));
15501 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Store2));
15502 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(StorePlayer));
15503 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Back));
15504 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaNr));
15505 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(WasJustMoving));
15506 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(WasJustFalling));
15507 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CheckCollision));
15508 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CheckImpact));
15509 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Stop));
15510 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Pushed));
15512 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeCount));
15513 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeEvent));
15515 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodePhase));
15516 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodeDelay));
15517 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodeField));
15519 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(RunnerVisit));
15520 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(PlayerVisit));
15522 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxFrame));
15523 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxRandom));
15524 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxElement));
15525 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxAction));
15526 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxDir));
15528 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(scroll_x));
15529 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(scroll_y));
15531 /* save level identification information */
15533 setString(&snapshot_level_identifier, leveldir_current->identifier);
15534 snapshot_level_nr = level_nr;
15537 ListNode *node = engine_snapshot_list;
15540 while (node != NULL)
15542 num_bytes += ((struct EngineSnapshotNodeInfo *)node->content)->size;
15547 printf("::: size of engine snapshot: %d bytes\n", num_bytes);
15551 static void LoadEngineSnapshotBuffer(struct EngineSnapshotNodeInfo *bi)
15553 memcpy(bi->buffer_orig, bi->buffer_copy, bi->size);
15556 void LoadEngineSnapshot()
15558 ListNode *node = engine_snapshot_list;
15560 if (engine_snapshot_list == NULL)
15563 while (node != NULL)
15565 LoadEngineSnapshotBuffer((struct EngineSnapshotNodeInfo *)node->content);
15570 /* restore special values from snapshot structure */
15572 LoadEngineSnapshotValues_RND();
15573 LoadEngineSnapshotValues_EM();
15576 boolean CheckEngineSnapshot()
15578 return (strEqual(snapshot_level_identifier, leveldir_current->identifier) &&
15579 snapshot_level_nr == level_nr);
15583 /* ---------- new game button stuff ---------------------------------------- */
15585 /* graphic position values for game buttons */
15586 #define GAME_BUTTON_XSIZE 30
15587 #define GAME_BUTTON_YSIZE 30
15588 #define GAME_BUTTON_XPOS 5
15589 #define GAME_BUTTON_YPOS 215
15590 #define SOUND_BUTTON_XPOS 5
15591 #define SOUND_BUTTON_YPOS (GAME_BUTTON_YPOS + GAME_BUTTON_YSIZE)
15593 #define GAME_BUTTON_STOP_XPOS (GAME_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
15594 #define GAME_BUTTON_PAUSE_XPOS (GAME_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
15595 #define GAME_BUTTON_PLAY_XPOS (GAME_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
15596 #define SOUND_BUTTON_MUSIC_XPOS (SOUND_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
15597 #define SOUND_BUTTON_LOOPS_XPOS (SOUND_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
15598 #define SOUND_BUTTON_SIMPLE_XPOS (SOUND_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
15606 } gamebutton_info[NUM_GAME_BUTTONS] =
15610 &game.button.stop.x, &game.button.stop.y,
15611 GAME_BUTTON_STOP_XPOS, GAME_BUTTON_YPOS,
15616 &game.button.pause.x, &game.button.pause.y,
15617 GAME_BUTTON_PAUSE_XPOS, GAME_BUTTON_YPOS,
15618 GAME_CTRL_ID_PAUSE,
15622 &game.button.play.x, &game.button.play.y,
15623 GAME_BUTTON_PLAY_XPOS, GAME_BUTTON_YPOS,
15628 &game.button.sound_music.x, &game.button.sound_music.y,
15629 SOUND_BUTTON_MUSIC_XPOS, SOUND_BUTTON_YPOS,
15630 SOUND_CTRL_ID_MUSIC,
15631 "background music on/off"
15634 &game.button.sound_loops.x, &game.button.sound_loops.y,
15635 SOUND_BUTTON_LOOPS_XPOS, SOUND_BUTTON_YPOS,
15636 SOUND_CTRL_ID_LOOPS,
15637 "sound loops on/off"
15640 &game.button.sound_simple.x,&game.button.sound_simple.y,
15641 SOUND_BUTTON_SIMPLE_XPOS, SOUND_BUTTON_YPOS,
15642 SOUND_CTRL_ID_SIMPLE,
15643 "normal sounds on/off"
15647 GAME_BUTTON_STOP_XPOS, GAME_BUTTON_YPOS,
15652 GAME_BUTTON_PAUSE_XPOS, GAME_BUTTON_YPOS,
15653 GAME_CTRL_ID_PAUSE,
15657 GAME_BUTTON_PLAY_XPOS, GAME_BUTTON_YPOS,
15662 SOUND_BUTTON_MUSIC_XPOS, SOUND_BUTTON_YPOS,
15663 SOUND_CTRL_ID_MUSIC,
15664 "background music on/off"
15667 SOUND_BUTTON_LOOPS_XPOS, SOUND_BUTTON_YPOS,
15668 SOUND_CTRL_ID_LOOPS,
15669 "sound loops on/off"
15672 SOUND_BUTTON_SIMPLE_XPOS, SOUND_BUTTON_YPOS,
15673 SOUND_CTRL_ID_SIMPLE,
15674 "normal sounds on/off"
15679 void CreateGameButtons()
15683 for (i = 0; i < NUM_GAME_BUTTONS; i++)
15685 Bitmap *gd_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
15686 struct GadgetInfo *gi;
15689 unsigned long event_mask;
15691 int gd_xoffset, gd_yoffset;
15692 int gd_x1, gd_x2, gd_y1, gd_y2;
15695 x = DX + *gamebutton_info[i].x;
15696 y = DY + *gamebutton_info[i].y;
15697 gd_xoffset = gamebutton_info[i].gd_x;
15698 gd_yoffset = gamebutton_info[i].gd_y;
15699 gd_x1 = DOOR_GFX_PAGEX4 + gd_xoffset;
15700 gd_x2 = DOOR_GFX_PAGEX3 + gd_xoffset;
15702 if (id == GAME_CTRL_ID_STOP ||
15703 id == GAME_CTRL_ID_PAUSE ||
15704 id == GAME_CTRL_ID_PLAY)
15706 button_type = GD_TYPE_NORMAL_BUTTON;
15708 event_mask = GD_EVENT_RELEASED;
15709 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
15710 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
15714 button_type = GD_TYPE_CHECK_BUTTON;
15716 ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
15717 (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
15718 (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
15719 event_mask = GD_EVENT_PRESSED;
15720 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset;
15721 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
15724 gi = CreateGadget(GDI_CUSTOM_ID, id,
15725 GDI_INFO_TEXT, gamebutton_info[i].infotext,
15730 GDI_X, DX + gd_xoffset,
15731 GDI_Y, DY + gd_yoffset,
15733 GDI_WIDTH, GAME_BUTTON_XSIZE,
15734 GDI_HEIGHT, GAME_BUTTON_YSIZE,
15735 GDI_TYPE, button_type,
15736 GDI_STATE, GD_BUTTON_UNPRESSED,
15737 GDI_CHECKED, checked,
15738 GDI_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y1,
15739 GDI_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y1,
15740 GDI_ALT_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y2,
15741 GDI_ALT_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y2,
15742 GDI_DIRECT_DRAW, FALSE,
15743 GDI_EVENT_MASK, event_mask,
15744 GDI_CALLBACK_ACTION, HandleGameButtons,
15748 Error(ERR_EXIT, "cannot create gadget");
15750 game_gadget[id] = gi;
15754 void FreeGameButtons()
15758 for (i = 0; i < NUM_GAME_BUTTONS; i++)
15759 FreeGadget(game_gadget[i]);
15762 static void MapGameButtons()
15766 for (i = 0; i < NUM_GAME_BUTTONS; i++)
15767 MapGadget(game_gadget[i]);
15770 void UnmapGameButtons()
15774 for (i = 0; i < NUM_GAME_BUTTONS; i++)
15775 UnmapGadget(game_gadget[i]);
15778 void RedrawGameButtons()
15782 for (i = 0; i < NUM_GAME_BUTTONS; i++)
15783 RedrawGadget(game_gadget[i]);
15786 static void HandleGameButtons(struct GadgetInfo *gi)
15788 int id = gi->custom_id;
15790 if (game_status != GAME_MODE_PLAYING)
15795 case GAME_CTRL_ID_STOP:
15799 RequestQuitGame(TRUE);
15802 case GAME_CTRL_ID_PAUSE:
15803 if (options.network)
15805 #if defined(NETWORK_AVALIABLE)
15807 SendToServer_ContinuePlaying();
15809 SendToServer_PausePlaying();
15813 TapeTogglePause(TAPE_TOGGLE_MANUAL);
15816 case GAME_CTRL_ID_PLAY:
15819 #if defined(NETWORK_AVALIABLE)
15820 if (options.network)
15821 SendToServer_ContinuePlaying();
15825 tape.pausing = FALSE;
15826 DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF, 0);
15831 case SOUND_CTRL_ID_MUSIC:
15832 if (setup.sound_music)
15834 setup.sound_music = FALSE;
15837 else if (audio.music_available)
15839 setup.sound = setup.sound_music = TRUE;
15841 SetAudioMode(setup.sound);
15847 case SOUND_CTRL_ID_LOOPS:
15848 if (setup.sound_loops)
15849 setup.sound_loops = FALSE;
15850 else if (audio.loops_available)
15852 setup.sound = setup.sound_loops = TRUE;
15853 SetAudioMode(setup.sound);
15857 case SOUND_CTRL_ID_SIMPLE:
15858 if (setup.sound_simple)
15859 setup.sound_simple = FALSE;
15860 else if (audio.sound_available)
15862 setup.sound = setup.sound_simple = TRUE;
15863 SetAudioMode(setup.sound);