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"
26 #define DEBUG_INIT_PLAYER 1
27 #define DEBUG_PLAYER_ACTIONS 0
29 /* EXPERIMENTAL STUFF */
30 #define USE_NEW_AMOEBA_CODE FALSE
32 /* EXPERIMENTAL STUFF */
33 #define USE_NEW_STUFF ( 1)
35 #define USE_NEW_SP_SLIPPERY (USE_NEW_STUFF * 1)
36 #define USE_NEW_CUSTOM_VALUE (USE_NEW_STUFF * 1)
37 #define USE_NEW_PLAYER_ANIM (USE_NEW_STUFF * 1)
38 #define USE_NEW_ALL_SLIPPERY (USE_NEW_STUFF * 1)
39 #define USE_NEW_PLAYER_SPEED (USE_NEW_STUFF * 1)
40 #define USE_NEW_DELAYED_ACTION (USE_NEW_STUFF * 1)
41 #define USE_NEW_SNAP_DELAY (USE_NEW_STUFF * 1)
42 #define USE_ONLY_ONE_CHANGE_PER_FRAME (USE_NEW_STUFF * 1)
43 #define USE_ONE_MORE_CHANGE_PER_FRAME (USE_NEW_STUFF * 1)
44 #define USE_FIXED_DONT_RUN_INTO (USE_NEW_STUFF * 1)
45 #define USE_NEW_SPRING_BUMPER (USE_NEW_STUFF * 1)
46 #define USE_STOP_CHANGED_ELEMENTS (USE_NEW_STUFF * 1)
47 #define USE_ELEMENT_TOUCHING_BUGFIX (USE_NEW_STUFF * 1)
48 #define USE_NEW_CONTINUOUS_SNAPPING (USE_NEW_STUFF * 1)
49 #define USE_GFX_RESET_GFX_ANIMATION (USE_NEW_STUFF * 1)
50 #define USE_BOTH_SWITCHGATE_SWITCHES (USE_NEW_STUFF * 1)
51 #define USE_PLAYER_GRAVITY (USE_NEW_STUFF * 1)
52 #define USE_FIXED_BORDER_RUNNING_GFX (USE_NEW_STUFF * 1)
53 #define USE_QUICKSAND_BD_ROCK_BUGFIX (USE_NEW_STUFF * 0)
55 #define USE_QUICKSAND_IMPACT_BUGFIX (USE_NEW_STUFF * 0)
57 #define USE_CODE_THAT_BREAKS_SNAKE_BITE (USE_NEW_STUFF * 1)
59 #define USE_UFAST_PLAYER_EXIT_BUGFIX (USE_NEW_STUFF * 1)
61 #define USE_GFX_RESET_ONLY_WHEN_MOVING (USE_NEW_STUFF * 1)
62 #define USE_GFX_RESET_PLAYER_ARTWORK (USE_NEW_STUFF * 1)
64 #define USE_FIX_KILLED_BY_NON_WALKABLE (USE_NEW_STUFF * 1)
65 #define USE_FIX_IMPACT_COLLISION (USE_NEW_STUFF * 1)
66 #define USE_FIX_CE_ACTION_WITH_PLAYER (USE_NEW_STUFF * 1)
67 #define USE_FIX_NO_ACTION_AFTER_CHANGE (USE_NEW_STUFF * 1)
69 #define USE_PLAYER_REANIMATION (USE_NEW_STUFF * 1)
71 #define USE_GFX_RESET_WHEN_NOT_MOVING (USE_NEW_STUFF * 1)
73 #define USE_NEW_PLAYER_ASSIGNMENTS (USE_NEW_STUFF * 1)
75 #define USE_DELAYED_GFX_REDRAW (USE_NEW_STUFF * 0)
77 #if USE_DELAYED_GFX_REDRAW
78 #define TEST_DrawLevelField(x, y) \
79 GfxRedraw[x][y] |= GFX_REDRAW_TILE
80 #define TEST_DrawLevelFieldCrumbled(x, y) \
81 GfxRedraw[x][y] |= GFX_REDRAW_TILE_CRUMBLED
82 #define TEST_DrawLevelFieldCrumbledNeighbours(x, y) \
83 GfxRedraw[x][y] |= GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS
84 #define TEST_DrawTwinkleOnField(x, y) \
85 GfxRedraw[x][y] |= GFX_REDRAW_TILE_TWINKLED
87 #define TEST_DrawLevelField(x, y) \
89 #define TEST_DrawLevelFieldCrumbled(x, y) \
90 DrawLevelFieldCrumbled(x, y)
91 #define TEST_DrawLevelFieldCrumbledNeighbours(x, y) \
92 DrawLevelFieldCrumbledNeighbours(x, y)
93 #define TEST_DrawTwinkleOnField(x, y) \
94 DrawTwinkleOnField(x, y)
103 /* for MovePlayer() */
104 #define MP_NO_ACTION 0
107 #define MP_DONT_RUN_INTO (MP_MOVING | MP_ACTION)
109 /* for ScrollPlayer() */
110 #define SCROLL_INIT 0
111 #define SCROLL_GO_ON 1
113 /* for Bang()/Explode() */
114 #define EX_PHASE_START 0
115 #define EX_TYPE_NONE 0
116 #define EX_TYPE_NORMAL (1 << 0)
117 #define EX_TYPE_CENTER (1 << 1)
118 #define EX_TYPE_BORDER (1 << 2)
119 #define EX_TYPE_CROSS (1 << 3)
120 #define EX_TYPE_DYNA (1 << 4)
121 #define EX_TYPE_SINGLE_TILE (EX_TYPE_CENTER | EX_TYPE_BORDER)
123 #define PANEL_OFF() (local_player->LevelSolved_PanelOff)
124 #define PANEL_DEACTIVATED(p) ((p)->x < 0 || (p)->y < 0 || PANEL_OFF())
125 #define PANEL_XPOS(p) (DX + ALIGNED_TEXT_XPOS(p))
126 #define PANEL_YPOS(p) (DY + ALIGNED_TEXT_YPOS(p))
128 /* game panel display and control definitions */
129 #define GAME_PANEL_LEVEL_NUMBER 0
130 #define GAME_PANEL_GEMS 1
131 #define GAME_PANEL_INVENTORY_COUNT 2
132 #define GAME_PANEL_INVENTORY_FIRST_1 3
133 #define GAME_PANEL_INVENTORY_FIRST_2 4
134 #define GAME_PANEL_INVENTORY_FIRST_3 5
135 #define GAME_PANEL_INVENTORY_FIRST_4 6
136 #define GAME_PANEL_INVENTORY_FIRST_5 7
137 #define GAME_PANEL_INVENTORY_FIRST_6 8
138 #define GAME_PANEL_INVENTORY_FIRST_7 9
139 #define GAME_PANEL_INVENTORY_FIRST_8 10
140 #define GAME_PANEL_INVENTORY_LAST_1 11
141 #define GAME_PANEL_INVENTORY_LAST_2 12
142 #define GAME_PANEL_INVENTORY_LAST_3 13
143 #define GAME_PANEL_INVENTORY_LAST_4 14
144 #define GAME_PANEL_INVENTORY_LAST_5 15
145 #define GAME_PANEL_INVENTORY_LAST_6 16
146 #define GAME_PANEL_INVENTORY_LAST_7 17
147 #define GAME_PANEL_INVENTORY_LAST_8 18
148 #define GAME_PANEL_KEY_1 19
149 #define GAME_PANEL_KEY_2 20
150 #define GAME_PANEL_KEY_3 21
151 #define GAME_PANEL_KEY_4 22
152 #define GAME_PANEL_KEY_5 23
153 #define GAME_PANEL_KEY_6 24
154 #define GAME_PANEL_KEY_7 25
155 #define GAME_PANEL_KEY_8 26
156 #define GAME_PANEL_KEY_WHITE 27
157 #define GAME_PANEL_KEY_WHITE_COUNT 28
158 #define GAME_PANEL_SCORE 29
159 #define GAME_PANEL_HIGHSCORE 30
160 #define GAME_PANEL_TIME 31
161 #define GAME_PANEL_TIME_HH 32
162 #define GAME_PANEL_TIME_MM 33
163 #define GAME_PANEL_TIME_SS 34
164 #define GAME_PANEL_FRAME 35
165 #define GAME_PANEL_SHIELD_NORMAL 36
166 #define GAME_PANEL_SHIELD_NORMAL_TIME 37
167 #define GAME_PANEL_SHIELD_DEADLY 38
168 #define GAME_PANEL_SHIELD_DEADLY_TIME 39
169 #define GAME_PANEL_EXIT 40
170 #define GAME_PANEL_EMC_MAGIC_BALL 41
171 #define GAME_PANEL_EMC_MAGIC_BALL_SWITCH 42
172 #define GAME_PANEL_LIGHT_SWITCH 43
173 #define GAME_PANEL_LIGHT_SWITCH_TIME 44
174 #define GAME_PANEL_TIMEGATE_SWITCH 45
175 #define GAME_PANEL_TIMEGATE_SWITCH_TIME 46
176 #define GAME_PANEL_SWITCHGATE_SWITCH 47
177 #define GAME_PANEL_EMC_LENSES 48
178 #define GAME_PANEL_EMC_LENSES_TIME 49
179 #define GAME_PANEL_EMC_MAGNIFIER 50
180 #define GAME_PANEL_EMC_MAGNIFIER_TIME 51
181 #define GAME_PANEL_BALLOON_SWITCH 52
182 #define GAME_PANEL_DYNABOMB_NUMBER 53
183 #define GAME_PANEL_DYNABOMB_SIZE 54
184 #define GAME_PANEL_DYNABOMB_POWER 55
185 #define GAME_PANEL_PENGUINS 56
186 #define GAME_PANEL_SOKOBAN_OBJECTS 57
187 #define GAME_PANEL_SOKOBAN_FIELDS 58
188 #define GAME_PANEL_ROBOT_WHEEL 59
189 #define GAME_PANEL_CONVEYOR_BELT_1 60
190 #define GAME_PANEL_CONVEYOR_BELT_2 61
191 #define GAME_PANEL_CONVEYOR_BELT_3 62
192 #define GAME_PANEL_CONVEYOR_BELT_4 63
193 #define GAME_PANEL_CONVEYOR_BELT_1_SWITCH 64
194 #define GAME_PANEL_CONVEYOR_BELT_2_SWITCH 65
195 #define GAME_PANEL_CONVEYOR_BELT_3_SWITCH 66
196 #define GAME_PANEL_CONVEYOR_BELT_4_SWITCH 67
197 #define GAME_PANEL_MAGIC_WALL 68
198 #define GAME_PANEL_MAGIC_WALL_TIME 69
199 #define GAME_PANEL_GRAVITY_STATE 70
200 #define GAME_PANEL_GRAPHIC_1 71
201 #define GAME_PANEL_GRAPHIC_2 72
202 #define GAME_PANEL_GRAPHIC_3 73
203 #define GAME_PANEL_GRAPHIC_4 74
204 #define GAME_PANEL_GRAPHIC_5 75
205 #define GAME_PANEL_GRAPHIC_6 76
206 #define GAME_PANEL_GRAPHIC_7 77
207 #define GAME_PANEL_GRAPHIC_8 78
208 #define GAME_PANEL_ELEMENT_1 79
209 #define GAME_PANEL_ELEMENT_2 80
210 #define GAME_PANEL_ELEMENT_3 81
211 #define GAME_PANEL_ELEMENT_4 82
212 #define GAME_PANEL_ELEMENT_5 83
213 #define GAME_PANEL_ELEMENT_6 84
214 #define GAME_PANEL_ELEMENT_7 85
215 #define GAME_PANEL_ELEMENT_8 86
216 #define GAME_PANEL_ELEMENT_COUNT_1 87
217 #define GAME_PANEL_ELEMENT_COUNT_2 88
218 #define GAME_PANEL_ELEMENT_COUNT_3 89
219 #define GAME_PANEL_ELEMENT_COUNT_4 90
220 #define GAME_PANEL_ELEMENT_COUNT_5 91
221 #define GAME_PANEL_ELEMENT_COUNT_6 92
222 #define GAME_PANEL_ELEMENT_COUNT_7 93
223 #define GAME_PANEL_ELEMENT_COUNT_8 94
224 #define GAME_PANEL_CE_SCORE_1 95
225 #define GAME_PANEL_CE_SCORE_2 96
226 #define GAME_PANEL_CE_SCORE_3 97
227 #define GAME_PANEL_CE_SCORE_4 98
228 #define GAME_PANEL_CE_SCORE_5 99
229 #define GAME_PANEL_CE_SCORE_6 100
230 #define GAME_PANEL_CE_SCORE_7 101
231 #define GAME_PANEL_CE_SCORE_8 102
232 #define GAME_PANEL_CE_SCORE_1_ELEMENT 103
233 #define GAME_PANEL_CE_SCORE_2_ELEMENT 104
234 #define GAME_PANEL_CE_SCORE_3_ELEMENT 105
235 #define GAME_PANEL_CE_SCORE_4_ELEMENT 106
236 #define GAME_PANEL_CE_SCORE_5_ELEMENT 107
237 #define GAME_PANEL_CE_SCORE_6_ELEMENT 108
238 #define GAME_PANEL_CE_SCORE_7_ELEMENT 109
239 #define GAME_PANEL_CE_SCORE_8_ELEMENT 110
240 #define GAME_PANEL_PLAYER_NAME 111
241 #define GAME_PANEL_LEVEL_NAME 112
242 #define GAME_PANEL_LEVEL_AUTHOR 113
244 #define NUM_GAME_PANEL_CONTROLS 114
246 struct GamePanelOrderInfo
252 static struct GamePanelOrderInfo game_panel_order[NUM_GAME_PANEL_CONTROLS];
254 struct GamePanelControlInfo
258 struct TextPosInfo *pos;
261 int value, last_value;
262 int frame, last_frame;
267 static struct GamePanelControlInfo game_panel_controls[] =
270 GAME_PANEL_LEVEL_NUMBER,
271 &game.panel.level_number,
280 GAME_PANEL_INVENTORY_COUNT,
281 &game.panel.inventory_count,
285 GAME_PANEL_INVENTORY_FIRST_1,
286 &game.panel.inventory_first[0],
290 GAME_PANEL_INVENTORY_FIRST_2,
291 &game.panel.inventory_first[1],
295 GAME_PANEL_INVENTORY_FIRST_3,
296 &game.panel.inventory_first[2],
300 GAME_PANEL_INVENTORY_FIRST_4,
301 &game.panel.inventory_first[3],
305 GAME_PANEL_INVENTORY_FIRST_5,
306 &game.panel.inventory_first[4],
310 GAME_PANEL_INVENTORY_FIRST_6,
311 &game.panel.inventory_first[5],
315 GAME_PANEL_INVENTORY_FIRST_7,
316 &game.panel.inventory_first[6],
320 GAME_PANEL_INVENTORY_FIRST_8,
321 &game.panel.inventory_first[7],
325 GAME_PANEL_INVENTORY_LAST_1,
326 &game.panel.inventory_last[0],
330 GAME_PANEL_INVENTORY_LAST_2,
331 &game.panel.inventory_last[1],
335 GAME_PANEL_INVENTORY_LAST_3,
336 &game.panel.inventory_last[2],
340 GAME_PANEL_INVENTORY_LAST_4,
341 &game.panel.inventory_last[3],
345 GAME_PANEL_INVENTORY_LAST_5,
346 &game.panel.inventory_last[4],
350 GAME_PANEL_INVENTORY_LAST_6,
351 &game.panel.inventory_last[5],
355 GAME_PANEL_INVENTORY_LAST_7,
356 &game.panel.inventory_last[6],
360 GAME_PANEL_INVENTORY_LAST_8,
361 &game.panel.inventory_last[7],
405 GAME_PANEL_KEY_WHITE,
406 &game.panel.key_white,
410 GAME_PANEL_KEY_WHITE_COUNT,
411 &game.panel.key_white_count,
420 GAME_PANEL_HIGHSCORE,
421 &game.panel.highscore,
450 GAME_PANEL_SHIELD_NORMAL,
451 &game.panel.shield_normal,
455 GAME_PANEL_SHIELD_NORMAL_TIME,
456 &game.panel.shield_normal_time,
460 GAME_PANEL_SHIELD_DEADLY,
461 &game.panel.shield_deadly,
465 GAME_PANEL_SHIELD_DEADLY_TIME,
466 &game.panel.shield_deadly_time,
475 GAME_PANEL_EMC_MAGIC_BALL,
476 &game.panel.emc_magic_ball,
480 GAME_PANEL_EMC_MAGIC_BALL_SWITCH,
481 &game.panel.emc_magic_ball_switch,
485 GAME_PANEL_LIGHT_SWITCH,
486 &game.panel.light_switch,
490 GAME_PANEL_LIGHT_SWITCH_TIME,
491 &game.panel.light_switch_time,
495 GAME_PANEL_TIMEGATE_SWITCH,
496 &game.panel.timegate_switch,
500 GAME_PANEL_TIMEGATE_SWITCH_TIME,
501 &game.panel.timegate_switch_time,
505 GAME_PANEL_SWITCHGATE_SWITCH,
506 &game.panel.switchgate_switch,
510 GAME_PANEL_EMC_LENSES,
511 &game.panel.emc_lenses,
515 GAME_PANEL_EMC_LENSES_TIME,
516 &game.panel.emc_lenses_time,
520 GAME_PANEL_EMC_MAGNIFIER,
521 &game.panel.emc_magnifier,
525 GAME_PANEL_EMC_MAGNIFIER_TIME,
526 &game.panel.emc_magnifier_time,
530 GAME_PANEL_BALLOON_SWITCH,
531 &game.panel.balloon_switch,
535 GAME_PANEL_DYNABOMB_NUMBER,
536 &game.panel.dynabomb_number,
540 GAME_PANEL_DYNABOMB_SIZE,
541 &game.panel.dynabomb_size,
545 GAME_PANEL_DYNABOMB_POWER,
546 &game.panel.dynabomb_power,
551 &game.panel.penguins,
555 GAME_PANEL_SOKOBAN_OBJECTS,
556 &game.panel.sokoban_objects,
560 GAME_PANEL_SOKOBAN_FIELDS,
561 &game.panel.sokoban_fields,
565 GAME_PANEL_ROBOT_WHEEL,
566 &game.panel.robot_wheel,
570 GAME_PANEL_CONVEYOR_BELT_1,
571 &game.panel.conveyor_belt[0],
575 GAME_PANEL_CONVEYOR_BELT_2,
576 &game.panel.conveyor_belt[1],
580 GAME_PANEL_CONVEYOR_BELT_3,
581 &game.panel.conveyor_belt[2],
585 GAME_PANEL_CONVEYOR_BELT_4,
586 &game.panel.conveyor_belt[3],
590 GAME_PANEL_CONVEYOR_BELT_1_SWITCH,
591 &game.panel.conveyor_belt_switch[0],
595 GAME_PANEL_CONVEYOR_BELT_2_SWITCH,
596 &game.panel.conveyor_belt_switch[1],
600 GAME_PANEL_CONVEYOR_BELT_3_SWITCH,
601 &game.panel.conveyor_belt_switch[2],
605 GAME_PANEL_CONVEYOR_BELT_4_SWITCH,
606 &game.panel.conveyor_belt_switch[3],
610 GAME_PANEL_MAGIC_WALL,
611 &game.panel.magic_wall,
615 GAME_PANEL_MAGIC_WALL_TIME,
616 &game.panel.magic_wall_time,
620 GAME_PANEL_GRAVITY_STATE,
621 &game.panel.gravity_state,
625 GAME_PANEL_GRAPHIC_1,
626 &game.panel.graphic[0],
630 GAME_PANEL_GRAPHIC_2,
631 &game.panel.graphic[1],
635 GAME_PANEL_GRAPHIC_3,
636 &game.panel.graphic[2],
640 GAME_PANEL_GRAPHIC_4,
641 &game.panel.graphic[3],
645 GAME_PANEL_GRAPHIC_5,
646 &game.panel.graphic[4],
650 GAME_PANEL_GRAPHIC_6,
651 &game.panel.graphic[5],
655 GAME_PANEL_GRAPHIC_7,
656 &game.panel.graphic[6],
660 GAME_PANEL_GRAPHIC_8,
661 &game.panel.graphic[7],
665 GAME_PANEL_ELEMENT_1,
666 &game.panel.element[0],
670 GAME_PANEL_ELEMENT_2,
671 &game.panel.element[1],
675 GAME_PANEL_ELEMENT_3,
676 &game.panel.element[2],
680 GAME_PANEL_ELEMENT_4,
681 &game.panel.element[3],
685 GAME_PANEL_ELEMENT_5,
686 &game.panel.element[4],
690 GAME_PANEL_ELEMENT_6,
691 &game.panel.element[5],
695 GAME_PANEL_ELEMENT_7,
696 &game.panel.element[6],
700 GAME_PANEL_ELEMENT_8,
701 &game.panel.element[7],
705 GAME_PANEL_ELEMENT_COUNT_1,
706 &game.panel.element_count[0],
710 GAME_PANEL_ELEMENT_COUNT_2,
711 &game.panel.element_count[1],
715 GAME_PANEL_ELEMENT_COUNT_3,
716 &game.panel.element_count[2],
720 GAME_PANEL_ELEMENT_COUNT_4,
721 &game.panel.element_count[3],
725 GAME_PANEL_ELEMENT_COUNT_5,
726 &game.panel.element_count[4],
730 GAME_PANEL_ELEMENT_COUNT_6,
731 &game.panel.element_count[5],
735 GAME_PANEL_ELEMENT_COUNT_7,
736 &game.panel.element_count[6],
740 GAME_PANEL_ELEMENT_COUNT_8,
741 &game.panel.element_count[7],
745 GAME_PANEL_CE_SCORE_1,
746 &game.panel.ce_score[0],
750 GAME_PANEL_CE_SCORE_2,
751 &game.panel.ce_score[1],
755 GAME_PANEL_CE_SCORE_3,
756 &game.panel.ce_score[2],
760 GAME_PANEL_CE_SCORE_4,
761 &game.panel.ce_score[3],
765 GAME_PANEL_CE_SCORE_5,
766 &game.panel.ce_score[4],
770 GAME_PANEL_CE_SCORE_6,
771 &game.panel.ce_score[5],
775 GAME_PANEL_CE_SCORE_7,
776 &game.panel.ce_score[6],
780 GAME_PANEL_CE_SCORE_8,
781 &game.panel.ce_score[7],
785 GAME_PANEL_CE_SCORE_1_ELEMENT,
786 &game.panel.ce_score_element[0],
790 GAME_PANEL_CE_SCORE_2_ELEMENT,
791 &game.panel.ce_score_element[1],
795 GAME_PANEL_CE_SCORE_3_ELEMENT,
796 &game.panel.ce_score_element[2],
800 GAME_PANEL_CE_SCORE_4_ELEMENT,
801 &game.panel.ce_score_element[3],
805 GAME_PANEL_CE_SCORE_5_ELEMENT,
806 &game.panel.ce_score_element[4],
810 GAME_PANEL_CE_SCORE_6_ELEMENT,
811 &game.panel.ce_score_element[5],
815 GAME_PANEL_CE_SCORE_7_ELEMENT,
816 &game.panel.ce_score_element[6],
820 GAME_PANEL_CE_SCORE_8_ELEMENT,
821 &game.panel.ce_score_element[7],
825 GAME_PANEL_PLAYER_NAME,
826 &game.panel.player_name,
830 GAME_PANEL_LEVEL_NAME,
831 &game.panel.level_name,
835 GAME_PANEL_LEVEL_AUTHOR,
836 &game.panel.level_author,
847 /* values for delayed check of falling and moving elements and for collision */
848 #define CHECK_DELAY_MOVING 3
849 #define CHECK_DELAY_FALLING CHECK_DELAY_MOVING
850 #define CHECK_DELAY_COLLISION 2
851 #define CHECK_DELAY_IMPACT CHECK_DELAY_COLLISION
853 /* values for initial player move delay (initial delay counter value) */
854 #define INITIAL_MOVE_DELAY_OFF -1
855 #define INITIAL_MOVE_DELAY_ON 0
857 /* values for player movement speed (which is in fact a delay value) */
858 #define MOVE_DELAY_MIN_SPEED 32
859 #define MOVE_DELAY_NORMAL_SPEED 8
860 #define MOVE_DELAY_HIGH_SPEED 4
861 #define MOVE_DELAY_MAX_SPEED 1
863 #define DOUBLE_MOVE_DELAY(x) (x = (x < MOVE_DELAY_MIN_SPEED ? x * 2 : x))
864 #define HALVE_MOVE_DELAY(x) (x = (x > MOVE_DELAY_MAX_SPEED ? x / 2 : x))
866 #define DOUBLE_PLAYER_SPEED(p) (HALVE_MOVE_DELAY( (p)->move_delay_value))
867 #define HALVE_PLAYER_SPEED(p) (DOUBLE_MOVE_DELAY((p)->move_delay_value))
869 /* values for other actions */
870 #define MOVE_STEPSIZE_NORMAL (TILEX / MOVE_DELAY_NORMAL_SPEED)
871 #define MOVE_STEPSIZE_MIN (1)
872 #define MOVE_STEPSIZE_MAX (TILEX)
874 #define GET_DX_FROM_DIR(d) ((d) == MV_LEFT ? -1 : (d) == MV_RIGHT ? 1 : 0)
875 #define GET_DY_FROM_DIR(d) ((d) == MV_UP ? -1 : (d) == MV_DOWN ? 1 : 0)
877 #define INIT_GFX_RANDOM() (GetSimpleRandom(1000000))
879 #define GET_NEW_PUSH_DELAY(e) ( (element_info[e].push_delay_fixed) + \
880 RND(element_info[e].push_delay_random))
881 #define GET_NEW_DROP_DELAY(e) ( (element_info[e].drop_delay_fixed) + \
882 RND(element_info[e].drop_delay_random))
883 #define GET_NEW_MOVE_DELAY(e) ( (element_info[e].move_delay_fixed) + \
884 RND(element_info[e].move_delay_random))
885 #define GET_MAX_MOVE_DELAY(e) ( (element_info[e].move_delay_fixed) + \
886 (element_info[e].move_delay_random))
887 #define GET_NEW_CE_VALUE(e) ( (element_info[e].ce_value_fixed_initial) +\
888 RND(element_info[e].ce_value_random_initial))
889 #define GET_CE_SCORE(e) ( (element_info[e].collect_score))
890 #define GET_CHANGE_DELAY(c) ( ((c)->delay_fixed * (c)->delay_frames) + \
891 RND((c)->delay_random * (c)->delay_frames))
892 #define GET_CE_DELAY_VALUE(c) ( ((c)->delay_fixed) + \
893 RND((c)->delay_random))
896 #define GET_VALID_RUNTIME_ELEMENT(e) \
897 ((e) >= NUM_RUNTIME_ELEMENTS ? EL_UNKNOWN : (e))
899 #define RESOLVED_REFERENCE_ELEMENT(be, e) \
900 ((be) + (e) - EL_SELF < EL_CUSTOM_START ? EL_CUSTOM_START : \
901 (be) + (e) - EL_SELF > EL_CUSTOM_END ? EL_CUSTOM_END : \
902 (be) + (e) - EL_SELF)
904 #define GET_PLAYER_FROM_BITS(p) \
905 (EL_PLAYER_1 + ((p) != PLAYER_BITS_ANY ? log_2(p) : 0))
907 #define GET_TARGET_ELEMENT(be, e, ch, cv, cs) \
908 ((e) == EL_TRIGGER_PLAYER ? (ch)->actual_trigger_player : \
909 (e) == EL_TRIGGER_ELEMENT ? (ch)->actual_trigger_element : \
910 (e) == EL_TRIGGER_CE_VALUE ? (ch)->actual_trigger_ce_value : \
911 (e) == EL_TRIGGER_CE_SCORE ? (ch)->actual_trigger_ce_score : \
912 (e) == EL_CURRENT_CE_VALUE ? (cv) : \
913 (e) == EL_CURRENT_CE_SCORE ? (cs) : \
914 (e) >= EL_PREV_CE_8 && (e) <= EL_NEXT_CE_8 ? \
915 RESOLVED_REFERENCE_ELEMENT(be, e) : \
918 #define CAN_GROW_INTO(e) \
919 ((e) == EL_SAND || (IS_DIGGABLE(e) && level.grow_into_diggable))
921 #define ELEMENT_CAN_ENTER_FIELD_BASE_X(x, y, condition) \
922 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
925 #define ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, condition) \
926 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
927 (CAN_MOVE_INTO_ACID(e) && \
928 Feld[x][y] == EL_ACID) || \
931 #define ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, condition) \
932 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) || \
933 (CAN_MOVE_INTO_ACID(e) && \
934 Feld[x][y] == EL_ACID) || \
937 #define ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, condition) \
938 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
940 (CAN_MOVE_INTO_ACID(e) && \
941 Feld[x][y] == EL_ACID) || \
942 (DONT_COLLIDE_WITH(e) && \
944 !PLAYER_ENEMY_PROTECTED(x, y))))
946 #define ELEMENT_CAN_ENTER_FIELD(e, x, y) \
947 ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, 0)
949 #define SATELLITE_CAN_ENTER_FIELD(x, y) \
950 ELEMENT_CAN_ENTER_FIELD_BASE_2(EL_SATELLITE, x, y, 0)
952 #define ANDROID_CAN_ENTER_FIELD(e, x, y) \
953 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, Feld[x][y] == EL_EMC_PLANT)
955 #define ANDROID_CAN_CLONE_FIELD(x, y) \
956 (IN_LEV_FIELD(x, y) && (CAN_BE_CLONED_BY_ANDROID(Feld[x][y]) || \
957 CAN_BE_CLONED_BY_ANDROID(EL_TRIGGER_ELEMENT)))
959 #define ENEMY_CAN_ENTER_FIELD(e, x, y) \
960 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
962 #define YAMYAM_CAN_ENTER_FIELD(e, x, y) \
963 ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, Feld[x][y] == EL_DIAMOND)
965 #define DARK_YAMYAM_CAN_ENTER_FIELD(e, x, y) \
966 ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x,y, IS_FOOD_DARK_YAMYAM(Feld[x][y]))
968 #define PACMAN_CAN_ENTER_FIELD(e, x, y) \
969 ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, IS_AMOEBOID(Feld[x][y]))
971 #define PIG_CAN_ENTER_FIELD(e, x, y) \
972 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, IS_FOOD_PIG(Feld[x][y]))
974 #define PENGUIN_CAN_ENTER_FIELD(e, x, y) \
975 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (Feld[x][y] == EL_EXIT_OPEN || \
976 Feld[x][y] == EL_EM_EXIT_OPEN || \
977 Feld[x][y] == EL_STEEL_EXIT_OPEN || \
978 Feld[x][y] == EL_EM_STEEL_EXIT_OPEN || \
979 IS_FOOD_PENGUIN(Feld[x][y])))
980 #define DRAGON_CAN_ENTER_FIELD(e, x, y) \
981 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
983 #define MOLE_CAN_ENTER_FIELD(e, x, y, condition) \
984 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (condition))
986 #define SPRING_CAN_ENTER_FIELD(e, x, y) \
987 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
989 #define SPRING_CAN_BUMP_FROM_FIELD(x, y) \
990 (IN_LEV_FIELD(x, y) && (Feld[x][y] == EL_EMC_SPRING_BUMPER || \
991 Feld[x][y] == EL_EMC_SPRING_BUMPER_ACTIVE))
993 #define MOVE_ENTER_EL(e) (element_info[e].move_enter_element)
995 #define CE_ENTER_FIELD_COND(e, x, y) \
996 (!IS_PLAYER(x, y) && \
997 IS_EQUAL_OR_IN_GROUP(Feld[x][y], MOVE_ENTER_EL(e)))
999 #define CUSTOM_ELEMENT_CAN_ENTER_FIELD(e, x, y) \
1000 ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, CE_ENTER_FIELD_COND(e, x, y))
1002 #define IN_LEV_FIELD_AND_IS_FREE(x, y) (IN_LEV_FIELD(x, y) && IS_FREE(x, y))
1003 #define IN_LEV_FIELD_AND_NOT_FREE(x, y) (IN_LEV_FIELD(x, y) && !IS_FREE(x, y))
1005 #define ACCESS_FROM(e, d) (element_info[e].access_direction &(d))
1006 #define IS_WALKABLE_FROM(e, d) (IS_WALKABLE(e) && ACCESS_FROM(e, d))
1007 #define IS_PASSABLE_FROM(e, d) (IS_PASSABLE(e) && ACCESS_FROM(e, d))
1008 #define IS_ACCESSIBLE_FROM(e, d) (IS_ACCESSIBLE(e) && ACCESS_FROM(e, d))
1010 /* game button identifiers */
1011 #define GAME_CTRL_ID_STOP 0
1012 #define GAME_CTRL_ID_PAUSE 1
1013 #define GAME_CTRL_ID_PLAY 2
1014 #define SOUND_CTRL_ID_MUSIC 3
1015 #define SOUND_CTRL_ID_LOOPS 4
1016 #define SOUND_CTRL_ID_SIMPLE 5
1017 #define GAME_CTRL_ID_SAVE 6
1018 #define GAME_CTRL_ID_LOAD 7
1020 #define NUM_GAME_BUTTONS 8
1023 /* forward declaration for internal use */
1025 static void CreateField(int, int, int);
1027 static void ResetGfxAnimation(int, int);
1029 static void SetPlayerWaiting(struct PlayerInfo *, boolean);
1030 static void AdvanceFrameAndPlayerCounters(int);
1032 static boolean MovePlayerOneStep(struct PlayerInfo *, int, int, int, int);
1033 static boolean MovePlayer(struct PlayerInfo *, int, int);
1034 static void ScrollPlayer(struct PlayerInfo *, int);
1035 static void ScrollScreen(struct PlayerInfo *, int);
1037 static int DigField(struct PlayerInfo *, int, int, int, int, int, int, int);
1038 static boolean DigFieldByCE(int, int, int);
1039 static boolean SnapField(struct PlayerInfo *, int, int);
1040 static boolean DropElement(struct PlayerInfo *);
1042 static void InitBeltMovement(void);
1043 static void CloseAllOpenTimegates(void);
1044 static void CheckGravityMovement(struct PlayerInfo *);
1045 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *);
1046 static void KillPlayerUnlessEnemyProtected(int, int);
1047 static void KillPlayerUnlessExplosionProtected(int, int);
1049 static void TestIfPlayerTouchesCustomElement(int, int);
1050 static void TestIfElementTouchesCustomElement(int, int);
1051 static void TestIfElementHitsCustomElement(int, int, int);
1053 static void TestIfElementSmashesCustomElement(int, int, int);
1056 static void HandleElementChange(int, int, int);
1057 static void ExecuteCustomElementAction(int, int, int, int);
1058 static boolean ChangeElement(int, int, int, int);
1060 static boolean CheckTriggeredElementChangeExt(int, int, int, int, int,int,int);
1061 #define CheckTriggeredElementChange(x, y, e, ev) \
1062 CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY,CH_SIDE_ANY, -1)
1063 #define CheckTriggeredElementChangeByPlayer(x, y, e, ev, p, s) \
1064 CheckTriggeredElementChangeExt(x, y, e, ev, p, s, -1)
1065 #define CheckTriggeredElementChangeBySide(x, y, e, ev, s) \
1066 CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, s, -1)
1067 #define CheckTriggeredElementChangeByPage(x, y, e, ev, p) \
1068 CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY, CH_SIDE_ANY, p)
1070 static boolean CheckElementChangeExt(int, int, int, int, int, int, int);
1071 #define CheckElementChange(x, y, e, te, ev) \
1072 CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, CH_SIDE_ANY)
1073 #define CheckElementChangeByPlayer(x, y, e, ev, p, s) \
1074 CheckElementChangeExt(x, y, e, EL_EMPTY, ev, p, s)
1075 #define CheckElementChangeBySide(x, y, e, te, ev, s) \
1076 CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, s)
1078 static void PlayLevelSound(int, int, int);
1079 static void PlayLevelSoundNearest(int, int, int);
1080 static void PlayLevelSoundAction(int, int, int);
1081 static void PlayLevelSoundElementAction(int, int, int, int);
1082 static void PlayLevelSoundElementActionIfLoop(int, int, int, int);
1083 static void PlayLevelSoundActionIfLoop(int, int, int);
1084 static void StopLevelSoundActionIfLoop(int, int, int);
1085 static void PlayLevelMusic();
1087 static void HandleGameButtons(struct GadgetInfo *);
1089 int AmoebeNachbarNr(int, int);
1090 void AmoebeUmwandeln(int, int);
1091 void ContinueMoving(int, int);
1092 void Bang(int, int);
1093 void InitMovDir(int, int);
1094 void InitAmoebaNr(int, int);
1095 int NewHiScore(void);
1097 void TestIfGoodThingHitsBadThing(int, int, int);
1098 void TestIfBadThingHitsGoodThing(int, int, int);
1099 void TestIfPlayerTouchesBadThing(int, int);
1100 void TestIfPlayerRunsIntoBadThing(int, int, int);
1101 void TestIfBadThingTouchesPlayer(int, int);
1102 void TestIfBadThingRunsIntoPlayer(int, int, int);
1103 void TestIfFriendTouchesBadThing(int, int);
1104 void TestIfBadThingTouchesFriend(int, int);
1105 void TestIfBadThingTouchesOtherBadThing(int, int);
1106 void TestIfGoodThingGetsHitByBadThing(int, int, int);
1108 void KillPlayer(struct PlayerInfo *);
1109 void BuryPlayer(struct PlayerInfo *);
1110 void RemovePlayer(struct PlayerInfo *);
1112 static int getInvisibleActiveFromInvisibleElement(int);
1113 static int getInvisibleFromInvisibleActiveElement(int);
1115 static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
1117 /* for detection of endless loops, caused by custom element programming */
1118 /* (using maximal playfield width x 10 is just a rough approximation) */
1119 #define MAX_ELEMENT_CHANGE_RECURSION_DEPTH (MAX_PLAYFIELD_WIDTH * 10)
1121 #define RECURSION_LOOP_DETECTION_START(e, rc) \
1123 if (recursion_loop_detected) \
1126 if (recursion_loop_depth > MAX_ELEMENT_CHANGE_RECURSION_DEPTH) \
1128 recursion_loop_detected = TRUE; \
1129 recursion_loop_element = (e); \
1132 recursion_loop_depth++; \
1135 #define RECURSION_LOOP_DETECTION_END() \
1137 recursion_loop_depth--; \
1140 static int recursion_loop_depth;
1141 static boolean recursion_loop_detected;
1142 static boolean recursion_loop_element;
1144 static int map_player_action[MAX_PLAYERS];
1147 /* ------------------------------------------------------------------------- */
1148 /* definition of elements that automatically change to other elements after */
1149 /* a specified time, eventually calling a function when changing */
1150 /* ------------------------------------------------------------------------- */
1152 /* forward declaration for changer functions */
1153 static void InitBuggyBase(int, int);
1154 static void WarnBuggyBase(int, int);
1156 static void InitTrap(int, int);
1157 static void ActivateTrap(int, int);
1158 static void ChangeActiveTrap(int, int);
1160 static void InitRobotWheel(int, int);
1161 static void RunRobotWheel(int, int);
1162 static void StopRobotWheel(int, int);
1164 static void InitTimegateWheel(int, int);
1165 static void RunTimegateWheel(int, int);
1167 static void InitMagicBallDelay(int, int);
1168 static void ActivateMagicBall(int, int);
1170 struct ChangingElementInfo
1175 void (*pre_change_function)(int x, int y);
1176 void (*change_function)(int x, int y);
1177 void (*post_change_function)(int x, int y);
1180 static struct ChangingElementInfo change_delay_list[] =
1215 EL_STEEL_EXIT_OPENING,
1223 EL_STEEL_EXIT_CLOSING,
1224 EL_STEEL_EXIT_CLOSED,
1251 EL_EM_STEEL_EXIT_OPENING,
1252 EL_EM_STEEL_EXIT_OPEN,
1259 EL_EM_STEEL_EXIT_CLOSING,
1263 EL_EM_STEEL_EXIT_CLOSED,
1287 EL_SWITCHGATE_OPENING,
1295 EL_SWITCHGATE_CLOSING,
1296 EL_SWITCHGATE_CLOSED,
1303 EL_TIMEGATE_OPENING,
1311 EL_TIMEGATE_CLOSING,
1320 EL_ACID_SPLASH_LEFT,
1328 EL_ACID_SPLASH_RIGHT,
1337 EL_SP_BUGGY_BASE_ACTIVATING,
1344 EL_SP_BUGGY_BASE_ACTIVATING,
1345 EL_SP_BUGGY_BASE_ACTIVE,
1352 EL_SP_BUGGY_BASE_ACTIVE,
1376 EL_ROBOT_WHEEL_ACTIVE,
1384 EL_TIMEGATE_SWITCH_ACTIVE,
1392 EL_DC_TIMEGATE_SWITCH_ACTIVE,
1393 EL_DC_TIMEGATE_SWITCH,
1400 EL_EMC_MAGIC_BALL_ACTIVE,
1401 EL_EMC_MAGIC_BALL_ACTIVE,
1408 EL_EMC_SPRING_BUMPER_ACTIVE,
1409 EL_EMC_SPRING_BUMPER,
1416 EL_DIAGONAL_SHRINKING,
1424 EL_DIAGONAL_GROWING,
1445 int push_delay_fixed, push_delay_random;
1449 { EL_SPRING, 0, 0 },
1450 { EL_BALLOON, 0, 0 },
1452 { EL_SOKOBAN_OBJECT, 2, 0 },
1453 { EL_SOKOBAN_FIELD_FULL, 2, 0 },
1454 { EL_SATELLITE, 2, 0 },
1455 { EL_SP_DISK_YELLOW, 2, 0 },
1457 { EL_UNDEFINED, 0, 0 },
1465 move_stepsize_list[] =
1467 { EL_AMOEBA_DROP, 2 },
1468 { EL_AMOEBA_DROPPING, 2 },
1469 { EL_QUICKSAND_FILLING, 1 },
1470 { EL_QUICKSAND_EMPTYING, 1 },
1471 { EL_QUICKSAND_FAST_FILLING, 2 },
1472 { EL_QUICKSAND_FAST_EMPTYING, 2 },
1473 { EL_MAGIC_WALL_FILLING, 2 },
1474 { EL_MAGIC_WALL_EMPTYING, 2 },
1475 { EL_BD_MAGIC_WALL_FILLING, 2 },
1476 { EL_BD_MAGIC_WALL_EMPTYING, 2 },
1477 { EL_DC_MAGIC_WALL_FILLING, 2 },
1478 { EL_DC_MAGIC_WALL_EMPTYING, 2 },
1480 { EL_UNDEFINED, 0 },
1488 collect_count_list[] =
1491 { EL_BD_DIAMOND, 1 },
1492 { EL_EMERALD_YELLOW, 1 },
1493 { EL_EMERALD_RED, 1 },
1494 { EL_EMERALD_PURPLE, 1 },
1496 { EL_SP_INFOTRON, 1 },
1500 { EL_UNDEFINED, 0 },
1508 access_direction_list[] =
1510 { EL_TUBE_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1511 { EL_TUBE_VERTICAL, MV_UP | MV_DOWN },
1512 { EL_TUBE_HORIZONTAL, MV_LEFT | MV_RIGHT },
1513 { EL_TUBE_VERTICAL_LEFT, MV_LEFT | MV_UP | MV_DOWN },
1514 { EL_TUBE_VERTICAL_RIGHT, MV_RIGHT | MV_UP | MV_DOWN },
1515 { EL_TUBE_HORIZONTAL_UP, MV_LEFT | MV_RIGHT | MV_UP },
1516 { EL_TUBE_HORIZONTAL_DOWN, MV_LEFT | MV_RIGHT | MV_DOWN },
1517 { EL_TUBE_LEFT_UP, MV_LEFT | MV_UP },
1518 { EL_TUBE_LEFT_DOWN, MV_LEFT | MV_DOWN },
1519 { EL_TUBE_RIGHT_UP, MV_RIGHT | MV_UP },
1520 { EL_TUBE_RIGHT_DOWN, MV_RIGHT | MV_DOWN },
1522 { EL_SP_PORT_LEFT, MV_RIGHT },
1523 { EL_SP_PORT_RIGHT, MV_LEFT },
1524 { EL_SP_PORT_UP, MV_DOWN },
1525 { EL_SP_PORT_DOWN, MV_UP },
1526 { EL_SP_PORT_HORIZONTAL, MV_LEFT | MV_RIGHT },
1527 { EL_SP_PORT_VERTICAL, MV_UP | MV_DOWN },
1528 { EL_SP_PORT_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1529 { EL_SP_GRAVITY_PORT_LEFT, MV_RIGHT },
1530 { EL_SP_GRAVITY_PORT_RIGHT, MV_LEFT },
1531 { EL_SP_GRAVITY_PORT_UP, MV_DOWN },
1532 { EL_SP_GRAVITY_PORT_DOWN, MV_UP },
1533 { EL_SP_GRAVITY_ON_PORT_LEFT, MV_RIGHT },
1534 { EL_SP_GRAVITY_ON_PORT_RIGHT, MV_LEFT },
1535 { EL_SP_GRAVITY_ON_PORT_UP, MV_DOWN },
1536 { EL_SP_GRAVITY_ON_PORT_DOWN, MV_UP },
1537 { EL_SP_GRAVITY_OFF_PORT_LEFT, MV_RIGHT },
1538 { EL_SP_GRAVITY_OFF_PORT_RIGHT, MV_LEFT },
1539 { EL_SP_GRAVITY_OFF_PORT_UP, MV_DOWN },
1540 { EL_SP_GRAVITY_OFF_PORT_DOWN, MV_UP },
1542 { EL_UNDEFINED, MV_NONE }
1545 static boolean trigger_events[MAX_NUM_ELEMENTS][NUM_CHANGE_EVENTS];
1547 #define IS_AUTO_CHANGING(e) (element_info[e].has_change_event[CE_DELAY])
1548 #define IS_JUST_CHANGING(x, y) (ChangeDelay[x][y] != 0)
1549 #define IS_CHANGING(x, y) (IS_AUTO_CHANGING(Feld[x][y]) || \
1550 IS_JUST_CHANGING(x, y))
1552 #define CE_PAGE(e, ce) (element_info[e].event_page[ce])
1554 /* static variables for playfield scan mode (scanning forward or backward) */
1555 static int playfield_scan_start_x = 0;
1556 static int playfield_scan_start_y = 0;
1557 static int playfield_scan_delta_x = 1;
1558 static int playfield_scan_delta_y = 1;
1560 #define SCAN_PLAYFIELD(x, y) for ((y) = playfield_scan_start_y; \
1561 (y) >= 0 && (y) <= lev_fieldy - 1; \
1562 (y) += playfield_scan_delta_y) \
1563 for ((x) = playfield_scan_start_x; \
1564 (x) >= 0 && (x) <= lev_fieldx - 1; \
1565 (x) += playfield_scan_delta_x)
1568 void DEBUG_SetMaximumDynamite()
1572 for (i = 0; i < MAX_INVENTORY_SIZE; i++)
1573 if (local_player->inventory_size < MAX_INVENTORY_SIZE)
1574 local_player->inventory_element[local_player->inventory_size++] =
1579 static void InitPlayfieldScanModeVars()
1581 if (game.use_reverse_scan_direction)
1583 playfield_scan_start_x = lev_fieldx - 1;
1584 playfield_scan_start_y = lev_fieldy - 1;
1586 playfield_scan_delta_x = -1;
1587 playfield_scan_delta_y = -1;
1591 playfield_scan_start_x = 0;
1592 playfield_scan_start_y = 0;
1594 playfield_scan_delta_x = 1;
1595 playfield_scan_delta_y = 1;
1599 static void InitPlayfieldScanMode(int mode)
1601 game.use_reverse_scan_direction =
1602 (mode == CA_ARG_SCAN_MODE_REVERSE ? TRUE : FALSE);
1604 InitPlayfieldScanModeVars();
1607 static int get_move_delay_from_stepsize(int move_stepsize)
1610 MIN(MAX(MOVE_STEPSIZE_MIN, move_stepsize), MOVE_STEPSIZE_MAX);
1612 /* make sure that stepsize value is always a power of 2 */
1613 move_stepsize = (1 << log_2(move_stepsize));
1615 return TILEX / move_stepsize;
1618 static void SetPlayerMoveSpeed(struct PlayerInfo *player, int move_stepsize,
1621 int player_nr = player->index_nr;
1622 int move_delay = get_move_delay_from_stepsize(move_stepsize);
1623 boolean cannot_move = (move_stepsize == STEPSIZE_NOT_MOVING ? TRUE : FALSE);
1625 /* do no immediately change move delay -- the player might just be moving */
1626 player->move_delay_value_next = move_delay;
1628 /* information if player can move must be set separately */
1629 player->cannot_move = cannot_move;
1633 player->move_delay = game.initial_move_delay[player_nr];
1634 player->move_delay_value = game.initial_move_delay_value[player_nr];
1636 player->move_delay_value_next = -1;
1638 player->move_delay_reset_counter = 0;
1642 void GetPlayerConfig()
1644 GameFrameDelay = setup.game_frame_delay;
1646 if (!audio.sound_available)
1647 setup.sound_simple = FALSE;
1649 if (!audio.loops_available)
1650 setup.sound_loops = FALSE;
1652 if (!audio.music_available)
1653 setup.sound_music = FALSE;
1655 if (!video.fullscreen_available)
1656 setup.fullscreen = FALSE;
1658 setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
1660 SetAudioMode(setup.sound);
1664 int GetElementFromGroupElement(int element)
1666 if (IS_GROUP_ELEMENT(element))
1668 struct ElementGroupInfo *group = element_info[element].group;
1669 int last_anim_random_frame = gfx.anim_random_frame;
1672 if (group->choice_mode == ANIM_RANDOM)
1673 gfx.anim_random_frame = RND(group->num_elements_resolved);
1675 element_pos = getAnimationFrame(group->num_elements_resolved, 1,
1676 group->choice_mode, 0,
1679 if (group->choice_mode == ANIM_RANDOM)
1680 gfx.anim_random_frame = last_anim_random_frame;
1682 group->choice_pos++;
1684 element = group->element_resolved[element_pos];
1690 static void InitPlayerField(int x, int y, int element, boolean init_game)
1692 if (element == EL_SP_MURPHY)
1696 if (stored_player[0].present)
1698 Feld[x][y] = EL_SP_MURPHY_CLONE;
1704 stored_player[0].initial_element = element;
1705 stored_player[0].use_murphy = TRUE;
1707 if (!level.use_artwork_element[0])
1708 stored_player[0].artwork_element = EL_SP_MURPHY;
1711 Feld[x][y] = EL_PLAYER_1;
1717 struct PlayerInfo *player = &stored_player[Feld[x][y] - EL_PLAYER_1];
1718 int jx = player->jx, jy = player->jy;
1720 player->present = TRUE;
1722 player->block_last_field = (element == EL_SP_MURPHY ?
1723 level.sp_block_last_field :
1724 level.block_last_field);
1726 /* ---------- initialize player's last field block delay --------------- */
1728 /* always start with reliable default value (no adjustment needed) */
1729 player->block_delay_adjustment = 0;
1731 /* special case 1: in Supaplex, Murphy blocks last field one more frame */
1732 if (player->block_last_field && element == EL_SP_MURPHY)
1733 player->block_delay_adjustment = 1;
1735 /* special case 2: in game engines before 3.1.1, blocking was different */
1736 if (game.use_block_last_field_bug)
1737 player->block_delay_adjustment = (player->block_last_field ? -1 : 1);
1739 if (!options.network || player->connected)
1741 player->active = TRUE;
1743 /* remove potentially duplicate players */
1744 if (StorePlayer[jx][jy] == Feld[x][y])
1745 StorePlayer[jx][jy] = 0;
1747 StorePlayer[x][y] = Feld[x][y];
1749 #if DEBUG_INIT_PLAYER
1752 printf("- player element %d activated", player->element_nr);
1753 printf(" (local player is %d and currently %s)\n",
1754 local_player->element_nr,
1755 local_player->active ? "active" : "not active");
1760 Feld[x][y] = EL_EMPTY;
1762 player->jx = player->last_jx = x;
1763 player->jy = player->last_jy = y;
1766 #if USE_PLAYER_REANIMATION
1769 int player_nr = GET_PLAYER_NR(element);
1770 struct PlayerInfo *player = &stored_player[player_nr];
1772 if (player->active && player->killed)
1773 player->reanimated = TRUE; /* if player was just killed, reanimate him */
1778 static void InitField(int x, int y, boolean init_game)
1780 int element = Feld[x][y];
1789 InitPlayerField(x, y, element, init_game);
1792 case EL_SOKOBAN_FIELD_PLAYER:
1793 element = Feld[x][y] = EL_PLAYER_1;
1794 InitField(x, y, init_game);
1796 element = Feld[x][y] = EL_SOKOBAN_FIELD_EMPTY;
1797 InitField(x, y, init_game);
1800 case EL_SOKOBAN_FIELD_EMPTY:
1801 local_player->sokobanfields_still_needed++;
1805 if (x < lev_fieldx-1 && Feld[x+1][y] == EL_ACID)
1806 Feld[x][y] = EL_ACID_POOL_TOPLEFT;
1807 else if (x > 0 && Feld[x-1][y] == EL_ACID)
1808 Feld[x][y] = EL_ACID_POOL_TOPRIGHT;
1809 else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPLEFT)
1810 Feld[x][y] = EL_ACID_POOL_BOTTOMLEFT;
1811 else if (y > 0 && Feld[x][y-1] == EL_ACID)
1812 Feld[x][y] = EL_ACID_POOL_BOTTOM;
1813 else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPRIGHT)
1814 Feld[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
1823 case EL_SPACESHIP_RIGHT:
1824 case EL_SPACESHIP_UP:
1825 case EL_SPACESHIP_LEFT:
1826 case EL_SPACESHIP_DOWN:
1827 case EL_BD_BUTTERFLY:
1828 case EL_BD_BUTTERFLY_RIGHT:
1829 case EL_BD_BUTTERFLY_UP:
1830 case EL_BD_BUTTERFLY_LEFT:
1831 case EL_BD_BUTTERFLY_DOWN:
1833 case EL_BD_FIREFLY_RIGHT:
1834 case EL_BD_FIREFLY_UP:
1835 case EL_BD_FIREFLY_LEFT:
1836 case EL_BD_FIREFLY_DOWN:
1837 case EL_PACMAN_RIGHT:
1839 case EL_PACMAN_LEFT:
1840 case EL_PACMAN_DOWN:
1842 case EL_YAMYAM_LEFT:
1843 case EL_YAMYAM_RIGHT:
1845 case EL_YAMYAM_DOWN:
1846 case EL_DARK_YAMYAM:
1849 case EL_SP_SNIKSNAK:
1850 case EL_SP_ELECTRON:
1859 case EL_AMOEBA_FULL:
1864 case EL_AMOEBA_DROP:
1865 if (y == lev_fieldy - 1)
1867 Feld[x][y] = EL_AMOEBA_GROWING;
1868 Store[x][y] = EL_AMOEBA_WET;
1872 case EL_DYNAMITE_ACTIVE:
1873 case EL_SP_DISK_RED_ACTIVE:
1874 case EL_DYNABOMB_PLAYER_1_ACTIVE:
1875 case EL_DYNABOMB_PLAYER_2_ACTIVE:
1876 case EL_DYNABOMB_PLAYER_3_ACTIVE:
1877 case EL_DYNABOMB_PLAYER_4_ACTIVE:
1878 MovDelay[x][y] = 96;
1881 case EL_EM_DYNAMITE_ACTIVE:
1882 MovDelay[x][y] = 32;
1886 local_player->lights_still_needed++;
1890 local_player->friends_still_needed++;
1895 GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
1898 case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
1899 case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
1900 case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
1901 case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
1902 case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
1903 case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
1904 case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
1905 case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
1906 case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
1907 case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
1908 case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
1909 case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
1912 int belt_nr = getBeltNrFromBeltSwitchElement(Feld[x][y]);
1913 int belt_dir = getBeltDirFromBeltSwitchElement(Feld[x][y]);
1914 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Feld[x][y]);
1916 if (game.belt_dir_nr[belt_nr] == 3) /* initial value */
1918 game.belt_dir[belt_nr] = belt_dir;
1919 game.belt_dir_nr[belt_nr] = belt_dir_nr;
1921 else /* more than one switch -- set it like the first switch */
1923 Feld[x][y] = Feld[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
1928 #if !USE_BOTH_SWITCHGATE_SWITCHES
1929 case EL_SWITCHGATE_SWITCH_DOWN: /* always start with same switch pos */
1931 Feld[x][y] = EL_SWITCHGATE_SWITCH_UP;
1934 case EL_DC_SWITCHGATE_SWITCH_DOWN: /* always start with same switch pos */
1936 Feld[x][y] = EL_DC_SWITCHGATE_SWITCH_UP;
1940 case EL_LIGHT_SWITCH_ACTIVE:
1942 game.light_time_left = level.time_light * FRAMES_PER_SECOND;
1945 case EL_INVISIBLE_STEELWALL:
1946 case EL_INVISIBLE_WALL:
1947 case EL_INVISIBLE_SAND:
1948 if (game.light_time_left > 0 ||
1949 game.lenses_time_left > 0)
1950 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
1953 case EL_EMC_MAGIC_BALL:
1954 if (game.ball_state)
1955 Feld[x][y] = EL_EMC_MAGIC_BALL_ACTIVE;
1958 case EL_EMC_MAGIC_BALL_SWITCH:
1959 if (game.ball_state)
1960 Feld[x][y] = EL_EMC_MAGIC_BALL_SWITCH_ACTIVE;
1963 case EL_TRIGGER_PLAYER:
1964 case EL_TRIGGER_ELEMENT:
1965 case EL_TRIGGER_CE_VALUE:
1966 case EL_TRIGGER_CE_SCORE:
1968 case EL_ANY_ELEMENT:
1969 case EL_CURRENT_CE_VALUE:
1970 case EL_CURRENT_CE_SCORE:
1987 /* reference elements should not be used on the playfield */
1988 Feld[x][y] = EL_EMPTY;
1992 if (IS_CUSTOM_ELEMENT(element))
1994 if (CAN_MOVE(element))
1997 #if USE_NEW_CUSTOM_VALUE
1998 if (!element_info[element].use_last_ce_value || init_game)
1999 CustomValue[x][y] = GET_NEW_CE_VALUE(Feld[x][y]);
2002 else if (IS_GROUP_ELEMENT(element))
2004 Feld[x][y] = GetElementFromGroupElement(element);
2006 InitField(x, y, init_game);
2013 CheckTriggeredElementChange(x, y, element, CE_CREATION_OF_X);
2016 static inline void InitField_WithBug1(int x, int y, boolean init_game)
2018 InitField(x, y, init_game);
2020 /* not needed to call InitMovDir() -- already done by InitField()! */
2021 if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2022 CAN_MOVE(Feld[x][y]))
2026 static inline void InitField_WithBug2(int x, int y, boolean init_game)
2028 int old_element = Feld[x][y];
2030 InitField(x, y, init_game);
2032 /* not needed to call InitMovDir() -- already done by InitField()! */
2033 if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2034 CAN_MOVE(old_element) &&
2035 (old_element < EL_MOLE_LEFT || old_element > EL_MOLE_DOWN))
2038 /* this case is in fact a combination of not less than three bugs:
2039 first, it calls InitMovDir() for elements that can move, although this is
2040 already done by InitField(); then, it checks the element that was at this
2041 field _before_ the call to InitField() (which can change it); lastly, it
2042 was not called for "mole with direction" elements, which were treated as
2043 "cannot move" due to (fixed) wrong element initialization in "src/init.c"
2047 static int get_key_element_from_nr(int key_nr)
2049 int key_base_element = (key_nr >= STD_NUM_KEYS ? EL_EMC_KEY_5 - STD_NUM_KEYS :
2050 level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2051 EL_EM_KEY_1 : EL_KEY_1);
2053 return key_base_element + key_nr;
2056 static int get_next_dropped_element(struct PlayerInfo *player)
2058 return (player->inventory_size > 0 ?
2059 player->inventory_element[player->inventory_size - 1] :
2060 player->inventory_infinite_element != EL_UNDEFINED ?
2061 player->inventory_infinite_element :
2062 player->dynabombs_left > 0 ?
2063 EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
2067 static int get_inventory_element_from_pos(struct PlayerInfo *player, int pos)
2069 /* pos >= 0: get element from bottom of the stack;
2070 pos < 0: get element from top of the stack */
2074 int min_inventory_size = -pos;
2075 int inventory_pos = player->inventory_size - min_inventory_size;
2076 int min_dynabombs_left = min_inventory_size - player->inventory_size;
2078 return (player->inventory_size >= min_inventory_size ?
2079 player->inventory_element[inventory_pos] :
2080 player->inventory_infinite_element != EL_UNDEFINED ?
2081 player->inventory_infinite_element :
2082 player->dynabombs_left >= min_dynabombs_left ?
2083 EL_DYNABOMB_PLAYER_1 + player->index_nr :
2088 int min_dynabombs_left = pos + 1;
2089 int min_inventory_size = pos + 1 - player->dynabombs_left;
2090 int inventory_pos = pos - player->dynabombs_left;
2092 return (player->inventory_infinite_element != EL_UNDEFINED ?
2093 player->inventory_infinite_element :
2094 player->dynabombs_left >= min_dynabombs_left ?
2095 EL_DYNABOMB_PLAYER_1 + player->index_nr :
2096 player->inventory_size >= min_inventory_size ?
2097 player->inventory_element[inventory_pos] :
2102 static int compareGamePanelOrderInfo(const void *object1, const void *object2)
2104 const struct GamePanelOrderInfo *gpo1 = (struct GamePanelOrderInfo *)object1;
2105 const struct GamePanelOrderInfo *gpo2 = (struct GamePanelOrderInfo *)object2;
2108 if (gpo1->sort_priority != gpo2->sort_priority)
2109 compare_result = gpo1->sort_priority - gpo2->sort_priority;
2111 compare_result = gpo1->nr - gpo2->nr;
2113 return compare_result;
2116 void InitGameControlValues()
2120 for (i = 0; game_panel_controls[i].nr != -1; i++)
2122 struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2123 struct GamePanelOrderInfo *gpo = &game_panel_order[i];
2124 struct TextPosInfo *pos = gpc->pos;
2126 int type = gpc->type;
2130 Error(ERR_INFO, "'game_panel_controls' structure corrupted at %d", i);
2131 Error(ERR_EXIT, "this should not happen -- please debug");
2134 /* force update of game controls after initialization */
2135 gpc->value = gpc->last_value = -1;
2136 gpc->frame = gpc->last_frame = -1;
2137 gpc->gfx_frame = -1;
2139 /* determine panel value width for later calculation of alignment */
2140 if (type == TYPE_INTEGER || type == TYPE_STRING)
2142 pos->width = pos->size * getFontWidth(pos->font);
2143 pos->height = getFontHeight(pos->font);
2145 else if (type == TYPE_ELEMENT)
2147 pos->width = pos->size;
2148 pos->height = pos->size;
2151 /* fill structure for game panel draw order */
2153 gpo->sort_priority = pos->sort_priority;
2156 /* sort game panel controls according to sort_priority and control number */
2157 qsort(game_panel_order, NUM_GAME_PANEL_CONTROLS,
2158 sizeof(struct GamePanelOrderInfo), compareGamePanelOrderInfo);
2161 void UpdatePlayfieldElementCount()
2163 boolean use_element_count = FALSE;
2166 /* first check if it is needed at all to calculate playfield element count */
2167 for (i = GAME_PANEL_ELEMENT_COUNT_1; i <= GAME_PANEL_ELEMENT_COUNT_8; i++)
2168 if (!PANEL_DEACTIVATED(game_panel_controls[i].pos))
2169 use_element_count = TRUE;
2171 if (!use_element_count)
2174 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2175 element_info[i].element_count = 0;
2177 SCAN_PLAYFIELD(x, y)
2179 element_info[Feld[x][y]].element_count++;
2182 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
2183 for (j = 0; j < MAX_NUM_ELEMENTS; j++)
2184 if (IS_IN_GROUP(j, i))
2185 element_info[EL_GROUP_START + i].element_count +=
2186 element_info[j].element_count;
2189 void UpdateGameControlValues()
2192 int time = (local_player->LevelSolved ?
2193 local_player->LevelSolved_CountingTime :
2194 level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2195 level.native_em_level->lev->time :
2196 level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2197 level.native_sp_level->game_sp->time_played :
2198 game.no_time_limit ? TimePlayed : TimeLeft);
2199 int score = (local_player->LevelSolved ?
2200 local_player->LevelSolved_CountingScore :
2201 level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2202 level.native_em_level->lev->score :
2203 level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2204 level.native_sp_level->game_sp->score :
2205 local_player->score);
2206 int gems = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2207 level.native_em_level->lev->required :
2208 level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2209 level.native_sp_level->game_sp->infotrons_still_needed :
2210 local_player->gems_still_needed);
2211 int exit_closed = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2212 level.native_em_level->lev->required > 0 :
2213 level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2214 level.native_sp_level->game_sp->infotrons_still_needed > 0 :
2215 local_player->gems_still_needed > 0 ||
2216 local_player->sokobanfields_still_needed > 0 ||
2217 local_player->lights_still_needed > 0);
2219 UpdatePlayfieldElementCount();
2221 /* update game panel control values */
2223 game_panel_controls[GAME_PANEL_LEVEL_NUMBER].value = level_nr;
2224 game_panel_controls[GAME_PANEL_GEMS].value = gems;
2226 game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value = 0;
2227 for (i = 0; i < MAX_NUM_KEYS; i++)
2228 game_panel_controls[GAME_PANEL_KEY_1 + i].value = EL_EMPTY;
2229 game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_EMPTY;
2230 game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value = 0;
2232 if (game.centered_player_nr == -1)
2234 for (i = 0; i < MAX_PLAYERS; i++)
2236 /* only one player in Supaplex game engine */
2237 if (level.game_engine_type == GAME_ENGINE_TYPE_SP && i > 0)
2240 for (k = 0; k < MAX_NUM_KEYS; k++)
2242 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2244 if (level.native_em_level->ply[i]->keys & (1 << k))
2245 game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2246 get_key_element_from_nr(k);
2248 else if (stored_player[i].key[k])
2249 game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2250 get_key_element_from_nr(k);
2253 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2254 game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2255 level.native_em_level->ply[i]->dynamite;
2256 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
2257 game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2258 level.native_sp_level->game_sp->red_disk_count;
2260 game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2261 stored_player[i].inventory_size;
2263 if (stored_player[i].num_white_keys > 0)
2264 game_panel_controls[GAME_PANEL_KEY_WHITE].value =
2267 game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2268 stored_player[i].num_white_keys;
2273 int player_nr = game.centered_player_nr;
2275 for (k = 0; k < MAX_NUM_KEYS; k++)
2277 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2279 if (level.native_em_level->ply[player_nr]->keys & (1 << k))
2280 game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2281 get_key_element_from_nr(k);
2283 else if (stored_player[player_nr].key[k])
2284 game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2285 get_key_element_from_nr(k);
2288 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2289 game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2290 level.native_em_level->ply[player_nr]->dynamite;
2291 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
2292 game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2293 level.native_sp_level->game_sp->red_disk_count;
2295 game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2296 stored_player[player_nr].inventory_size;
2298 if (stored_player[player_nr].num_white_keys > 0)
2299 game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_DC_KEY_WHITE;
2301 game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2302 stored_player[player_nr].num_white_keys;
2305 for (i = 0; i < NUM_PANEL_INVENTORY; i++)
2307 game_panel_controls[GAME_PANEL_INVENTORY_FIRST_1 + i].value =
2308 get_inventory_element_from_pos(local_player, i);
2309 game_panel_controls[GAME_PANEL_INVENTORY_LAST_1 + i].value =
2310 get_inventory_element_from_pos(local_player, -i - 1);
2313 game_panel_controls[GAME_PANEL_SCORE].value = score;
2314 game_panel_controls[GAME_PANEL_HIGHSCORE].value = highscore[0].Score;
2316 game_panel_controls[GAME_PANEL_TIME].value = time;
2318 game_panel_controls[GAME_PANEL_TIME_HH].value = time / 3600;
2319 game_panel_controls[GAME_PANEL_TIME_MM].value = (time / 60) % 60;
2320 game_panel_controls[GAME_PANEL_TIME_SS].value = time % 60;
2322 game_panel_controls[GAME_PANEL_FRAME].value = FrameCounter;
2324 game_panel_controls[GAME_PANEL_SHIELD_NORMAL].value =
2325 (local_player->shield_normal_time_left > 0 ? EL_SHIELD_NORMAL_ACTIVE :
2327 game_panel_controls[GAME_PANEL_SHIELD_NORMAL_TIME].value =
2328 local_player->shield_normal_time_left;
2329 game_panel_controls[GAME_PANEL_SHIELD_DEADLY].value =
2330 (local_player->shield_deadly_time_left > 0 ? EL_SHIELD_DEADLY_ACTIVE :
2332 game_panel_controls[GAME_PANEL_SHIELD_DEADLY_TIME].value =
2333 local_player->shield_deadly_time_left;
2335 game_panel_controls[GAME_PANEL_EXIT].value =
2336 (exit_closed ? EL_EXIT_CLOSED : EL_EXIT_OPEN);
2338 game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL].value =
2339 (game.ball_state ? EL_EMC_MAGIC_BALL_ACTIVE : EL_EMC_MAGIC_BALL);
2340 game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL_SWITCH].value =
2341 (game.ball_state ? EL_EMC_MAGIC_BALL_SWITCH_ACTIVE :
2342 EL_EMC_MAGIC_BALL_SWITCH);
2344 game_panel_controls[GAME_PANEL_LIGHT_SWITCH].value =
2345 (game.light_time_left > 0 ? EL_LIGHT_SWITCH_ACTIVE : EL_LIGHT_SWITCH);
2346 game_panel_controls[GAME_PANEL_LIGHT_SWITCH_TIME].value =
2347 game.light_time_left;
2349 game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH].value =
2350 (game.timegate_time_left > 0 ? EL_TIMEGATE_OPEN : EL_TIMEGATE_CLOSED);
2351 game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH_TIME].value =
2352 game.timegate_time_left;
2354 game_panel_controls[GAME_PANEL_SWITCHGATE_SWITCH].value =
2355 EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
2357 game_panel_controls[GAME_PANEL_EMC_LENSES].value =
2358 (game.lenses_time_left > 0 ? EL_EMC_LENSES : EL_EMPTY);
2359 game_panel_controls[GAME_PANEL_EMC_LENSES_TIME].value =
2360 game.lenses_time_left;
2362 game_panel_controls[GAME_PANEL_EMC_MAGNIFIER].value =
2363 (game.magnify_time_left > 0 ? EL_EMC_MAGNIFIER : EL_EMPTY);
2364 game_panel_controls[GAME_PANEL_EMC_MAGNIFIER_TIME].value =
2365 game.magnify_time_left;
2367 game_panel_controls[GAME_PANEL_BALLOON_SWITCH].value =
2368 (game.wind_direction == MV_LEFT ? EL_BALLOON_SWITCH_LEFT :
2369 game.wind_direction == MV_RIGHT ? EL_BALLOON_SWITCH_RIGHT :
2370 game.wind_direction == MV_UP ? EL_BALLOON_SWITCH_UP :
2371 game.wind_direction == MV_DOWN ? EL_BALLOON_SWITCH_DOWN :
2372 EL_BALLOON_SWITCH_NONE);
2374 game_panel_controls[GAME_PANEL_DYNABOMB_NUMBER].value =
2375 local_player->dynabomb_count;
2376 game_panel_controls[GAME_PANEL_DYNABOMB_SIZE].value =
2377 local_player->dynabomb_size;
2378 game_panel_controls[GAME_PANEL_DYNABOMB_POWER].value =
2379 (local_player->dynabomb_xl ? EL_DYNABOMB_INCREASE_POWER : EL_EMPTY);
2381 game_panel_controls[GAME_PANEL_PENGUINS].value =
2382 local_player->friends_still_needed;
2384 game_panel_controls[GAME_PANEL_SOKOBAN_OBJECTS].value =
2385 local_player->sokobanfields_still_needed;
2386 game_panel_controls[GAME_PANEL_SOKOBAN_FIELDS].value =
2387 local_player->sokobanfields_still_needed;
2389 game_panel_controls[GAME_PANEL_ROBOT_WHEEL].value =
2390 (game.robot_wheel_active ? EL_ROBOT_WHEEL_ACTIVE : EL_ROBOT_WHEEL);
2392 for (i = 0; i < NUM_BELTS; i++)
2394 game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1 + i].value =
2395 (game.belt_dir[i] != MV_NONE ? EL_CONVEYOR_BELT_1_MIDDLE_ACTIVE :
2396 EL_CONVEYOR_BELT_1_MIDDLE) + i;
2397 game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1_SWITCH + i].value =
2398 getBeltSwitchElementFromBeltNrAndBeltDir(i, game.belt_dir[i]);
2401 game_panel_controls[GAME_PANEL_MAGIC_WALL].value =
2402 (game.magic_wall_active ? EL_MAGIC_WALL_ACTIVE : EL_MAGIC_WALL);
2403 game_panel_controls[GAME_PANEL_MAGIC_WALL_TIME].value =
2404 game.magic_wall_time_left;
2406 #if USE_PLAYER_GRAVITY
2407 game_panel_controls[GAME_PANEL_GRAVITY_STATE].value =
2408 local_player->gravity;
2410 game_panel_controls[GAME_PANEL_GRAVITY_STATE].value = game.gravity;
2413 for (i = 0; i < NUM_PANEL_GRAPHICS; i++)
2414 game_panel_controls[GAME_PANEL_GRAPHIC_1 + i].value = EL_GRAPHIC_1 + i;
2416 for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2417 game_panel_controls[GAME_PANEL_ELEMENT_1 + i].value =
2418 (IS_DRAWABLE_ELEMENT(game.panel.element[i].id) ?
2419 game.panel.element[i].id : EL_UNDEFINED);
2421 for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2422 game_panel_controls[GAME_PANEL_ELEMENT_COUNT_1 + i].value =
2423 (IS_VALID_ELEMENT(game.panel.element_count[i].id) ?
2424 element_info[game.panel.element_count[i].id].element_count : 0);
2426 for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2427 game_panel_controls[GAME_PANEL_CE_SCORE_1 + i].value =
2428 (IS_CUSTOM_ELEMENT(game.panel.ce_score[i].id) ?
2429 element_info[game.panel.ce_score[i].id].collect_score : 0);
2431 for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2432 game_panel_controls[GAME_PANEL_CE_SCORE_1_ELEMENT + i].value =
2433 (IS_CUSTOM_ELEMENT(game.panel.ce_score_element[i].id) ?
2434 element_info[game.panel.ce_score_element[i].id].collect_score :
2437 game_panel_controls[GAME_PANEL_PLAYER_NAME].value = 0;
2438 game_panel_controls[GAME_PANEL_LEVEL_NAME].value = 0;
2439 game_panel_controls[GAME_PANEL_LEVEL_AUTHOR].value = 0;
2441 /* update game panel control frames */
2443 for (i = 0; game_panel_controls[i].nr != -1; i++)
2445 struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2447 if (gpc->type == TYPE_ELEMENT)
2449 if (gpc->value != EL_UNDEFINED && gpc->value != EL_EMPTY)
2451 int last_anim_random_frame = gfx.anim_random_frame;
2452 int element = gpc->value;
2453 int graphic = el2panelimg(element);
2455 if (gpc->value != gpc->last_value)
2458 gpc->gfx_random = INIT_GFX_RANDOM();
2464 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2465 IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2466 gpc->gfx_random = INIT_GFX_RANDOM();
2469 if (ANIM_MODE(graphic) == ANIM_RANDOM)
2470 gfx.anim_random_frame = gpc->gfx_random;
2472 if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
2473 gpc->gfx_frame = element_info[element].collect_score;
2475 gpc->frame = getGraphicAnimationFrame(el2panelimg(gpc->value),
2478 if (ANIM_MODE(graphic) == ANIM_RANDOM)
2479 gfx.anim_random_frame = last_anim_random_frame;
2485 void DisplayGameControlValues()
2487 boolean redraw_panel = FALSE;
2490 for (i = 0; game_panel_controls[i].nr != -1; i++)
2492 struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2494 if (PANEL_DEACTIVATED(gpc->pos))
2497 if (gpc->value == gpc->last_value &&
2498 gpc->frame == gpc->last_frame)
2501 redraw_panel = TRUE;
2507 /* copy default game door content to main double buffer */
2509 /* !!! CHECK AGAIN !!! */
2510 SetPanelBackground();
2511 // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
2512 DrawBackground(DX, DY, DXSIZE, DYSIZE);
2514 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
2515 DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
2518 /* redraw game control buttons */
2520 RedrawGameButtons();
2526 game_status = GAME_MODE_PSEUDO_PANEL;
2529 for (i = 0; i < NUM_GAME_PANEL_CONTROLS; i++)
2531 for (i = 0; game_panel_controls[i].nr != -1; i++)
2535 int nr = game_panel_order[i].nr;
2536 struct GamePanelControlInfo *gpc = &game_panel_controls[nr];
2538 struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2541 struct TextPosInfo *pos = gpc->pos;
2542 int type = gpc->type;
2543 int value = gpc->value;
2544 int frame = gpc->frame;
2546 int last_value = gpc->last_value;
2547 int last_frame = gpc->last_frame;
2549 int size = pos->size;
2550 int font = pos->font;
2551 boolean draw_masked = pos->draw_masked;
2552 int mask_mode = (draw_masked ? BLIT_MASKED : BLIT_OPAQUE);
2554 if (PANEL_DEACTIVATED(pos))
2558 if (value == last_value && frame == last_frame)
2562 gpc->last_value = value;
2563 gpc->last_frame = frame;
2566 printf("::: value %d changed from %d to %d\n", nr, last_value, value);
2569 if (type == TYPE_INTEGER)
2571 if (nr == GAME_PANEL_LEVEL_NUMBER ||
2572 nr == GAME_PANEL_TIME)
2574 boolean use_dynamic_size = (size == -1 ? TRUE : FALSE);
2576 if (use_dynamic_size) /* use dynamic number of digits */
2578 int value_change = (nr == GAME_PANEL_LEVEL_NUMBER ? 100 : 1000);
2579 int size1 = (nr == GAME_PANEL_LEVEL_NUMBER ? 2 : 3);
2580 int size2 = size1 + 1;
2581 int font1 = pos->font;
2582 int font2 = pos->font_alt;
2584 size = (value < value_change ? size1 : size2);
2585 font = (value < value_change ? font1 : font2);
2588 /* clear background if value just changed its size (dynamic digits) */
2589 if ((last_value < value_change) != (value < value_change))
2591 int width1 = size1 * getFontWidth(font1);
2592 int width2 = size2 * getFontWidth(font2);
2593 int max_width = MAX(width1, width2);
2594 int max_height = MAX(getFontHeight(font1), getFontHeight(font2));
2596 pos->width = max_width;
2598 ClearRectangleOnBackground(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2599 max_width, max_height);
2606 /* correct text size if "digits" is zero or less */
2608 size = strlen(int2str(value, size));
2610 /* dynamically correct text alignment */
2611 pos->width = size * getFontWidth(font);
2614 DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2615 int2str(value, size), font, mask_mode);
2617 else if (type == TYPE_ELEMENT)
2619 int element, graphic;
2623 int dst_x = PANEL_XPOS(pos);
2624 int dst_y = PANEL_YPOS(pos);
2627 if (value != EL_UNDEFINED && value != EL_EMPTY)
2630 graphic = el2panelimg(value);
2632 // printf("::: %d, '%s' [%d]\n", element, EL_NAME(element), size);
2635 if (element >= EL_GRAPHIC_1 && element <= EL_GRAPHIC_8 && size == 0)
2639 getSizedGraphicSource(graphic, frame, size, &src_bitmap,
2642 width = graphic_info[graphic].width * size / TILESIZE;
2643 height = graphic_info[graphic].height * size / TILESIZE;
2647 SetClipOrigin(src_bitmap, src_bitmap->stored_clip_gc,
2648 dst_x - src_x, dst_y - src_y);
2649 BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2654 BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2659 if (value == EL_UNDEFINED || value == EL_EMPTY)
2661 element = (last_value == EL_UNDEFINED ? EL_EMPTY : last_value);
2662 graphic = el2panelimg(element);
2664 src_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
2665 src_x = DOOR_GFX_PAGEX5 + ALIGNED_TEXT_XPOS(pos);
2666 src_y = DOOR_GFX_PAGEY1 + ALIGNED_TEXT_YPOS(pos);
2671 graphic = el2panelimg(value);
2673 getSizedGraphicSource(graphic, frame, size, &src_bitmap, &src_x,&src_y);
2676 width = graphic_info[graphic].width * size / TILESIZE;
2677 height = graphic_info[graphic].height * size / TILESIZE;
2679 BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height, dst_x, dst_y);
2682 else if (type == TYPE_STRING)
2684 boolean active = (value != 0);
2685 char *state_normal = "off";
2686 char *state_active = "on";
2687 char *state = (active ? state_active : state_normal);
2688 char *s = (nr == GAME_PANEL_GRAVITY_STATE ? state :
2689 nr == GAME_PANEL_PLAYER_NAME ? setup.player_name :
2690 nr == GAME_PANEL_LEVEL_NAME ? level.name :
2691 nr == GAME_PANEL_LEVEL_AUTHOR ? level.author : NULL);
2693 if (nr == GAME_PANEL_GRAVITY_STATE)
2695 int font1 = pos->font; /* (used for normal state) */
2696 int font2 = pos->font_alt; /* (used for active state) */
2698 int size1 = strlen(state_normal);
2699 int size2 = strlen(state_active);
2700 int width1 = size1 * getFontWidth(font1);
2701 int width2 = size2 * getFontWidth(font2);
2702 int max_width = MAX(width1, width2);
2703 int max_height = MAX(getFontHeight(font1), getFontHeight(font2));
2705 pos->width = max_width;
2707 /* clear background for values that may have changed its size */
2708 ClearRectangleOnBackground(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2709 max_width, max_height);
2712 font = (active ? font2 : font1);
2722 /* don't truncate output if "chars" is zero or less */
2725 /* dynamically correct text alignment */
2726 pos->width = size * getFontWidth(font);
2730 s_cut = getStringCopyN(s, size);
2732 DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2733 s_cut, font, mask_mode);
2739 redraw_mask |= REDRAW_DOOR_1;
2742 game_status = GAME_MODE_PLAYING;
2745 void UpdateAndDisplayGameControlValues()
2747 if (tape.warp_forward)
2750 UpdateGameControlValues();
2751 DisplayGameControlValues();
2754 void DrawGameValue_Emeralds(int value)
2756 struct TextPosInfo *pos = &game.panel.gems;
2757 int font_nr = pos->font;
2758 int font_width = getFontWidth(font_nr);
2759 int chars = pos->size;
2762 return; /* !!! USE NEW STUFF !!! */
2765 if (PANEL_DEACTIVATED(pos))
2768 pos->width = chars * font_width;
2770 DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2773 void DrawGameValue_Dynamite(int value)
2775 struct TextPosInfo *pos = &game.panel.inventory_count;
2776 int font_nr = pos->font;
2777 int font_width = getFontWidth(font_nr);
2778 int chars = pos->size;
2781 return; /* !!! USE NEW STUFF !!! */
2784 if (PANEL_DEACTIVATED(pos))
2787 pos->width = chars * font_width;
2789 DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2792 void DrawGameValue_Score(int value)
2794 struct TextPosInfo *pos = &game.panel.score;
2795 int font_nr = pos->font;
2796 int font_width = getFontWidth(font_nr);
2797 int chars = pos->size;
2800 return; /* !!! USE NEW STUFF !!! */
2803 if (PANEL_DEACTIVATED(pos))
2806 pos->width = chars * font_width;
2808 DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2811 void DrawGameValue_Time(int value)
2813 struct TextPosInfo *pos = &game.panel.time;
2814 static int last_value = -1;
2817 int chars = pos->size;
2818 int font1_nr = pos->font;
2819 int font2_nr = pos->font_alt;
2820 int font_nr = font1_nr;
2821 boolean use_dynamic_chars = (chars == -1 ? TRUE : FALSE);
2824 return; /* !!! USE NEW STUFF !!! */
2827 if (PANEL_DEACTIVATED(pos))
2830 if (use_dynamic_chars) /* use dynamic number of chars */
2832 chars = (value < 1000 ? chars1 : chars2);
2833 font_nr = (value < 1000 ? font1_nr : font2_nr);
2836 /* clear background if value just changed its size (dynamic chars only) */
2837 if (use_dynamic_chars && (last_value < 1000) != (value < 1000))
2839 int width1 = chars1 * getFontWidth(font1_nr);
2840 int width2 = chars2 * getFontWidth(font2_nr);
2841 int max_width = MAX(width1, width2);
2842 int max_height = MAX(getFontHeight(font1_nr), getFontHeight(font2_nr));
2844 pos->width = max_width;
2846 ClearRectangleOnBackground(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2847 max_width, max_height);
2850 pos->width = chars * getFontWidth(font_nr);
2852 DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2857 void DrawGameValue_Level(int value)
2859 struct TextPosInfo *pos = &game.panel.level_number;
2862 int chars = pos->size;
2863 int font1_nr = pos->font;
2864 int font2_nr = pos->font_alt;
2865 int font_nr = font1_nr;
2866 boolean use_dynamic_chars = (chars == -1 ? TRUE : FALSE);
2869 return; /* !!! USE NEW STUFF !!! */
2872 if (PANEL_DEACTIVATED(pos))
2875 if (use_dynamic_chars) /* use dynamic number of chars */
2877 chars = (level_nr < 100 ? chars1 : chars2);
2878 font_nr = (level_nr < 100 ? font1_nr : font2_nr);
2881 pos->width = chars * getFontWidth(font_nr);
2883 DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2886 void DrawGameValue_Keys(int key[MAX_NUM_KEYS])
2891 return; /* !!! USE NEW STUFF !!! */
2894 for (i = 0; i < MAX_NUM_KEYS; i++)
2896 struct TextPosInfo *pos = &game.panel.key[i];
2897 int src_x = DOOR_GFX_PAGEX5 + 18 + (i % 4) * MINI_TILEX;
2898 int src_y = DOOR_GFX_PAGEY1 + 123;
2899 int dst_x = PANEL_XPOS(pos);
2900 int dst_y = PANEL_YPOS(pos);
2902 int element = (i >= STD_NUM_KEYS ? EL_EMC_KEY_5 - 4 :
2903 level.game_engine_type == GAME_ENGINE_TYPE_EM ? EL_EM_KEY_1 :
2905 int graphic = el2edimg(element);
2907 if (PANEL_DEACTIVATED(pos))
2911 /* masked blit with tiles from half-size scaled bitmap does not work yet
2912 (no mask bitmap created for these sizes after loading and scaling) --
2913 solution: load without creating mask, scale, then create final mask */
2915 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto, src_x, src_y,
2916 MINI_TILEX, MINI_TILEY, dst_x, dst_y);
2923 getMiniGraphicSource(graphic, &src_bitmap, &src_x, &src_y);
2925 SetClipOrigin(src_bitmap, src_bitmap->stored_clip_gc,
2926 dst_x - src_x, dst_y - src_y);
2927 BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, MINI_TILEX, MINI_TILEY,
2932 DrawMiniGraphicExt(drawto, dst_x, dst_y, graphic);
2934 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto, src_x, src_y,
2935 MINI_TILEX, MINI_TILEY, dst_x, dst_y);
2940 void DrawAllGameValues(int emeralds, int dynamite, int score, int time,
2943 int key[MAX_NUM_KEYS];
2946 /* prevent EM engine from updating time/score values parallel to GameWon() */
2947 if (level.game_engine_type == GAME_ENGINE_TYPE_EM &&
2948 local_player->LevelSolved)
2951 for (i = 0; i < MAX_NUM_KEYS; i++)
2952 key[i] = key_bits & (1 << i);
2954 DrawGameValue_Level(level_nr);
2956 DrawGameValue_Emeralds(emeralds);
2957 DrawGameValue_Dynamite(dynamite);
2958 DrawGameValue_Score(score);
2959 DrawGameValue_Time(time);
2961 DrawGameValue_Keys(key);
2964 void UpdateGameDoorValues()
2966 UpdateGameControlValues();
2969 void DrawGameDoorValues()
2971 DisplayGameControlValues();
2974 void DrawGameDoorValues_OLD()
2976 int time_value = (game.no_time_limit ? TimePlayed : TimeLeft);
2977 int dynamite_value = 0;
2978 int score_value = (local_player->LevelSolved ? local_player->score_final :
2979 local_player->score);
2980 int gems_value = local_player->gems_still_needed;
2984 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2986 DrawGameDoorValues_EM();
2991 if (game.centered_player_nr == -1)
2993 for (i = 0; i < MAX_PLAYERS; i++)
2995 for (j = 0; j < MAX_NUM_KEYS; j++)
2996 if (stored_player[i].key[j])
2997 key_bits |= (1 << j);
2999 dynamite_value += stored_player[i].inventory_size;
3004 int player_nr = game.centered_player_nr;
3006 for (i = 0; i < MAX_NUM_KEYS; i++)
3007 if (stored_player[player_nr].key[i])
3008 key_bits |= (1 << i);
3010 dynamite_value = stored_player[player_nr].inventory_size;
3013 DrawAllGameValues(gems_value, dynamite_value, score_value, time_value,
3019 =============================================================================
3021 -----------------------------------------------------------------------------
3022 initialize game engine due to level / tape version number
3023 =============================================================================
3026 static void InitGameEngine()
3028 int i, j, k, l, x, y;
3030 /* set game engine from tape file when re-playing, else from level file */
3031 game.engine_version = (tape.playing ? tape.engine_version :
3032 level.game_version);
3034 /* set single or multi-player game mode (needed for re-playing tapes) */
3035 game.team_mode = setup.team_mode;
3039 int num_players = 0;
3041 for (i = 0; i < MAX_PLAYERS; i++)
3042 if (tape.player_participates[i])
3045 /* multi-player tapes contain input data for more than one player */
3046 game.team_mode = (num_players > 1);
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];
3270 /* ---------- initialize reference elements in change conditions --------- */
3272 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3274 int element = EL_CUSTOM_START + i;
3275 struct ElementInfo *ei = &element_info[element];
3277 for (j = 0; j < ei->num_change_pages; j++)
3279 int trigger_element = ei->change_page[j].initial_trigger_element;
3281 if (trigger_element >= EL_PREV_CE_8 &&
3282 trigger_element <= EL_NEXT_CE_8)
3283 trigger_element = RESOLVED_REFERENCE_ELEMENT(element, trigger_element);
3285 ei->change_page[j].trigger_element = trigger_element;
3290 /* ---------- initialize run-time trigger player and element ------------- */
3292 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3294 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3296 for (j = 0; j < ei->num_change_pages; j++)
3298 ei->change_page[j].actual_trigger_element = EL_EMPTY;
3299 ei->change_page[j].actual_trigger_player = EL_EMPTY;
3300 ei->change_page[j].actual_trigger_player_bits = CH_PLAYER_NONE;
3301 ei->change_page[j].actual_trigger_side = CH_SIDE_NONE;
3302 ei->change_page[j].actual_trigger_ce_value = 0;
3303 ei->change_page[j].actual_trigger_ce_score = 0;
3307 /* ---------- initialize trigger events ---------------------------------- */
3309 /* initialize trigger events information */
3310 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3311 for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3312 trigger_events[i][j] = FALSE;
3314 /* add trigger events from element change event properties */
3315 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3317 struct ElementInfo *ei = &element_info[i];
3319 for (j = 0; j < ei->num_change_pages; j++)
3321 if (!ei->change_page[j].can_change_or_has_action)
3324 if (ei->change_page[j].has_event[CE_BY_OTHER_ACTION])
3326 int trigger_element = ei->change_page[j].trigger_element;
3328 for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3330 if (ei->change_page[j].has_event[k])
3332 if (IS_GROUP_ELEMENT(trigger_element))
3334 struct ElementGroupInfo *group =
3335 element_info[trigger_element].group;
3337 for (l = 0; l < group->num_elements_resolved; l++)
3338 trigger_events[group->element_resolved[l]][k] = TRUE;
3340 else if (trigger_element == EL_ANY_ELEMENT)
3341 for (l = 0; l < MAX_NUM_ELEMENTS; l++)
3342 trigger_events[l][k] = TRUE;
3344 trigger_events[trigger_element][k] = TRUE;
3351 /* ---------- initialize push delay -------------------------------------- */
3353 /* initialize push delay values to default */
3354 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3356 if (!IS_CUSTOM_ELEMENT(i))
3358 /* set default push delay values (corrected since version 3.0.7-1) */
3359 if (game.engine_version < VERSION_IDENT(3,0,7,1))
3361 element_info[i].push_delay_fixed = 2;
3362 element_info[i].push_delay_random = 8;
3366 element_info[i].push_delay_fixed = 8;
3367 element_info[i].push_delay_random = 8;
3372 /* set push delay value for certain elements from pre-defined list */
3373 for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
3375 int e = push_delay_list[i].element;
3377 element_info[e].push_delay_fixed = push_delay_list[i].push_delay_fixed;
3378 element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
3381 /* set push delay value for Supaplex elements for newer engine versions */
3382 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
3384 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3386 if (IS_SP_ELEMENT(i))
3388 /* set SP push delay to just enough to push under a falling zonk */
3389 int delay = (game.engine_version >= VERSION_IDENT(3,1,1,0) ? 8 : 6);
3391 element_info[i].push_delay_fixed = delay;
3392 element_info[i].push_delay_random = 0;
3397 /* ---------- initialize move stepsize ----------------------------------- */
3399 /* initialize move stepsize values to default */
3400 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3401 if (!IS_CUSTOM_ELEMENT(i))
3402 element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
3404 /* set move stepsize value for certain elements from pre-defined list */
3405 for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
3407 int e = move_stepsize_list[i].element;
3409 element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
3412 /* ---------- initialize collect score ----------------------------------- */
3414 /* initialize collect score values for custom elements from initial value */
3415 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3416 if (IS_CUSTOM_ELEMENT(i))
3417 element_info[i].collect_score = element_info[i].collect_score_initial;
3419 /* ---------- initialize collect count ----------------------------------- */
3421 /* initialize collect count values for non-custom elements */
3422 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3423 if (!IS_CUSTOM_ELEMENT(i))
3424 element_info[i].collect_count_initial = 0;
3426 /* add collect count values for all elements from pre-defined list */
3427 for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
3428 element_info[collect_count_list[i].element].collect_count_initial =
3429 collect_count_list[i].count;
3431 /* ---------- initialize access direction -------------------------------- */
3433 /* initialize access direction values to default (access from every side) */
3434 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3435 if (!IS_CUSTOM_ELEMENT(i))
3436 element_info[i].access_direction = MV_ALL_DIRECTIONS;
3438 /* set access direction value for certain elements from pre-defined list */
3439 for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
3440 element_info[access_direction_list[i].element].access_direction =
3441 access_direction_list[i].direction;
3443 /* ---------- initialize explosion content ------------------------------- */
3444 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3446 if (IS_CUSTOM_ELEMENT(i))
3449 for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
3451 /* (content for EL_YAMYAM set at run-time with game.yamyam_content_nr) */
3453 element_info[i].content.e[x][y] =
3454 (i == EL_PLAYER_1 ? EL_EMERALD_YELLOW :
3455 i == EL_PLAYER_2 ? EL_EMERALD_RED :
3456 i == EL_PLAYER_3 ? EL_EMERALD :
3457 i == EL_PLAYER_4 ? EL_EMERALD_PURPLE :
3458 i == EL_MOLE ? EL_EMERALD_RED :
3459 i == EL_PENGUIN ? EL_EMERALD_PURPLE :
3460 i == EL_BUG ? (x == 1 && y == 1 ? EL_DIAMOND : EL_EMERALD) :
3461 i == EL_BD_BUTTERFLY ? EL_BD_DIAMOND :
3462 i == EL_SP_ELECTRON ? EL_SP_INFOTRON :
3463 i == EL_AMOEBA_TO_DIAMOND ? level.amoeba_content :
3464 i == EL_WALL_EMERALD ? EL_EMERALD :
3465 i == EL_WALL_DIAMOND ? EL_DIAMOND :
3466 i == EL_WALL_BD_DIAMOND ? EL_BD_DIAMOND :
3467 i == EL_WALL_EMERALD_YELLOW ? EL_EMERALD_YELLOW :
3468 i == EL_WALL_EMERALD_RED ? EL_EMERALD_RED :
3469 i == EL_WALL_EMERALD_PURPLE ? EL_EMERALD_PURPLE :
3470 i == EL_WALL_PEARL ? EL_PEARL :
3471 i == EL_WALL_CRYSTAL ? EL_CRYSTAL :
3476 /* ---------- initialize recursion detection ------------------------------ */
3477 recursion_loop_depth = 0;
3478 recursion_loop_detected = FALSE;
3479 recursion_loop_element = EL_UNDEFINED;
3481 /* ---------- initialize graphics engine ---------------------------------- */
3482 game.scroll_delay_value =
3483 (game.forced_scroll_delay_value != -1 ? game.forced_scroll_delay_value :
3484 setup.scroll_delay ? setup.scroll_delay_value : 0);
3485 game.scroll_delay_value =
3486 MIN(MAX(MIN_SCROLL_DELAY, game.scroll_delay_value), MAX_SCROLL_DELAY);
3489 int get_num_special_action(int element, int action_first, int action_last)
3491 int num_special_action = 0;
3494 for (i = action_first; i <= action_last; i++)
3496 boolean found = FALSE;
3498 for (j = 0; j < NUM_DIRECTIONS; j++)
3499 if (el_act_dir2img(element, i, j) !=
3500 el_act_dir2img(element, ACTION_DEFAULT, j))
3504 num_special_action++;
3509 return num_special_action;
3514 =============================================================================
3516 -----------------------------------------------------------------------------
3517 initialize and start new game
3518 =============================================================================
3523 int full_lev_fieldx = lev_fieldx + (BorderElement != EL_EMPTY ? 2 : 0);
3524 int full_lev_fieldy = lev_fieldy + (BorderElement != EL_EMPTY ? 2 : 0);
3526 boolean emulate_bd = TRUE; /* unless non-BOULDERDASH elements found */
3527 boolean emulate_sb = TRUE; /* unless non-SOKOBAN elements found */
3528 boolean emulate_sp = TRUE; /* unless non-SUPAPLEX elements found */
3530 boolean do_fading = (game_status == GAME_MODE_MAIN);
3533 int initial_move_dir = MV_DOWN;
3535 int initial_move_dir = MV_NONE;
3540 game_status = GAME_MODE_PLAYING;
3547 if (!game.restart_level)
3548 CloseDoor(DOOR_CLOSE_1);
3551 if (level_editor_test_game)
3552 FadeSkipNextFadeIn();
3554 FadeSetEnterScreen();
3556 if (level_editor_test_game)
3557 fading = fading_none;
3559 fading = menu.destination;
3563 FadeOut(REDRAW_FIELD);
3566 FadeOut(REDRAW_FIELD);
3572 printf("::: FADING OUT: DONE\n");
3577 game_status = GAME_MODE_PLAYING;
3581 /* needed if different viewport properties defined for playing */
3582 ChangeViewportPropertiesIfNeeded();
3586 DrawCompleteVideoDisplay();
3590 InitGameControlValues();
3592 /* don't play tapes over network */
3593 network_playing = (options.network && !tape.playing);
3595 for (i = 0; i < MAX_PLAYERS; i++)
3597 struct PlayerInfo *player = &stored_player[i];
3599 player->index_nr = i;
3600 player->index_bit = (1 << i);
3601 player->element_nr = EL_PLAYER_1 + i;
3603 player->present = FALSE;
3604 player->active = FALSE;
3605 player->mapped = FALSE;
3607 player->killed = FALSE;
3608 player->reanimated = FALSE;
3611 player->effective_action = 0;
3612 player->programmed_action = 0;
3615 player->score_final = 0;
3617 player->gems_still_needed = level.gems_needed;
3618 player->sokobanfields_still_needed = 0;
3619 player->lights_still_needed = 0;
3620 player->friends_still_needed = 0;
3622 for (j = 0; j < MAX_NUM_KEYS; j++)
3623 player->key[j] = FALSE;
3625 player->num_white_keys = 0;
3627 player->dynabomb_count = 0;
3628 player->dynabomb_size = 1;
3629 player->dynabombs_left = 0;
3630 player->dynabomb_xl = FALSE;
3632 player->MovDir = initial_move_dir;
3635 player->GfxDir = initial_move_dir;
3636 player->GfxAction = ACTION_DEFAULT;
3638 player->StepFrame = 0;
3640 player->initial_element = player->element_nr;
3641 player->artwork_element =
3642 (level.use_artwork_element[i] ? level.artwork_element[i] :
3643 player->element_nr);
3644 player->use_murphy = FALSE;
3646 player->block_last_field = FALSE; /* initialized in InitPlayerField() */
3647 player->block_delay_adjustment = 0; /* initialized in InitPlayerField() */
3649 player->gravity = level.initial_player_gravity[i];
3651 player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
3653 player->actual_frame_counter = 0;
3655 player->step_counter = 0;
3657 player->last_move_dir = initial_move_dir;
3659 player->is_active = FALSE;
3661 player->is_waiting = FALSE;
3662 player->is_moving = FALSE;
3663 player->is_auto_moving = FALSE;
3664 player->is_digging = FALSE;
3665 player->is_snapping = FALSE;
3666 player->is_collecting = FALSE;
3667 player->is_pushing = FALSE;
3668 player->is_switching = FALSE;
3669 player->is_dropping = FALSE;
3670 player->is_dropping_pressed = FALSE;
3672 player->is_bored = FALSE;
3673 player->is_sleeping = FALSE;
3675 player->frame_counter_bored = -1;
3676 player->frame_counter_sleeping = -1;
3678 player->anim_delay_counter = 0;
3679 player->post_delay_counter = 0;
3681 player->dir_waiting = initial_move_dir;
3682 player->action_waiting = ACTION_DEFAULT;
3683 player->last_action_waiting = ACTION_DEFAULT;
3684 player->special_action_bored = ACTION_DEFAULT;
3685 player->special_action_sleeping = ACTION_DEFAULT;
3687 player->switch_x = -1;
3688 player->switch_y = -1;
3690 player->drop_x = -1;
3691 player->drop_y = -1;
3693 player->show_envelope = 0;
3695 SetPlayerMoveSpeed(player, level.initial_player_stepsize[i], TRUE);
3697 player->push_delay = -1; /* initialized when pushing starts */
3698 player->push_delay_value = game.initial_push_delay_value;
3700 player->drop_delay = 0;
3701 player->drop_pressed_delay = 0;
3703 player->last_jx = -1;
3704 player->last_jy = -1;
3708 player->shield_normal_time_left = 0;
3709 player->shield_deadly_time_left = 0;
3711 player->inventory_infinite_element = EL_UNDEFINED;
3712 player->inventory_size = 0;
3714 if (level.use_initial_inventory[i])
3716 for (j = 0; j < level.initial_inventory_size[i]; j++)
3718 int element = level.initial_inventory_content[i][j];
3719 int collect_count = element_info[element].collect_count_initial;
3722 if (!IS_CUSTOM_ELEMENT(element))
3725 if (collect_count == 0)
3726 player->inventory_infinite_element = element;
3728 for (k = 0; k < collect_count; k++)
3729 if (player->inventory_size < MAX_INVENTORY_SIZE)
3730 player->inventory_element[player->inventory_size++] = element;
3734 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
3735 SnapField(player, 0, 0);
3737 player->LevelSolved = FALSE;
3738 player->GameOver = FALSE;
3740 player->LevelSolved_GameWon = FALSE;
3741 player->LevelSolved_GameEnd = FALSE;
3742 player->LevelSolved_PanelOff = FALSE;
3743 player->LevelSolved_SaveTape = FALSE;
3744 player->LevelSolved_SaveScore = FALSE;
3745 player->LevelSolved_CountingTime = 0;
3746 player->LevelSolved_CountingScore = 0;
3748 map_player_action[i] = i;
3751 network_player_action_received = FALSE;
3753 #if defined(NETWORK_AVALIABLE)
3754 /* initial null action */
3755 if (network_playing)
3756 SendToServer_MovePlayer(MV_NONE);
3765 TimeLeft = level.time;
3768 ScreenMovDir = MV_NONE;
3772 ScrollStepSize = 0; /* will be correctly initialized by ScrollScreen() */
3774 AllPlayersGone = FALSE;
3776 game.no_time_limit = (level.time == 0);
3778 game.yamyam_content_nr = 0;
3779 game.robot_wheel_active = FALSE;
3780 game.magic_wall_active = FALSE;
3781 game.magic_wall_time_left = 0;
3782 game.light_time_left = 0;
3783 game.timegate_time_left = 0;
3784 game.switchgate_pos = 0;
3785 game.wind_direction = level.wind_direction_initial;
3787 #if !USE_PLAYER_GRAVITY
3788 game.gravity = FALSE;
3789 game.explosions_delayed = TRUE;
3792 game.lenses_time_left = 0;
3793 game.magnify_time_left = 0;
3795 game.ball_state = level.ball_state_initial;
3796 game.ball_content_nr = 0;
3798 game.envelope_active = FALSE;
3800 /* set focus to local player for network games, else to all players */
3801 game.centered_player_nr = (network_playing ? local_player->index_nr : -1);
3802 game.centered_player_nr_next = game.centered_player_nr;
3803 game.set_centered_player = FALSE;
3805 if (network_playing && tape.recording)
3807 /* store client dependent player focus when recording network games */
3808 tape.centered_player_nr_next = game.centered_player_nr_next;
3809 tape.set_centered_player = TRUE;
3812 for (i = 0; i < NUM_BELTS; i++)
3814 game.belt_dir[i] = MV_NONE;
3815 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
3818 for (i = 0; i < MAX_NUM_AMOEBA; i++)
3819 AmoebaCnt[i] = AmoebaCnt2[i] = 0;
3821 #if DEBUG_INIT_PLAYER
3824 printf("Player status at level initialization:\n");
3828 SCAN_PLAYFIELD(x, y)
3830 Feld[x][y] = level.field[x][y];
3831 MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
3832 ChangeDelay[x][y] = 0;
3833 ChangePage[x][y] = -1;
3834 #if USE_NEW_CUSTOM_VALUE
3835 CustomValue[x][y] = 0; /* initialized in InitField() */
3837 Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
3839 WasJustMoving[x][y] = 0;
3840 WasJustFalling[x][y] = 0;
3841 CheckCollision[x][y] = 0;
3842 CheckImpact[x][y] = 0;
3844 Pushed[x][y] = FALSE;
3846 ChangeCount[x][y] = 0;
3847 ChangeEvent[x][y] = -1;
3849 ExplodePhase[x][y] = 0;
3850 ExplodeDelay[x][y] = 0;
3851 ExplodeField[x][y] = EX_TYPE_NONE;
3853 RunnerVisit[x][y] = 0;
3854 PlayerVisit[x][y] = 0;
3857 GfxRandom[x][y] = INIT_GFX_RANDOM();
3858 GfxElement[x][y] = EL_UNDEFINED;
3859 GfxAction[x][y] = ACTION_DEFAULT;
3860 GfxDir[x][y] = MV_NONE;
3861 GfxRedraw[x][y] = GFX_REDRAW_NONE;
3864 SCAN_PLAYFIELD(x, y)
3866 if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
3868 if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
3870 if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
3873 InitField(x, y, TRUE);
3875 ResetGfxAnimation(x, y);
3880 for (i = 0; i < MAX_PLAYERS; i++)
3882 struct PlayerInfo *player = &stored_player[i];
3884 /* set number of special actions for bored and sleeping animation */
3885 player->num_special_action_bored =
3886 get_num_special_action(player->artwork_element,
3887 ACTION_BORING_1, ACTION_BORING_LAST);
3888 player->num_special_action_sleeping =
3889 get_num_special_action(player->artwork_element,
3890 ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
3893 game.emulation = (emulate_bd ? EMU_BOULDERDASH :
3894 emulate_sb ? EMU_SOKOBAN :
3895 emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
3897 #if USE_NEW_ALL_SLIPPERY
3898 /* initialize type of slippery elements */
3899 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3901 if (!IS_CUSTOM_ELEMENT(i))
3903 /* default: elements slip down either to the left or right randomly */
3904 element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
3906 /* SP style elements prefer to slip down on the left side */
3907 if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
3908 element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3910 /* BD style elements prefer to slip down on the left side */
3911 if (game.emulation == EMU_BOULDERDASH)
3912 element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3917 /* initialize explosion and ignition delay */
3918 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3920 if (!IS_CUSTOM_ELEMENT(i))
3923 int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
3924 game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
3925 game.emulation == EMU_SUPAPLEX ? 3 : 2);
3926 int last_phase = (num_phase + 1) * delay;
3927 int half_phase = (num_phase / 2) * delay;
3929 element_info[i].explosion_delay = last_phase - 1;
3930 element_info[i].ignition_delay = half_phase;
3932 if (i == EL_BLACK_ORB)
3933 element_info[i].ignition_delay = 1;
3937 if (element_info[i].explosion_delay < 1) /* !!! check again !!! */
3938 element_info[i].explosion_delay = 1;
3940 if (element_info[i].ignition_delay < 1) /* !!! check again !!! */
3941 element_info[i].ignition_delay = 1;
3945 /* correct non-moving belts to start moving left */
3946 for (i = 0; i < NUM_BELTS; i++)
3947 if (game.belt_dir[i] == MV_NONE)
3948 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
3950 #if USE_NEW_PLAYER_ASSIGNMENTS
3951 /* !!! SAME AS init.c:InitPlayerInfo() -- FIX THIS !!! */
3952 /* choose default local player */
3953 local_player = &stored_player[0];
3955 for (i = 0; i < MAX_PLAYERS; i++)
3956 stored_player[i].connected = FALSE;
3958 local_player->connected = TRUE;
3959 /* !!! SAME AS init.c:InitPlayerInfo() -- FIX THIS !!! */
3962 printf("::: TEAM MODE: %d\n", game.team_mode);
3968 for (i = 0; i < MAX_PLAYERS; i++)
3969 stored_player[i].connected = tape.player_participates[i];
3971 /* try to guess locally connected team mode players (needed for correct
3972 assignment of player figures from level to locally playing players) */
3974 for (i = 0; i < MAX_PLAYERS; i++)
3975 if (tape.player_participates[i])
3976 stored_player[i].connected = TRUE;
3979 else if (game.team_mode && !options.network)
3981 /* try to guess locally connected team mode players (needed for correct
3982 assignment of player figures from level to locally playing players) */
3984 for (i = 0; i < MAX_PLAYERS; i++)
3985 if (setup.input[i].use_joystick ||
3986 setup.input[i].key.left != KSYM_UNDEFINED)
3987 stored_player[i].connected = TRUE;
3990 #if DEBUG_INIT_PLAYER
3993 printf("Player status after level initialization:\n");
3995 for (i = 0; i < MAX_PLAYERS; i++)
3997 struct PlayerInfo *player = &stored_player[i];
3999 printf("- player %d: present == %d, connected == %d, active == %d",
4005 if (local_player == player)
4006 printf(" (local player)");
4013 #if DEBUG_INIT_PLAYER
4015 printf("Reassigning players ...\n");
4018 /* check if any connected player was not found in playfield */
4019 for (i = 0; i < MAX_PLAYERS; i++)
4021 struct PlayerInfo *player = &stored_player[i];
4023 if (player->connected && !player->present)
4025 struct PlayerInfo *field_player = NULL;
4027 #if DEBUG_INIT_PLAYER
4029 printf("- looking for field player for player %d ...\n", i + 1);
4032 /* assign first free player found that is present in the playfield */
4035 /* first try: look for unmapped playfield player that is not connected */
4036 for (j = 0; j < MAX_PLAYERS; j++)
4037 if (field_player == NULL &&
4038 stored_player[j].present &&
4039 !stored_player[j].mapped &&
4040 !stored_player[j].connected)
4041 field_player = &stored_player[j];
4043 /* second try: look for *any* unmapped playfield player */
4044 for (j = 0; j < MAX_PLAYERS; j++)
4045 if (field_player == NULL &&
4046 stored_player[j].present &&
4047 !stored_player[j].mapped)
4048 field_player = &stored_player[j];
4050 /* first try: look for unmapped playfield player that is not connected */
4051 if (field_player == NULL)
4052 for (j = 0; j < MAX_PLAYERS; j++)
4053 if (stored_player[j].present &&
4054 !stored_player[j].mapped &&
4055 !stored_player[j].connected)
4056 field_player = &stored_player[j];
4058 /* second try: look for *any* unmapped playfield player */
4059 if (field_player == NULL)
4060 for (j = 0; j < MAX_PLAYERS; j++)
4061 if (stored_player[j].present &&
4062 !stored_player[j].mapped)
4063 field_player = &stored_player[j];
4066 if (field_player != NULL)
4068 int jx = field_player->jx, jy = field_player->jy;
4070 #if DEBUG_INIT_PLAYER
4072 printf("- found player %d\n", field_player->index_nr + 1);
4075 player->present = FALSE;
4076 player->active = FALSE;
4078 field_player->present = TRUE;
4079 field_player->active = TRUE;
4082 player->initial_element = field_player->initial_element;
4083 player->artwork_element = field_player->artwork_element;
4085 player->block_last_field = field_player->block_last_field;
4086 player->block_delay_adjustment = field_player->block_delay_adjustment;
4089 StorePlayer[jx][jy] = field_player->element_nr;
4091 field_player->jx = field_player->last_jx = jx;
4092 field_player->jy = field_player->last_jy = jy;
4094 if (local_player == player)
4095 local_player = field_player;
4097 map_player_action[field_player->index_nr] = i;
4099 field_player->mapped = TRUE;
4101 #if DEBUG_INIT_PLAYER
4103 printf("- map_player_action[%d] == %d\n",
4104 field_player->index_nr + 1, i + 1);
4109 if (player->connected && player->present)
4110 player->mapped = TRUE;
4113 #if DEBUG_INIT_PLAYER
4116 printf("Player status after player assignment (first stage):\n");
4118 for (i = 0; i < MAX_PLAYERS; i++)
4120 struct PlayerInfo *player = &stored_player[i];
4122 printf("- player %d: present == %d, connected == %d, active == %d",
4128 if (local_player == player)
4129 printf(" (local player)");
4138 /* check if any connected player was not found in playfield */
4139 for (i = 0; i < MAX_PLAYERS; i++)
4141 struct PlayerInfo *player = &stored_player[i];
4143 if (player->connected && !player->present)
4145 for (j = 0; j < MAX_PLAYERS; j++)
4147 struct PlayerInfo *field_player = &stored_player[j];
4148 int jx = field_player->jx, jy = field_player->jy;
4150 /* assign first free player found that is present in the playfield */
4151 if (field_player->present && !field_player->connected)
4153 player->present = TRUE;
4154 player->active = TRUE;
4156 field_player->present = FALSE;
4157 field_player->active = FALSE;
4159 player->initial_element = field_player->initial_element;
4160 player->artwork_element = field_player->artwork_element;
4162 player->block_last_field = field_player->block_last_field;
4163 player->block_delay_adjustment = field_player->block_delay_adjustment;
4165 StorePlayer[jx][jy] = player->element_nr;
4167 player->jx = player->last_jx = jx;
4168 player->jy = player->last_jy = jy;
4178 printf("::: local_player->present == %d\n", local_player->present);
4183 /* when playing a tape, eliminate all players who do not participate */
4185 #if USE_NEW_PLAYER_ASSIGNMENTS
4188 if (!game.team_mode)
4191 for (i = 0; i < MAX_PLAYERS; i++)
4193 if (stored_player[i].active &&
4194 !tape.player_participates[map_player_action[i]])
4196 struct PlayerInfo *player = &stored_player[i];
4197 int jx = player->jx, jy = player->jy;
4199 #if DEBUG_INIT_PLAYER
4201 printf("Removing player %d at (%d, %d)\n", i + 1, jx, jy);
4204 player->active = FALSE;
4205 StorePlayer[jx][jy] = 0;
4206 Feld[jx][jy] = EL_EMPTY;
4212 for (i = 0; i < MAX_PLAYERS; i++)
4214 if (stored_player[i].active &&
4215 !tape.player_participates[i])
4217 struct PlayerInfo *player = &stored_player[i];
4218 int jx = player->jx, jy = player->jy;
4220 player->active = FALSE;
4221 StorePlayer[jx][jy] = 0;
4222 Feld[jx][jy] = EL_EMPTY;
4227 else if (!options.network && !game.team_mode) /* && !tape.playing */
4229 /* when in single player mode, eliminate all but the first active player */
4231 for (i = 0; i < MAX_PLAYERS; i++)
4233 if (stored_player[i].active)
4235 for (j = i + 1; j < MAX_PLAYERS; j++)
4237 if (stored_player[j].active)
4239 struct PlayerInfo *player = &stored_player[j];
4240 int jx = player->jx, jy = player->jy;
4242 player->active = FALSE;
4243 player->present = FALSE;
4245 StorePlayer[jx][jy] = 0;
4246 Feld[jx][jy] = EL_EMPTY;
4253 /* when recording the game, store which players take part in the game */
4256 #if USE_NEW_PLAYER_ASSIGNMENTS
4257 for (i = 0; i < MAX_PLAYERS; i++)
4258 if (stored_player[i].connected)
4259 tape.player_participates[i] = TRUE;
4261 for (i = 0; i < MAX_PLAYERS; i++)
4262 if (stored_player[i].active)
4263 tape.player_participates[i] = TRUE;
4267 #if DEBUG_INIT_PLAYER
4270 printf("Player status after player assignment (final stage):\n");
4272 for (i = 0; i < MAX_PLAYERS; i++)
4274 struct PlayerInfo *player = &stored_player[i];
4276 printf("- player %d: present == %d, connected == %d, active == %d",
4282 if (local_player == player)
4283 printf(" (local player)");
4290 if (BorderElement == EL_EMPTY)
4293 SBX_Right = lev_fieldx - SCR_FIELDX;
4295 SBY_Lower = lev_fieldy - SCR_FIELDY;
4300 SBX_Right = lev_fieldx - SCR_FIELDX + 1;
4302 SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
4307 // printf("::: START-0: %d, %d\n", lev_fieldx, SCR_FIELDX);
4308 // printf("::: START-1: %d, %d\n", SBX_Left, SBX_Right);
4311 if (full_lev_fieldx <= SCR_FIELDX)
4312 SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
4314 if (full_lev_fieldy <= SCR_FIELDY)
4315 SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
4317 if (lev_fieldx + (SBX_Left < 0 ? 2 : 0) <= SCR_FIELDX)
4318 SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
4320 if (lev_fieldy + (SBY_Upper < 0 ? 2 : 0) <= SCR_FIELDY)
4321 SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
4325 printf("::: START-2: %d, %d (%d)\n", SBX_Left, SBX_Right,
4326 SBX_Right - SBX_Left + 1);
4330 if (EVEN(SCR_FIELDX) && full_lev_fieldx > SCR_FIELDX)
4332 if (EVEN(SCR_FIELDY) && full_lev_fieldy > SCR_FIELDY)
4335 if (EVEN(SCR_FIELDX))
4337 if (EVEN(SCR_FIELDY))
4342 printf("::: START-3: %d, %d\n", SBX_Left, SBX_Right);
4348 if (lev_fieldx + (SBX_Left == -1 ? 2 : 0) <= SCR_FIELDX)
4349 SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
4351 if (lev_fieldy + (SBY_Upper == -1 ? 2 : 0) <= SCR_FIELDY)
4352 SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
4355 /* if local player not found, look for custom element that might create
4356 the player (make some assumptions about the right custom element) */
4357 if (!local_player->present)
4359 int start_x = 0, start_y = 0;
4360 int found_rating = 0;
4361 int found_element = EL_UNDEFINED;
4362 int player_nr = local_player->index_nr;
4364 SCAN_PLAYFIELD(x, y)
4366 int element = Feld[x][y];
4371 if (level.use_start_element[player_nr] &&
4372 level.start_element[player_nr] == element &&
4379 found_element = element;
4382 if (!IS_CUSTOM_ELEMENT(element))
4385 if (CAN_CHANGE(element))
4387 for (i = 0; i < element_info[element].num_change_pages; i++)
4389 /* check for player created from custom element as single target */
4390 content = element_info[element].change_page[i].target_element;
4391 is_player = ELEM_IS_PLAYER(content);
4393 if (is_player && (found_rating < 3 ||
4394 (found_rating == 3 && element < found_element)))
4400 found_element = element;
4405 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
4407 /* check for player created from custom element as explosion content */
4408 content = element_info[element].content.e[xx][yy];
4409 is_player = ELEM_IS_PLAYER(content);
4411 if (is_player && (found_rating < 2 ||
4412 (found_rating == 2 && element < found_element)))
4414 start_x = x + xx - 1;
4415 start_y = y + yy - 1;
4418 found_element = element;
4421 if (!CAN_CHANGE(element))
4424 for (i = 0; i < element_info[element].num_change_pages; i++)
4426 /* check for player created from custom element as extended target */
4428 element_info[element].change_page[i].target_content.e[xx][yy];
4430 is_player = ELEM_IS_PLAYER(content);
4432 if (is_player && (found_rating < 1 ||
4433 (found_rating == 1 && element < found_element)))
4435 start_x = x + xx - 1;
4436 start_y = y + yy - 1;
4439 found_element = element;
4445 scroll_x = (start_x < SBX_Left + MIDPOSX ? SBX_Left :
4446 start_x > SBX_Right + MIDPOSX ? SBX_Right :
4449 scroll_y = (start_y < SBY_Upper + MIDPOSY ? SBY_Upper :
4450 start_y > SBY_Lower + MIDPOSY ? SBY_Lower :
4455 scroll_x = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
4456 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
4457 local_player->jx - MIDPOSX);
4459 scroll_y = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
4460 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
4461 local_player->jy - MIDPOSY);
4465 printf("::: %d, %d (initial)\n", scroll_x, scroll_y);
4469 /* do not use PLAYING mask for fading out from main screen */
4470 game_status = GAME_MODE_MAIN;
4477 if (!game.restart_level)
4478 CloseDoor(DOOR_CLOSE_1);
4481 if (level_editor_test_game)
4482 FadeSkipNextFadeIn();
4484 FadeSetEnterScreen();
4486 if (level_editor_test_game)
4487 fading = fading_none;
4489 fading = menu.destination;
4493 FadeOut(REDRAW_FIELD);
4496 FadeOut(REDRAW_FIELD);
4502 game_status = GAME_MODE_PLAYING;
4505 /* !!! FIX THIS (START) !!! */
4506 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
4508 InitGameEngine_EM();
4511 /* blit playfield from scroll buffer to normal back buffer for fading in */
4512 BlitScreenToBitmap_EM(backbuffer);
4515 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
4517 InitGameEngine_SP();
4520 /* blit playfield from scroll buffer to normal back buffer for fading in */
4521 BlitScreenToBitmap_SP(backbuffer);
4526 DrawLevel(REDRAW_FIELD);
4529 /* after drawing the level, correct some elements */
4530 if (game.timegate_time_left == 0)
4531 CloseAllOpenTimegates();
4534 /* blit playfield from scroll buffer to normal back buffer for fading in */
4536 BlitScreenToBitmap(backbuffer);
4538 if (setup.soft_scrolling)
4539 BlitBitmap(fieldbuffer, backbuffer, FX, FY, SXSIZE, SYSIZE, SX, SY);
4544 redraw_mask |= REDRAW_FROM_BACKBUFFER;
4548 /* blit playfield from scroll buffer to normal back buffer for fading in */
4549 BlitScreenToBitmap(backbuffer);
4551 redraw_mask |= REDRAW_FROM_BACKBUFFER;
4553 /* !!! FIX THIS (END) !!! */
4556 FadeIn(REDRAW_FIELD);
4559 FadeIn(REDRAW_FIELD);
4564 if (!game.restart_level)
4566 /* copy default game door content to main double buffer */
4569 /* !!! CHECK AGAIN !!! */
4570 SetPanelBackground();
4571 // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
4572 DrawBackground(DX, DY, DXSIZE, DYSIZE);
4574 struct GraphicInfo *gfx = &graphic_info[IMG_BACKGROUND_PANEL];
4576 /* (ClearRectangle() only needed if panel bitmap is smaller than panel) */
4577 ClearRectangle(drawto, DX, DY, DXSIZE, DYSIZE);
4578 BlitBitmap(gfx->bitmap, drawto, gfx->src_x, gfx->src_y,
4579 MIN(gfx->width, DXSIZE), MIN(gfx->height, DYSIZE), DX, DY);
4582 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
4583 DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
4587 SetPanelBackground();
4588 SetDrawBackgroundMask(REDRAW_DOOR_1);
4591 UpdateAndDisplayGameControlValues();
4593 UpdateGameDoorValues();
4594 DrawGameDoorValues();
4597 if (!game.restart_level)
4601 game_gadget[SOUND_CTRL_ID_MUSIC]->checked = setup.sound_music;
4602 game_gadget[SOUND_CTRL_ID_LOOPS]->checked = setup.sound_loops;
4603 game_gadget[SOUND_CTRL_ID_SIMPLE]->checked = setup.sound_simple;
4607 /* copy actual game door content to door double buffer for OpenDoor() */
4609 BlitBitmap(drawto, bitmap_db_door_1, DX, DY, DXSIZE, DYSIZE, 0, 0);
4611 BlitBitmap(drawto, bitmap_db_door,
4612 DX, DY, DXSIZE, DYSIZE, DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1);
4615 OpenDoor(DOOR_OPEN_ALL);
4617 PlaySound(SND_GAME_STARTING);
4619 if (setup.sound_music)
4622 KeyboardAutoRepeatOffUnlessAutoplay();
4624 #if DEBUG_INIT_PLAYER
4627 printf("Player status (final):\n");
4629 for (i = 0; i < MAX_PLAYERS; i++)
4631 struct PlayerInfo *player = &stored_player[i];
4633 printf("- player %d: present == %d, connected == %d, active == %d",
4639 if (local_player == player)
4640 printf(" (local player)");
4655 if (!game.restart_level && !tape.playing)
4657 LevelStats_incPlayed(level_nr);
4659 SaveLevelSetup_SeriesInfo();
4662 printf("::: PLAYING LEVEL (%d)\n", LevelStats_getPlayed(level_nr));
4666 game.restart_level = FALSE;
4669 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y)
4671 /* this is used for non-R'n'D game engines to update certain engine values */
4673 /* needed to determine if sounds are played within the visible screen area */
4674 scroll_x = actual_scroll_x;
4675 scroll_y = actual_scroll_y;
4678 void InitMovDir(int x, int y)
4680 int i, element = Feld[x][y];
4681 static int xy[4][2] =
4688 static int direction[3][4] =
4690 { MV_RIGHT, MV_UP, MV_LEFT, MV_DOWN },
4691 { MV_LEFT, MV_DOWN, MV_RIGHT, MV_UP },
4692 { MV_LEFT, MV_RIGHT, MV_UP, MV_DOWN }
4701 Feld[x][y] = EL_BUG;
4702 MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
4705 case EL_SPACESHIP_RIGHT:
4706 case EL_SPACESHIP_UP:
4707 case EL_SPACESHIP_LEFT:
4708 case EL_SPACESHIP_DOWN:
4709 Feld[x][y] = EL_SPACESHIP;
4710 MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
4713 case EL_BD_BUTTERFLY_RIGHT:
4714 case EL_BD_BUTTERFLY_UP:
4715 case EL_BD_BUTTERFLY_LEFT:
4716 case EL_BD_BUTTERFLY_DOWN:
4717 Feld[x][y] = EL_BD_BUTTERFLY;
4718 MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
4721 case EL_BD_FIREFLY_RIGHT:
4722 case EL_BD_FIREFLY_UP:
4723 case EL_BD_FIREFLY_LEFT:
4724 case EL_BD_FIREFLY_DOWN:
4725 Feld[x][y] = EL_BD_FIREFLY;
4726 MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
4729 case EL_PACMAN_RIGHT:
4731 case EL_PACMAN_LEFT:
4732 case EL_PACMAN_DOWN:
4733 Feld[x][y] = EL_PACMAN;
4734 MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
4737 case EL_YAMYAM_LEFT:
4738 case EL_YAMYAM_RIGHT:
4740 case EL_YAMYAM_DOWN:
4741 Feld[x][y] = EL_YAMYAM;
4742 MovDir[x][y] = direction[2][element - EL_YAMYAM_LEFT];
4745 case EL_SP_SNIKSNAK:
4746 MovDir[x][y] = MV_UP;
4749 case EL_SP_ELECTRON:
4750 MovDir[x][y] = MV_LEFT;
4757 Feld[x][y] = EL_MOLE;
4758 MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
4762 if (IS_CUSTOM_ELEMENT(element))
4764 struct ElementInfo *ei = &element_info[element];
4765 int move_direction_initial = ei->move_direction_initial;
4766 int move_pattern = ei->move_pattern;
4768 if (move_direction_initial == MV_START_PREVIOUS)
4770 if (MovDir[x][y] != MV_NONE)
4773 move_direction_initial = MV_START_AUTOMATIC;
4776 if (move_direction_initial == MV_START_RANDOM)
4777 MovDir[x][y] = 1 << RND(4);
4778 else if (move_direction_initial & MV_ANY_DIRECTION)
4779 MovDir[x][y] = move_direction_initial;
4780 else if (move_pattern == MV_ALL_DIRECTIONS ||
4781 move_pattern == MV_TURNING_LEFT ||
4782 move_pattern == MV_TURNING_RIGHT ||
4783 move_pattern == MV_TURNING_LEFT_RIGHT ||
4784 move_pattern == MV_TURNING_RIGHT_LEFT ||
4785 move_pattern == MV_TURNING_RANDOM)
4786 MovDir[x][y] = 1 << RND(4);
4787 else if (move_pattern == MV_HORIZONTAL)
4788 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
4789 else if (move_pattern == MV_VERTICAL)
4790 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
4791 else if (move_pattern & MV_ANY_DIRECTION)
4792 MovDir[x][y] = element_info[element].move_pattern;
4793 else if (move_pattern == MV_ALONG_LEFT_SIDE ||
4794 move_pattern == MV_ALONG_RIGHT_SIDE)
4796 /* use random direction as default start direction */
4797 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
4798 MovDir[x][y] = 1 << RND(4);
4800 for (i = 0; i < NUM_DIRECTIONS; i++)
4802 int x1 = x + xy[i][0];
4803 int y1 = y + xy[i][1];
4805 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4807 if (move_pattern == MV_ALONG_RIGHT_SIDE)
4808 MovDir[x][y] = direction[0][i];
4810 MovDir[x][y] = direction[1][i];
4819 MovDir[x][y] = 1 << RND(4);
4821 if (element != EL_BUG &&
4822 element != EL_SPACESHIP &&
4823 element != EL_BD_BUTTERFLY &&
4824 element != EL_BD_FIREFLY)
4827 for (i = 0; i < NUM_DIRECTIONS; i++)
4829 int x1 = x + xy[i][0];
4830 int y1 = y + xy[i][1];
4832 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4834 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
4836 MovDir[x][y] = direction[0][i];
4839 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
4840 element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4842 MovDir[x][y] = direction[1][i];
4851 GfxDir[x][y] = MovDir[x][y];
4854 void InitAmoebaNr(int x, int y)
4857 int group_nr = AmoebeNachbarNr(x, y);
4861 for (i = 1; i < MAX_NUM_AMOEBA; i++)
4863 if (AmoebaCnt[i] == 0)
4871 AmoebaNr[x][y] = group_nr;
4872 AmoebaCnt[group_nr]++;
4873 AmoebaCnt2[group_nr]++;
4876 static void PlayerWins(struct PlayerInfo *player)
4878 player->LevelSolved = TRUE;
4879 player->GameOver = TRUE;
4881 player->score_final = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
4882 level.native_em_level->lev->score : player->score);
4884 player->LevelSolved_CountingTime = (game.no_time_limit ? TimePlayed :
4886 player->LevelSolved_CountingScore = player->score_final;
4891 static int time, time_final;
4892 static int score, score_final;
4893 static int game_over_delay_1 = 0;
4894 static int game_over_delay_2 = 0;
4895 int game_over_delay_value_1 = 50;
4896 int game_over_delay_value_2 = 50;
4898 if (!local_player->LevelSolved_GameWon)
4902 /* do not start end game actions before the player stops moving (to exit) */
4903 if (local_player->MovPos)
4906 local_player->LevelSolved_GameWon = TRUE;
4907 local_player->LevelSolved_SaveTape = tape.recording;
4908 local_player->LevelSolved_SaveScore = !tape.playing;
4912 LevelStats_incSolved(level_nr);
4914 SaveLevelSetup_SeriesInfo();
4917 printf("::: LEVEL SOLVED (%d)\n", LevelStats_getSolved(level_nr));
4921 if (tape.auto_play) /* tape might already be stopped here */
4922 tape.auto_play_level_solved = TRUE;
4928 game_over_delay_1 = game_over_delay_value_1;
4929 game_over_delay_2 = game_over_delay_value_2;
4931 time = time_final = (game.no_time_limit ? TimePlayed : TimeLeft);
4932 score = score_final = local_player->score_final;
4937 score_final += TimeLeft * level.score[SC_TIME_BONUS];
4939 else if (game.no_time_limit && TimePlayed < 999)
4942 score_final += (999 - TimePlayed) * level.score[SC_TIME_BONUS];
4945 local_player->score_final = score_final;
4947 if (level_editor_test_game)
4950 score = score_final;
4953 local_player->LevelSolved_CountingTime = time;
4954 local_player->LevelSolved_CountingScore = score;
4956 game_panel_controls[GAME_PANEL_TIME].value = time;
4957 game_panel_controls[GAME_PANEL_SCORE].value = score;
4959 DisplayGameControlValues();
4961 DrawGameValue_Time(time);
4962 DrawGameValue_Score(score);
4966 if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
4968 if (ExitX >= 0 && ExitY >= 0) /* local player has left the level */
4970 /* close exit door after last player */
4971 if ((AllPlayersGone &&
4972 (Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
4973 Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN ||
4974 Feld[ExitX][ExitY] == EL_STEEL_EXIT_OPEN)) ||
4975 Feld[ExitX][ExitY] == EL_EM_EXIT_OPEN ||
4976 Feld[ExitX][ExitY] == EL_EM_STEEL_EXIT_OPEN)
4978 int element = Feld[ExitX][ExitY];
4981 if (element == EL_EM_EXIT_OPEN ||
4982 element == EL_EM_STEEL_EXIT_OPEN)
4989 Feld[ExitX][ExitY] =
4990 (element == EL_EXIT_OPEN ? EL_EXIT_CLOSING :
4991 element == EL_EM_EXIT_OPEN ? EL_EM_EXIT_CLOSING :
4992 element == EL_SP_EXIT_OPEN ? EL_SP_EXIT_CLOSING:
4993 element == EL_STEEL_EXIT_OPEN ? EL_STEEL_EXIT_CLOSING:
4994 EL_EM_STEEL_EXIT_CLOSING);
4996 PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING);
5000 /* player disappears */
5001 DrawLevelField(ExitX, ExitY);
5004 for (i = 0; i < MAX_PLAYERS; i++)
5006 struct PlayerInfo *player = &stored_player[i];
5008 if (player->present)
5010 RemovePlayer(player);
5012 /* player disappears */
5013 DrawLevelField(player->jx, player->jy);
5018 PlaySound(SND_GAME_WINNING);
5021 if (game_over_delay_1 > 0)
5023 game_over_delay_1--;
5028 if (time != time_final)
5030 int time_to_go = ABS(time_final - time);
5031 int time_count_dir = (time < time_final ? +1 : -1);
5032 int time_count_steps = (time_to_go > 100 && time_to_go % 10 == 0 ? 10 : 1);
5034 time += time_count_steps * time_count_dir;
5035 score += time_count_steps * level.score[SC_TIME_BONUS];
5038 local_player->LevelSolved_CountingTime = time;
5039 local_player->LevelSolved_CountingScore = score;
5041 game_panel_controls[GAME_PANEL_TIME].value = time;
5042 game_panel_controls[GAME_PANEL_SCORE].value = score;
5044 DisplayGameControlValues();
5046 DrawGameValue_Time(time);
5047 DrawGameValue_Score(score);
5050 if (time == time_final)
5051 StopSound(SND_GAME_LEVELTIME_BONUS);
5052 else if (setup.sound_loops)
5053 PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
5055 PlaySound(SND_GAME_LEVELTIME_BONUS);
5060 local_player->LevelSolved_PanelOff = TRUE;
5062 if (game_over_delay_2 > 0)
5064 game_over_delay_2--;
5077 boolean raise_level = FALSE;
5079 local_player->LevelSolved_GameEnd = TRUE;
5081 CloseDoor(DOOR_CLOSE_1);
5083 if (local_player->LevelSolved_SaveTape)
5090 SaveTapeChecked(tape.level_nr); /* ask to save tape */
5092 SaveTape(tape.level_nr); /* ask to save tape */
5096 if (level_editor_test_game)
5098 game_status = GAME_MODE_MAIN;
5101 DrawAndFadeInMainMenu(REDRAW_FIELD);
5109 if (!local_player->LevelSolved_SaveScore)
5112 FadeOut(REDRAW_FIELD);
5115 game_status = GAME_MODE_MAIN;
5117 DrawAndFadeInMainMenu(REDRAW_FIELD);
5122 if (level_nr == leveldir_current->handicap_level)
5124 leveldir_current->handicap_level++;
5126 SaveLevelSetup_SeriesInfo();
5129 if (level_nr < leveldir_current->last_level)
5130 raise_level = TRUE; /* advance to next level */
5132 if ((hi_pos = NewHiScore()) >= 0)
5134 game_status = GAME_MODE_SCORES;
5136 DrawHallOfFame(hi_pos);
5147 FadeOut(REDRAW_FIELD);
5150 game_status = GAME_MODE_MAIN;
5158 DrawAndFadeInMainMenu(REDRAW_FIELD);
5167 LoadScore(level_nr);
5169 if (strEqual(setup.player_name, EMPTY_PLAYER_NAME) ||
5170 local_player->score_final < highscore[MAX_SCORE_ENTRIES - 1].Score)
5173 for (k = 0; k < MAX_SCORE_ENTRIES; k++)
5175 if (local_player->score_final > highscore[k].Score)
5177 /* player has made it to the hall of fame */
5179 if (k < MAX_SCORE_ENTRIES - 1)
5181 int m = MAX_SCORE_ENTRIES - 1;
5184 for (l = k; l < MAX_SCORE_ENTRIES; l++)
5185 if (strEqual(setup.player_name, highscore[l].Name))
5187 if (m == k) /* player's new highscore overwrites his old one */
5191 for (l = m; l > k; l--)
5193 strcpy(highscore[l].Name, highscore[l - 1].Name);
5194 highscore[l].Score = highscore[l - 1].Score;
5201 strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
5202 highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
5203 highscore[k].Score = local_player->score_final;
5209 else if (!strncmp(setup.player_name, highscore[k].Name,
5210 MAX_PLAYER_NAME_LEN))
5211 break; /* player already there with a higher score */
5217 SaveScore(level_nr);
5222 inline static int getElementMoveStepsizeExt(int x, int y, int direction)
5224 int element = Feld[x][y];
5225 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
5226 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
5227 int horiz_move = (dx != 0);
5228 int sign = (horiz_move ? dx : dy);
5229 int step = sign * element_info[element].move_stepsize;
5231 /* special values for move stepsize for spring and things on conveyor belt */
5234 if (CAN_FALL(element) &&
5235 y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
5236 step = sign * MOVE_STEPSIZE_NORMAL / 2;
5237 else if (element == EL_SPRING)
5238 step = sign * MOVE_STEPSIZE_NORMAL * 2;
5244 inline static int getElementMoveStepsize(int x, int y)
5246 return getElementMoveStepsizeExt(x, y, MovDir[x][y]);
5249 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
5251 if (player->GfxAction != action || player->GfxDir != dir)
5254 printf("Player frame reset! (%d => %d, %d => %d)\n",
5255 player->GfxAction, action, player->GfxDir, dir);
5258 player->GfxAction = action;
5259 player->GfxDir = dir;
5261 player->StepFrame = 0;
5265 #if USE_GFX_RESET_GFX_ANIMATION
5266 static void ResetGfxFrame(int x, int y, boolean redraw)
5268 int element = Feld[x][y];
5269 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
5270 int last_gfx_frame = GfxFrame[x][y];
5272 if (graphic_info[graphic].anim_global_sync)
5273 GfxFrame[x][y] = FrameCounter;
5274 else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
5275 GfxFrame[x][y] = CustomValue[x][y];
5276 else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
5277 GfxFrame[x][y] = element_info[element].collect_score;
5278 else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
5279 GfxFrame[x][y] = ChangeDelay[x][y];
5281 if (redraw && GfxFrame[x][y] != last_gfx_frame)
5282 DrawLevelGraphicAnimation(x, y, graphic);
5286 static void ResetGfxAnimation(int x, int y)
5288 GfxAction[x][y] = ACTION_DEFAULT;
5289 GfxDir[x][y] = MovDir[x][y];
5292 #if USE_GFX_RESET_GFX_ANIMATION
5293 ResetGfxFrame(x, y, FALSE);
5297 static void ResetRandomAnimationValue(int x, int y)
5299 GfxRandom[x][y] = INIT_GFX_RANDOM();
5302 void InitMovingField(int x, int y, int direction)
5304 int element = Feld[x][y];
5305 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
5306 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
5309 boolean is_moving_before, is_moving_after;
5311 boolean continues_moving = (WasJustMoving[x][y] && direction == MovDir[x][y]);
5314 /* check if element was/is moving or being moved before/after mode change */
5317 is_moving_before = (WasJustMoving[x][y] != 0);
5319 /* (!!! this does not work -- WasJustMoving is NOT a boolean value !!!) */
5320 is_moving_before = WasJustMoving[x][y];
5323 is_moving_before = (getElementMoveStepsizeExt(x, y, MovDir[x][y]) != 0);
5325 is_moving_after = (getElementMoveStepsizeExt(x, y, direction) != 0);
5327 /* reset animation only for moving elements which change direction of moving
5328 or which just started or stopped moving
5329 (else CEs with property "can move" / "not moving" are reset each frame) */
5330 #if USE_GFX_RESET_ONLY_WHEN_MOVING
5332 if (is_moving_before != is_moving_after ||
5333 direction != MovDir[x][y])
5334 ResetGfxAnimation(x, y);
5336 if ((is_moving_before || is_moving_after) && !continues_moving)
5337 ResetGfxAnimation(x, y);
5340 if (!continues_moving)
5341 ResetGfxAnimation(x, y);
5344 MovDir[x][y] = direction;
5345 GfxDir[x][y] = direction;
5347 #if USE_GFX_RESET_ONLY_WHEN_MOVING
5348 GfxAction[x][y] = (!is_moving_after ? ACTION_WAITING :
5349 direction == MV_DOWN && CAN_FALL(element) ?
5350 ACTION_FALLING : ACTION_MOVING);
5352 GfxAction[x][y] = (direction == MV_DOWN && CAN_FALL(element) ?
5353 ACTION_FALLING : ACTION_MOVING);
5356 /* this is needed for CEs with property "can move" / "not moving" */
5358 if (is_moving_after)
5360 if (Feld[newx][newy] == EL_EMPTY)
5361 Feld[newx][newy] = EL_BLOCKED;
5363 MovDir[newx][newy] = MovDir[x][y];
5365 #if USE_NEW_CUSTOM_VALUE
5366 CustomValue[newx][newy] = CustomValue[x][y];
5369 GfxFrame[newx][newy] = GfxFrame[x][y];
5370 GfxRandom[newx][newy] = GfxRandom[x][y];
5371 GfxAction[newx][newy] = GfxAction[x][y];
5372 GfxDir[newx][newy] = GfxDir[x][y];
5376 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
5378 int direction = MovDir[x][y];
5379 int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0);
5380 int newy = y + (direction & MV_UP ? -1 : direction & MV_DOWN ? +1 : 0);
5386 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
5388 int oldx = x, oldy = y;
5389 int direction = MovDir[x][y];
5391 if (direction == MV_LEFT)
5393 else if (direction == MV_RIGHT)
5395 else if (direction == MV_UP)
5397 else if (direction == MV_DOWN)
5400 *comes_from_x = oldx;
5401 *comes_from_y = oldy;
5404 int MovingOrBlocked2Element(int x, int y)
5406 int element = Feld[x][y];
5408 if (element == EL_BLOCKED)
5412 Blocked2Moving(x, y, &oldx, &oldy);
5413 return Feld[oldx][oldy];
5419 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
5421 /* like MovingOrBlocked2Element(), but if element is moving
5422 and (x,y) is the field the moving element is just leaving,
5423 return EL_BLOCKED instead of the element value */
5424 int element = Feld[x][y];
5426 if (IS_MOVING(x, y))
5428 if (element == EL_BLOCKED)
5432 Blocked2Moving(x, y, &oldx, &oldy);
5433 return Feld[oldx][oldy];
5442 static void RemoveField(int x, int y)
5444 Feld[x][y] = EL_EMPTY;
5450 #if USE_NEW_CUSTOM_VALUE
5451 CustomValue[x][y] = 0;
5455 ChangeDelay[x][y] = 0;
5456 ChangePage[x][y] = -1;
5457 Pushed[x][y] = FALSE;
5460 ExplodeField[x][y] = EX_TYPE_NONE;
5463 GfxElement[x][y] = EL_UNDEFINED;
5464 GfxAction[x][y] = ACTION_DEFAULT;
5465 GfxDir[x][y] = MV_NONE;
5467 /* !!! this would prevent the removed tile from being redrawn !!! */
5468 GfxRedraw[x][y] = GFX_REDRAW_NONE;
5472 void RemoveMovingField(int x, int y)
5474 int oldx = x, oldy = y, newx = x, newy = y;
5475 int element = Feld[x][y];
5476 int next_element = EL_UNDEFINED;
5478 if (element != EL_BLOCKED && !IS_MOVING(x, y))
5481 if (IS_MOVING(x, y))
5483 Moving2Blocked(x, y, &newx, &newy);
5485 if (Feld[newx][newy] != EL_BLOCKED)
5487 /* element is moving, but target field is not free (blocked), but
5488 already occupied by something different (example: acid pool);
5489 in this case, only remove the moving field, but not the target */
5491 RemoveField(oldx, oldy);
5493 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5495 TEST_DrawLevelField(oldx, oldy);
5500 else if (element == EL_BLOCKED)
5502 Blocked2Moving(x, y, &oldx, &oldy);
5503 if (!IS_MOVING(oldx, oldy))
5507 if (element == EL_BLOCKED &&
5508 (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
5509 Feld[oldx][oldy] == EL_QUICKSAND_FAST_EMPTYING ||
5510 Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
5511 Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
5512 Feld[oldx][oldy] == EL_DC_MAGIC_WALL_EMPTYING ||
5513 Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
5514 next_element = get_next_element(Feld[oldx][oldy]);
5516 RemoveField(oldx, oldy);
5517 RemoveField(newx, newy);
5519 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5521 if (next_element != EL_UNDEFINED)
5522 Feld[oldx][oldy] = next_element;
5524 TEST_DrawLevelField(oldx, oldy);
5525 TEST_DrawLevelField(newx, newy);
5528 void DrawDynamite(int x, int y)
5530 int sx = SCREENX(x), sy = SCREENY(y);
5531 int graphic = el2img(Feld[x][y]);
5534 if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
5537 if (IS_WALKABLE_INSIDE(Back[x][y]))
5541 DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
5542 else if (Store[x][y])
5543 DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
5545 frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5547 if (Back[x][y] || Store[x][y])
5548 DrawGraphicThruMask(sx, sy, graphic, frame);
5550 DrawGraphic(sx, sy, graphic, frame);
5553 void CheckDynamite(int x, int y)
5555 if (MovDelay[x][y] != 0) /* dynamite is still waiting to explode */
5559 if (MovDelay[x][y] != 0)
5562 PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5568 StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5573 static void setMinimalPlayerBoundaries(int *sx1, int *sy1, int *sx2, int *sy2)
5575 boolean num_checked_players = 0;
5578 for (i = 0; i < MAX_PLAYERS; i++)
5580 if (stored_player[i].active)
5582 int sx = stored_player[i].jx;
5583 int sy = stored_player[i].jy;
5585 if (num_checked_players == 0)
5592 *sx1 = MIN(*sx1, sx);
5593 *sy1 = MIN(*sy1, sy);
5594 *sx2 = MAX(*sx2, sx);
5595 *sy2 = MAX(*sy2, sy);
5598 num_checked_players++;
5603 static boolean checkIfAllPlayersFitToScreen_RND()
5605 int sx1 = 0, sy1 = 0, sx2 = 0, sy2 = 0;
5607 setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5609 return (sx2 - sx1 < SCR_FIELDX &&
5610 sy2 - sy1 < SCR_FIELDY);
5613 static void setScreenCenteredToAllPlayers(int *sx, int *sy)
5615 int sx1 = scroll_x, sy1 = scroll_y, sx2 = scroll_x, sy2 = scroll_y;
5617 setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5619 *sx = (sx1 + sx2) / 2;
5620 *sy = (sy1 + sy2) / 2;
5623 void DrawRelocateScreen(int old_x, int old_y, int x, int y, int move_dir,
5624 boolean center_screen, boolean quick_relocation)
5626 boolean ffwd_delay = (tape.playing && tape.fast_forward);
5627 boolean no_delay = (tape.warp_forward);
5628 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5629 int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5631 if (quick_relocation)
5633 if (!IN_VIS_FIELD(SCREENX(x), SCREENY(y)) || center_screen)
5635 if (!level.shifted_relocation || center_screen)
5637 /* quick relocation (without scrolling), with centering of screen */
5639 scroll_x = (x < SBX_Left + MIDPOSX ? SBX_Left :
5640 x > SBX_Right + MIDPOSX ? SBX_Right :
5643 scroll_y = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
5644 y > SBY_Lower + MIDPOSY ? SBY_Lower :
5649 /* quick relocation (without scrolling), but do not center screen */
5651 int center_scroll_x = (old_x < SBX_Left + MIDPOSX ? SBX_Left :
5652 old_x > SBX_Right + MIDPOSX ? SBX_Right :
5655 int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5656 old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5659 int offset_x = x + (scroll_x - center_scroll_x);
5660 int offset_y = y + (scroll_y - center_scroll_y);
5662 scroll_x = (offset_x < SBX_Left + MIDPOSX ? SBX_Left :
5663 offset_x > SBX_Right + MIDPOSX ? SBX_Right :
5664 offset_x - MIDPOSX);
5666 scroll_y = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5667 offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5668 offset_y - MIDPOSY);
5674 if (!level.shifted_relocation || center_screen)
5676 /* quick relocation (without scrolling), with centering of screen */
5678 scroll_x = (x < SBX_Left + MIDPOSX ? SBX_Left :
5679 x > SBX_Right + MIDPOSX ? SBX_Right :
5682 scroll_y = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
5683 y > SBY_Lower + MIDPOSY ? SBY_Lower :
5688 /* quick relocation (without scrolling), but do not center screen */
5690 int center_scroll_x = (old_x < SBX_Left + MIDPOSX ? SBX_Left :
5691 old_x > SBX_Right + MIDPOSX ? SBX_Right :
5694 int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5695 old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5698 int offset_x = x + (scroll_x - center_scroll_x);
5699 int offset_y = y + (scroll_y - center_scroll_y);
5701 scroll_x = (offset_x < SBX_Left + MIDPOSX ? SBX_Left :
5702 offset_x > SBX_Right + MIDPOSX ? SBX_Right :
5703 offset_x - MIDPOSX);
5705 scroll_y = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5706 offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5707 offset_y - MIDPOSY);
5710 /* quick relocation (without scrolling), inside visible screen area */
5712 int offset = game.scroll_delay_value;
5714 if ((move_dir == MV_LEFT && scroll_x > x - MIDPOSX + offset) ||
5715 (move_dir == MV_RIGHT && scroll_x < x - MIDPOSX - offset))
5716 scroll_x = x - MIDPOSX + (scroll_x < x - MIDPOSX ? -offset : +offset);
5718 if ((move_dir == MV_UP && scroll_y > y - MIDPOSY + offset) ||
5719 (move_dir == MV_DOWN && scroll_y < y - MIDPOSY - offset))
5720 scroll_y = y - MIDPOSY + (scroll_y < y - MIDPOSY ? -offset : +offset);
5722 /* don't scroll over playfield boundaries */
5723 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
5724 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
5726 /* don't scroll over playfield boundaries */
5727 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
5728 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
5732 RedrawPlayfield(TRUE, 0,0,0,0);
5737 int scroll_xx, scroll_yy;
5739 if (!level.shifted_relocation || center_screen)
5741 /* visible relocation (with scrolling), with centering of screen */
5743 scroll_xx = (x < SBX_Left + MIDPOSX ? SBX_Left :
5744 x > SBX_Right + MIDPOSX ? SBX_Right :
5747 scroll_yy = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
5748 y > SBY_Lower + MIDPOSY ? SBY_Lower :
5753 /* visible relocation (with scrolling), but do not center screen */
5755 int center_scroll_x = (old_x < SBX_Left + MIDPOSX ? SBX_Left :
5756 old_x > SBX_Right + MIDPOSX ? SBX_Right :
5759 int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5760 old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5763 int offset_x = x + (scroll_x - center_scroll_x);
5764 int offset_y = y + (scroll_y - center_scroll_y);
5766 scroll_xx = (offset_x < SBX_Left + MIDPOSX ? SBX_Left :
5767 offset_x > SBX_Right + MIDPOSX ? SBX_Right :
5768 offset_x - MIDPOSX);
5770 scroll_yy = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5771 offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5772 offset_y - MIDPOSY);
5777 /* visible relocation (with scrolling), with centering of screen */
5779 int scroll_xx = (x < SBX_Left + MIDPOSX ? SBX_Left :
5780 x > SBX_Right + MIDPOSX ? SBX_Right :
5783 int scroll_yy = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
5784 y > SBY_Lower + MIDPOSY ? SBY_Lower :
5788 ScrollScreen(NULL, SCROLL_GO_ON); /* scroll last frame to full tile */
5790 while (scroll_x != scroll_xx || scroll_y != scroll_yy)
5793 int fx = FX, fy = FY;
5795 dx = (scroll_xx < scroll_x ? +1 : scroll_xx > scroll_x ? -1 : 0);
5796 dy = (scroll_yy < scroll_y ? +1 : scroll_yy > scroll_y ? -1 : 0);
5798 if (dx == 0 && dy == 0) /* no scrolling needed at all */
5804 fx += dx * TILEX / 2;
5805 fy += dy * TILEY / 2;
5807 ScrollLevel(dx, dy);
5810 /* scroll in two steps of half tile size to make things smoother */
5811 BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
5813 Delay(wait_delay_value);
5815 /* scroll second step to align at full tile size */
5817 Delay(wait_delay_value);
5822 Delay(wait_delay_value);
5826 void RelocatePlayer(int jx, int jy, int el_player_raw)
5828 int el_player = GET_PLAYER_ELEMENT(el_player_raw);
5829 int player_nr = GET_PLAYER_NR(el_player);
5830 struct PlayerInfo *player = &stored_player[player_nr];
5831 boolean ffwd_delay = (tape.playing && tape.fast_forward);
5832 boolean no_delay = (tape.warp_forward);
5833 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5834 int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5835 int old_jx = player->jx;
5836 int old_jy = player->jy;
5837 int old_element = Feld[old_jx][old_jy];
5838 int element = Feld[jx][jy];
5839 boolean player_relocated = (old_jx != jx || old_jy != jy);
5841 int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
5842 int move_dir_vert = (jy < old_jy ? MV_UP : jy > old_jy ? MV_DOWN : 0);
5843 int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
5844 int enter_side_vert = MV_DIR_OPPOSITE(move_dir_vert);
5845 int leave_side_horiz = move_dir_horiz;
5846 int leave_side_vert = move_dir_vert;
5847 int enter_side = enter_side_horiz | enter_side_vert;
5848 int leave_side = leave_side_horiz | leave_side_vert;
5850 if (player->GameOver) /* do not reanimate dead player */
5853 if (!player_relocated) /* no need to relocate the player */
5856 if (IS_PLAYER(jx, jy)) /* player already placed at new position */
5858 RemoveField(jx, jy); /* temporarily remove newly placed player */
5859 DrawLevelField(jx, jy);
5862 if (player->present)
5864 while (player->MovPos)
5866 ScrollPlayer(player, SCROLL_GO_ON);
5867 ScrollScreen(NULL, SCROLL_GO_ON);
5869 AdvanceFrameAndPlayerCounters(player->index_nr);
5874 Delay(wait_delay_value);
5877 DrawPlayer(player); /* needed here only to cleanup last field */
5878 DrawLevelField(player->jx, player->jy); /* remove player graphic */
5880 player->is_moving = FALSE;
5883 if (IS_CUSTOM_ELEMENT(old_element))
5884 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
5886 player->index_bit, leave_side);
5888 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
5890 player->index_bit, leave_side);
5892 Feld[jx][jy] = el_player;
5893 InitPlayerField(jx, jy, el_player, TRUE);
5895 /* "InitPlayerField()" above sets Feld[jx][jy] to EL_EMPTY, but it may be
5896 possible that the relocation target field did not contain a player element,
5897 but a walkable element, to which the new player was relocated -- in this
5898 case, restore that (already initialized!) element on the player field */
5899 if (!ELEM_IS_PLAYER(element)) /* player may be set on walkable element */
5901 Feld[jx][jy] = element; /* restore previously existing element */
5903 /* !!! do not initialize already initialized element a second time !!! */
5904 /* (this causes at least problems with "element creation" CE trigger for
5905 already existing elements, and existing Sokoban fields counted twice) */
5906 InitField(jx, jy, FALSE);
5910 /* only visually relocate centered player */
5911 DrawRelocateScreen(old_jx, old_jy, player->jx, player->jy, player->MovDir,
5912 FALSE, level.instant_relocation);
5914 TestIfPlayerTouchesBadThing(jx, jy);
5915 TestIfPlayerTouchesCustomElement(jx, jy);
5917 if (IS_CUSTOM_ELEMENT(element))
5918 CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
5919 player->index_bit, enter_side);
5921 CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
5922 player->index_bit, enter_side);
5925 if (player->is_switching)
5927 /* ensure that relocation while still switching an element does not cause
5928 a new element to be treated as also switched directly after relocation
5929 (this is important for teleporter switches that teleport the player to
5930 a place where another teleporter switch is in the same direction, which
5931 would then incorrectly be treated as immediately switched before the
5932 direction key that caused the switch was released) */
5934 player->switch_x += jx - old_jx;
5935 player->switch_y += jy - old_jy;
5940 void Explode(int ex, int ey, int phase, int mode)
5946 /* !!! eliminate this variable !!! */
5947 int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
5949 if (game.explosions_delayed)
5951 ExplodeField[ex][ey] = mode;
5955 if (phase == EX_PHASE_START) /* initialize 'Store[][]' field */
5957 int center_element = Feld[ex][ey];
5958 int artwork_element, explosion_element; /* set these values later */
5961 /* --- This is only really needed (and now handled) in "Impact()". --- */
5962 /* do not explode moving elements that left the explode field in time */
5963 if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
5964 center_element == EL_EMPTY &&
5965 (mode == EX_TYPE_NORMAL || mode == EX_TYPE_CENTER))
5970 /* !!! at this place, the center element may be EL_BLOCKED !!! */
5971 if (mode == EX_TYPE_NORMAL ||
5972 mode == EX_TYPE_CENTER ||
5973 mode == EX_TYPE_CROSS)
5974 PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
5977 /* remove things displayed in background while burning dynamite */
5978 if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
5981 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
5983 /* put moving element to center field (and let it explode there) */
5984 center_element = MovingOrBlocked2Element(ex, ey);
5985 RemoveMovingField(ex, ey);
5986 Feld[ex][ey] = center_element;
5989 /* now "center_element" is finally determined -- set related values now */
5990 artwork_element = center_element; /* for custom player artwork */
5991 explosion_element = center_element; /* for custom player artwork */
5993 if (IS_PLAYER(ex, ey))
5995 int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
5997 artwork_element = stored_player[player_nr].artwork_element;
5999 if (level.use_explosion_element[player_nr])
6001 explosion_element = level.explosion_element[player_nr];
6002 artwork_element = explosion_element;
6007 if (mode == EX_TYPE_NORMAL ||
6008 mode == EX_TYPE_CENTER ||
6009 mode == EX_TYPE_CROSS)
6010 PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
6013 last_phase = element_info[explosion_element].explosion_delay + 1;
6015 for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
6017 int xx = x - ex + 1;
6018 int yy = y - ey + 1;
6021 if (!IN_LEV_FIELD(x, y) ||
6022 (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
6023 (mode == EX_TYPE_CROSS && (x != ex && y != ey)))
6026 element = Feld[x][y];
6028 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
6030 element = MovingOrBlocked2Element(x, y);
6032 if (!IS_EXPLOSION_PROOF(element))
6033 RemoveMovingField(x, y);
6036 /* indestructible elements can only explode in center (but not flames) */
6037 if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
6038 mode == EX_TYPE_BORDER)) ||
6039 element == EL_FLAMES)
6042 /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
6043 behaviour, for example when touching a yamyam that explodes to rocks
6044 with active deadly shield, a rock is created under the player !!! */
6045 /* (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8) */
6047 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
6048 (game.engine_version < VERSION_IDENT(3,1,0,0) ||
6049 (x == ex && y == ey && mode != EX_TYPE_BORDER)))
6051 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
6054 if (IS_ACTIVE_BOMB(element))
6056 /* re-activate things under the bomb like gate or penguin */
6057 Feld[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
6064 /* save walkable background elements while explosion on same tile */
6065 if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
6066 (x != ex || y != ey || mode == EX_TYPE_BORDER))
6067 Back[x][y] = element;
6069 /* ignite explodable elements reached by other explosion */
6070 if (element == EL_EXPLOSION)
6071 element = Store2[x][y];
6073 if (AmoebaNr[x][y] &&
6074 (element == EL_AMOEBA_FULL ||
6075 element == EL_BD_AMOEBA ||
6076 element == EL_AMOEBA_GROWING))
6078 AmoebaCnt[AmoebaNr[x][y]]--;
6079 AmoebaCnt2[AmoebaNr[x][y]]--;
6084 if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
6086 int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
6088 Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
6090 if (PLAYERINFO(ex, ey)->use_murphy)
6091 Store[x][y] = EL_EMPTY;
6094 /* !!! check this case -- currently needed for rnd_rado_negundo_v,
6095 !!! levels 015 018 019 020 021 022 023 026 027 028 !!! */
6096 else if (ELEM_IS_PLAYER(center_element))
6097 Store[x][y] = EL_EMPTY;
6098 else if (center_element == EL_YAMYAM)
6099 Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
6100 else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
6101 Store[x][y] = element_info[center_element].content.e[xx][yy];
6103 /* needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
6104 (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
6105 otherwise) -- FIX THIS !!! */
6106 else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
6107 Store[x][y] = element_info[element].content.e[1][1];
6109 else if (!CAN_EXPLODE(element))
6110 Store[x][y] = element_info[element].content.e[1][1];
6113 Store[x][y] = EL_EMPTY;
6115 if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
6116 center_element == EL_AMOEBA_TO_DIAMOND)
6117 Store2[x][y] = element;
6119 Feld[x][y] = EL_EXPLOSION;
6120 GfxElement[x][y] = artwork_element;
6122 ExplodePhase[x][y] = 1;
6123 ExplodeDelay[x][y] = last_phase;
6128 if (center_element == EL_YAMYAM)
6129 game.yamyam_content_nr =
6130 (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
6142 GfxFrame[x][y] = 0; /* restart explosion animation */
6144 last_phase = ExplodeDelay[x][y];
6146 ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
6150 /* activate this even in non-DEBUG version until cause for crash in
6151 getGraphicAnimationFrame() (see below) is found and eliminated */
6157 /* this can happen if the player leaves an explosion just in time */
6158 if (GfxElement[x][y] == EL_UNDEFINED)
6159 GfxElement[x][y] = EL_EMPTY;
6161 if (GfxElement[x][y] == EL_UNDEFINED)
6164 printf("Explode(): x = %d, y = %d: GfxElement == EL_UNDEFINED\n", x, y);
6165 printf("Explode(): This should never happen!\n");
6168 GfxElement[x][y] = EL_EMPTY;
6174 border_element = Store2[x][y];
6175 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
6176 border_element = StorePlayer[x][y];
6178 if (phase == element_info[border_element].ignition_delay ||
6179 phase == last_phase)
6181 boolean border_explosion = FALSE;
6183 if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
6184 !PLAYER_EXPLOSION_PROTECTED(x, y))
6186 KillPlayerUnlessExplosionProtected(x, y);
6187 border_explosion = TRUE;
6189 else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
6191 Feld[x][y] = Store2[x][y];
6194 border_explosion = TRUE;
6196 else if (border_element == EL_AMOEBA_TO_DIAMOND)
6198 AmoebeUmwandeln(x, y);
6200 border_explosion = TRUE;
6203 /* if an element just explodes due to another explosion (chain-reaction),
6204 do not immediately end the new explosion when it was the last frame of
6205 the explosion (as it would be done in the following "if"-statement!) */
6206 if (border_explosion && phase == last_phase)
6210 if (phase == last_phase)
6214 element = Feld[x][y] = Store[x][y];
6215 Store[x][y] = Store2[x][y] = 0;
6216 GfxElement[x][y] = EL_UNDEFINED;
6218 /* player can escape from explosions and might therefore be still alive */
6219 if (element >= EL_PLAYER_IS_EXPLODING_1 &&
6220 element <= EL_PLAYER_IS_EXPLODING_4)
6222 int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
6223 int explosion_element = EL_PLAYER_1 + player_nr;
6224 int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
6225 int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
6227 if (level.use_explosion_element[player_nr])
6228 explosion_element = level.explosion_element[player_nr];
6230 Feld[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
6231 element_info[explosion_element].content.e[xx][yy]);
6234 /* restore probably existing indestructible background element */
6235 if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
6236 element = Feld[x][y] = Back[x][y];
6239 MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
6240 GfxDir[x][y] = MV_NONE;
6241 ChangeDelay[x][y] = 0;
6242 ChangePage[x][y] = -1;
6244 #if USE_NEW_CUSTOM_VALUE
6245 CustomValue[x][y] = 0;
6248 InitField_WithBug2(x, y, FALSE);
6250 TEST_DrawLevelField(x, y);
6252 TestIfElementTouchesCustomElement(x, y);
6254 if (GFX_CRUMBLED(element))
6255 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6257 if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
6258 StorePlayer[x][y] = 0;
6260 if (ELEM_IS_PLAYER(element))
6261 RelocatePlayer(x, y, element);
6263 else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
6265 int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
6266 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
6269 TEST_DrawLevelFieldCrumbled(x, y);
6271 if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
6273 DrawLevelElement(x, y, Back[x][y]);
6274 DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
6276 else if (IS_WALKABLE_UNDER(Back[x][y]))
6278 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
6279 DrawLevelElementThruMask(x, y, Back[x][y]);
6281 else if (!IS_WALKABLE_INSIDE(Back[x][y]))
6282 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
6286 void DynaExplode(int ex, int ey)
6289 int dynabomb_element = Feld[ex][ey];
6290 int dynabomb_size = 1;
6291 boolean dynabomb_xl = FALSE;
6292 struct PlayerInfo *player;
6293 static int xy[4][2] =
6301 if (IS_ACTIVE_BOMB(dynabomb_element))
6303 player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
6304 dynabomb_size = player->dynabomb_size;
6305 dynabomb_xl = player->dynabomb_xl;
6306 player->dynabombs_left++;
6309 Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
6311 for (i = 0; i < NUM_DIRECTIONS; i++)
6313 for (j = 1; j <= dynabomb_size; j++)
6315 int x = ex + j * xy[i][0];
6316 int y = ey + j * xy[i][1];
6319 if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
6322 element = Feld[x][y];
6324 /* do not restart explosions of fields with active bombs */
6325 if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
6328 Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
6330 if (element != EL_EMPTY && element != EL_EXPLOSION &&
6331 !IS_DIGGABLE(element) && !dynabomb_xl)
6337 void Bang(int x, int y)
6339 int element = MovingOrBlocked2Element(x, y);
6340 int explosion_type = EX_TYPE_NORMAL;
6342 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
6344 struct PlayerInfo *player = PLAYERINFO(x, y);
6346 #if USE_FIX_CE_ACTION_WITH_PLAYER
6347 element = Feld[x][y] = player->initial_element;
6349 element = Feld[x][y] = (player->use_murphy ? EL_SP_MURPHY :
6350 player->element_nr);
6353 if (level.use_explosion_element[player->index_nr])
6355 int explosion_element = level.explosion_element[player->index_nr];
6357 if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
6358 explosion_type = EX_TYPE_CROSS;
6359 else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
6360 explosion_type = EX_TYPE_CENTER;
6368 case EL_BD_BUTTERFLY:
6371 case EL_DARK_YAMYAM:
6375 RaiseScoreElement(element);
6378 case EL_DYNABOMB_PLAYER_1_ACTIVE:
6379 case EL_DYNABOMB_PLAYER_2_ACTIVE:
6380 case EL_DYNABOMB_PLAYER_3_ACTIVE:
6381 case EL_DYNABOMB_PLAYER_4_ACTIVE:
6382 case EL_DYNABOMB_INCREASE_NUMBER:
6383 case EL_DYNABOMB_INCREASE_SIZE:
6384 case EL_DYNABOMB_INCREASE_POWER:
6385 explosion_type = EX_TYPE_DYNA;
6388 case EL_DC_LANDMINE:
6390 case EL_EM_EXIT_OPEN:
6391 case EL_EM_STEEL_EXIT_OPEN:
6393 explosion_type = EX_TYPE_CENTER;
6398 case EL_LAMP_ACTIVE:
6399 case EL_AMOEBA_TO_DIAMOND:
6400 if (!IS_PLAYER(x, y)) /* penguin and player may be at same field */
6401 explosion_type = EX_TYPE_CENTER;
6405 if (element_info[element].explosion_type == EXPLODES_CROSS)
6406 explosion_type = EX_TYPE_CROSS;
6407 else if (element_info[element].explosion_type == EXPLODES_1X1)
6408 explosion_type = EX_TYPE_CENTER;
6412 if (explosion_type == EX_TYPE_DYNA)
6415 Explode(x, y, EX_PHASE_START, explosion_type);
6417 CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
6420 void SplashAcid(int x, int y)
6422 if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
6423 (!IN_LEV_FIELD(x - 1, y - 2) ||
6424 !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
6425 Feld[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
6427 if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
6428 (!IN_LEV_FIELD(x + 1, y - 2) ||
6429 !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
6430 Feld[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
6432 PlayLevelSound(x, y, SND_ACID_SPLASHING);
6435 static void InitBeltMovement()
6437 static int belt_base_element[4] =
6439 EL_CONVEYOR_BELT_1_LEFT,
6440 EL_CONVEYOR_BELT_2_LEFT,
6441 EL_CONVEYOR_BELT_3_LEFT,
6442 EL_CONVEYOR_BELT_4_LEFT
6444 static int belt_base_active_element[4] =
6446 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
6447 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
6448 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
6449 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
6454 /* set frame order for belt animation graphic according to belt direction */
6455 for (i = 0; i < NUM_BELTS; i++)
6459 for (j = 0; j < NUM_BELT_PARTS; j++)
6461 int element = belt_base_active_element[belt_nr] + j;
6462 int graphic_1 = el2img(element);
6463 int graphic_2 = el2panelimg(element);
6465 if (game.belt_dir[i] == MV_LEFT)
6467 graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
6468 graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
6472 graphic_info[graphic_1].anim_mode |= ANIM_REVERSE;
6473 graphic_info[graphic_2].anim_mode |= ANIM_REVERSE;
6478 SCAN_PLAYFIELD(x, y)
6480 int element = Feld[x][y];
6482 for (i = 0; i < NUM_BELTS; i++)
6484 if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
6486 int e_belt_nr = getBeltNrFromBeltElement(element);
6489 if (e_belt_nr == belt_nr)
6491 int belt_part = Feld[x][y] - belt_base_element[belt_nr];
6493 Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
6500 static void ToggleBeltSwitch(int x, int y)
6502 static int belt_base_element[4] =
6504 EL_CONVEYOR_BELT_1_LEFT,
6505 EL_CONVEYOR_BELT_2_LEFT,
6506 EL_CONVEYOR_BELT_3_LEFT,
6507 EL_CONVEYOR_BELT_4_LEFT
6509 static int belt_base_active_element[4] =
6511 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
6512 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
6513 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
6514 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
6516 static int belt_base_switch_element[4] =
6518 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
6519 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
6520 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
6521 EL_CONVEYOR_BELT_4_SWITCH_LEFT
6523 static int belt_move_dir[4] =
6531 int element = Feld[x][y];
6532 int belt_nr = getBeltNrFromBeltSwitchElement(element);
6533 int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
6534 int belt_dir = belt_move_dir[belt_dir_nr];
6537 if (!IS_BELT_SWITCH(element))
6540 game.belt_dir_nr[belt_nr] = belt_dir_nr;
6541 game.belt_dir[belt_nr] = belt_dir;
6543 if (belt_dir_nr == 3)
6546 /* set frame order for belt animation graphic according to belt direction */
6547 for (i = 0; i < NUM_BELT_PARTS; i++)
6549 int element = belt_base_active_element[belt_nr] + i;
6550 int graphic_1 = el2img(element);
6551 int graphic_2 = el2panelimg(element);
6553 if (belt_dir == MV_LEFT)
6555 graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
6556 graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
6560 graphic_info[graphic_1].anim_mode |= ANIM_REVERSE;
6561 graphic_info[graphic_2].anim_mode |= ANIM_REVERSE;
6565 SCAN_PLAYFIELD(xx, yy)
6567 int element = Feld[xx][yy];
6569 if (IS_BELT_SWITCH(element))
6571 int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
6573 if (e_belt_nr == belt_nr)
6575 Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
6576 TEST_DrawLevelField(xx, yy);
6579 else if (IS_BELT(element) && belt_dir != MV_NONE)
6581 int e_belt_nr = getBeltNrFromBeltElement(element);
6583 if (e_belt_nr == belt_nr)
6585 int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
6587 Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
6588 TEST_DrawLevelField(xx, yy);
6591 else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
6593 int e_belt_nr = getBeltNrFromBeltActiveElement(element);
6595 if (e_belt_nr == belt_nr)
6597 int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
6599 Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
6600 TEST_DrawLevelField(xx, yy);
6606 static void ToggleSwitchgateSwitch(int x, int y)
6610 game.switchgate_pos = !game.switchgate_pos;
6612 SCAN_PLAYFIELD(xx, yy)
6614 int element = Feld[xx][yy];
6616 #if !USE_BOTH_SWITCHGATE_SWITCHES
6617 if (element == EL_SWITCHGATE_SWITCH_UP ||
6618 element == EL_SWITCHGATE_SWITCH_DOWN)
6620 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
6621 TEST_DrawLevelField(xx, yy);
6623 else if (element == EL_DC_SWITCHGATE_SWITCH_UP ||
6624 element == EL_DC_SWITCHGATE_SWITCH_DOWN)
6626 Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
6627 TEST_DrawLevelField(xx, yy);
6630 if (element == EL_SWITCHGATE_SWITCH_UP)
6632 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_DOWN;
6633 TEST_DrawLevelField(xx, yy);
6635 else if (element == EL_SWITCHGATE_SWITCH_DOWN)
6637 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP;
6638 TEST_DrawLevelField(xx, yy);
6640 else if (element == EL_DC_SWITCHGATE_SWITCH_UP)
6642 Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_DOWN;
6643 TEST_DrawLevelField(xx, yy);
6645 else if (element == EL_DC_SWITCHGATE_SWITCH_DOWN)
6647 Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP;
6648 TEST_DrawLevelField(xx, yy);
6651 else if (element == EL_SWITCHGATE_OPEN ||
6652 element == EL_SWITCHGATE_OPENING)
6654 Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
6656 PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
6658 else if (element == EL_SWITCHGATE_CLOSED ||
6659 element == EL_SWITCHGATE_CLOSING)
6661 Feld[xx][yy] = EL_SWITCHGATE_OPENING;
6663 PlayLevelSoundAction(xx, yy, ACTION_OPENING);
6668 static int getInvisibleActiveFromInvisibleElement(int element)
6670 return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
6671 element == EL_INVISIBLE_WALL ? EL_INVISIBLE_WALL_ACTIVE :
6672 element == EL_INVISIBLE_SAND ? EL_INVISIBLE_SAND_ACTIVE :
6676 static int getInvisibleFromInvisibleActiveElement(int element)
6678 return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
6679 element == EL_INVISIBLE_WALL_ACTIVE ? EL_INVISIBLE_WALL :
6680 element == EL_INVISIBLE_SAND_ACTIVE ? EL_INVISIBLE_SAND :
6684 static void RedrawAllLightSwitchesAndInvisibleElements()
6688 SCAN_PLAYFIELD(x, y)
6690 int element = Feld[x][y];
6692 if (element == EL_LIGHT_SWITCH &&
6693 game.light_time_left > 0)
6695 Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
6696 TEST_DrawLevelField(x, y);
6698 else if (element == EL_LIGHT_SWITCH_ACTIVE &&
6699 game.light_time_left == 0)
6701 Feld[x][y] = EL_LIGHT_SWITCH;
6702 TEST_DrawLevelField(x, y);
6704 else if (element == EL_EMC_DRIPPER &&
6705 game.light_time_left > 0)
6707 Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
6708 TEST_DrawLevelField(x, y);
6710 else if (element == EL_EMC_DRIPPER_ACTIVE &&
6711 game.light_time_left == 0)
6713 Feld[x][y] = EL_EMC_DRIPPER;
6714 TEST_DrawLevelField(x, y);
6716 else if (element == EL_INVISIBLE_STEELWALL ||
6717 element == EL_INVISIBLE_WALL ||
6718 element == EL_INVISIBLE_SAND)
6720 if (game.light_time_left > 0)
6721 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
6723 TEST_DrawLevelField(x, y);
6725 /* uncrumble neighbour fields, if needed */
6726 if (element == EL_INVISIBLE_SAND)
6727 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6729 else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6730 element == EL_INVISIBLE_WALL_ACTIVE ||
6731 element == EL_INVISIBLE_SAND_ACTIVE)
6733 if (game.light_time_left == 0)
6734 Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
6736 TEST_DrawLevelField(x, y);
6738 /* re-crumble neighbour fields, if needed */
6739 if (element == EL_INVISIBLE_SAND)
6740 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6745 static void RedrawAllInvisibleElementsForLenses()
6749 SCAN_PLAYFIELD(x, y)
6751 int element = Feld[x][y];
6753 if (element == EL_EMC_DRIPPER &&
6754 game.lenses_time_left > 0)
6756 Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
6757 TEST_DrawLevelField(x, y);
6759 else if (element == EL_EMC_DRIPPER_ACTIVE &&
6760 game.lenses_time_left == 0)
6762 Feld[x][y] = EL_EMC_DRIPPER;
6763 TEST_DrawLevelField(x, y);
6765 else if (element == EL_INVISIBLE_STEELWALL ||
6766 element == EL_INVISIBLE_WALL ||
6767 element == EL_INVISIBLE_SAND)
6769 if (game.lenses_time_left > 0)
6770 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
6772 TEST_DrawLevelField(x, y);
6774 /* uncrumble neighbour fields, if needed */
6775 if (element == EL_INVISIBLE_SAND)
6776 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6778 else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6779 element == EL_INVISIBLE_WALL_ACTIVE ||
6780 element == EL_INVISIBLE_SAND_ACTIVE)
6782 if (game.lenses_time_left == 0)
6783 Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
6785 TEST_DrawLevelField(x, y);
6787 /* re-crumble neighbour fields, if needed */
6788 if (element == EL_INVISIBLE_SAND)
6789 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6794 static void RedrawAllInvisibleElementsForMagnifier()
6798 SCAN_PLAYFIELD(x, y)
6800 int element = Feld[x][y];
6802 if (element == EL_EMC_FAKE_GRASS &&
6803 game.magnify_time_left > 0)
6805 Feld[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
6806 TEST_DrawLevelField(x, y);
6808 else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
6809 game.magnify_time_left == 0)
6811 Feld[x][y] = EL_EMC_FAKE_GRASS;
6812 TEST_DrawLevelField(x, y);
6814 else if (IS_GATE_GRAY(element) &&
6815 game.magnify_time_left > 0)
6817 Feld[x][y] = (IS_RND_GATE_GRAY(element) ?
6818 element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
6819 IS_EM_GATE_GRAY(element) ?
6820 element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
6821 IS_EMC_GATE_GRAY(element) ?
6822 element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
6823 IS_DC_GATE_GRAY(element) ?
6824 EL_DC_GATE_WHITE_GRAY_ACTIVE :
6826 TEST_DrawLevelField(x, y);
6828 else if (IS_GATE_GRAY_ACTIVE(element) &&
6829 game.magnify_time_left == 0)
6831 Feld[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
6832 element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
6833 IS_EM_GATE_GRAY_ACTIVE(element) ?
6834 element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
6835 IS_EMC_GATE_GRAY_ACTIVE(element) ?
6836 element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
6837 IS_DC_GATE_GRAY_ACTIVE(element) ?
6838 EL_DC_GATE_WHITE_GRAY :
6840 TEST_DrawLevelField(x, y);
6845 static void ToggleLightSwitch(int x, int y)
6847 int element = Feld[x][y];
6849 game.light_time_left =
6850 (element == EL_LIGHT_SWITCH ?
6851 level.time_light * FRAMES_PER_SECOND : 0);
6853 RedrawAllLightSwitchesAndInvisibleElements();
6856 static void ActivateTimegateSwitch(int x, int y)
6860 game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
6862 SCAN_PLAYFIELD(xx, yy)
6864 int element = Feld[xx][yy];
6866 if (element == EL_TIMEGATE_CLOSED ||
6867 element == EL_TIMEGATE_CLOSING)
6869 Feld[xx][yy] = EL_TIMEGATE_OPENING;
6870 PlayLevelSound(xx, yy, SND_CLASS_TIMEGATE_OPENING);
6874 else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
6876 Feld[xx][yy] = EL_TIMEGATE_SWITCH;
6877 TEST_DrawLevelField(xx, yy);
6884 Feld[x][y] = (Feld[x][y] == EL_TIMEGATE_SWITCH ? EL_TIMEGATE_SWITCH_ACTIVE :
6885 EL_DC_TIMEGATE_SWITCH_ACTIVE);
6887 Feld[x][y] = EL_TIMEGATE_SWITCH_ACTIVE;
6891 void Impact(int x, int y)
6893 boolean last_line = (y == lev_fieldy - 1);
6894 boolean object_hit = FALSE;
6895 boolean impact = (last_line || object_hit);
6896 int element = Feld[x][y];
6897 int smashed = EL_STEELWALL;
6899 if (!last_line) /* check if element below was hit */
6901 if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
6904 object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
6905 MovDir[x][y + 1] != MV_DOWN ||
6906 MovPos[x][y + 1] <= TILEY / 2));
6908 /* do not smash moving elements that left the smashed field in time */
6909 if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
6910 ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
6913 #if USE_QUICKSAND_IMPACT_BUGFIX
6914 if (Feld[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
6916 RemoveMovingField(x, y + 1);
6917 Feld[x][y + 1] = EL_QUICKSAND_EMPTY;
6918 Feld[x][y + 2] = EL_ROCK;
6919 TEST_DrawLevelField(x, y + 2);
6924 if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTYING && object_hit == FALSE)
6926 RemoveMovingField(x, y + 1);
6927 Feld[x][y + 1] = EL_QUICKSAND_FAST_EMPTY;
6928 Feld[x][y + 2] = EL_ROCK;
6929 TEST_DrawLevelField(x, y + 2);
6936 smashed = MovingOrBlocked2Element(x, y + 1);
6938 impact = (last_line || object_hit);
6941 if (!last_line && smashed == EL_ACID) /* element falls into acid */
6943 SplashAcid(x, y + 1);
6947 /* !!! not sufficient for all cases -- see EL_PEARL below !!! */
6948 /* only reset graphic animation if graphic really changes after impact */
6950 el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
6952 ResetGfxAnimation(x, y);
6953 TEST_DrawLevelField(x, y);
6956 if (impact && CAN_EXPLODE_IMPACT(element))
6961 else if (impact && element == EL_PEARL &&
6962 smashed != EL_DC_MAGIC_WALL && smashed != EL_DC_MAGIC_WALL_ACTIVE)
6964 ResetGfxAnimation(x, y);
6966 Feld[x][y] = EL_PEARL_BREAKING;
6967 PlayLevelSound(x, y, SND_PEARL_BREAKING);
6970 else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
6972 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6977 if (impact && element == EL_AMOEBA_DROP)
6979 if (object_hit && IS_PLAYER(x, y + 1))
6980 KillPlayerUnlessEnemyProtected(x, y + 1);
6981 else if (object_hit && smashed == EL_PENGUIN)
6985 Feld[x][y] = EL_AMOEBA_GROWING;
6986 Store[x][y] = EL_AMOEBA_WET;
6988 ResetRandomAnimationValue(x, y);
6993 if (object_hit) /* check which object was hit */
6995 if ((CAN_PASS_MAGIC_WALL(element) &&
6996 (smashed == EL_MAGIC_WALL ||
6997 smashed == EL_BD_MAGIC_WALL)) ||
6998 (CAN_PASS_DC_MAGIC_WALL(element) &&
6999 smashed == EL_DC_MAGIC_WALL))
7002 int activated_magic_wall =
7003 (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
7004 smashed == EL_BD_MAGIC_WALL ? EL_BD_MAGIC_WALL_ACTIVE :
7005 EL_DC_MAGIC_WALL_ACTIVE);
7007 /* activate magic wall / mill */
7008 SCAN_PLAYFIELD(xx, yy)
7010 if (Feld[xx][yy] == smashed)
7011 Feld[xx][yy] = activated_magic_wall;
7014 game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
7015 game.magic_wall_active = TRUE;
7017 PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
7018 SND_MAGIC_WALL_ACTIVATING :
7019 smashed == EL_BD_MAGIC_WALL ?
7020 SND_BD_MAGIC_WALL_ACTIVATING :
7021 SND_DC_MAGIC_WALL_ACTIVATING));
7024 if (IS_PLAYER(x, y + 1))
7026 if (CAN_SMASH_PLAYER(element))
7028 KillPlayerUnlessEnemyProtected(x, y + 1);
7032 else if (smashed == EL_PENGUIN)
7034 if (CAN_SMASH_PLAYER(element))
7040 else if (element == EL_BD_DIAMOND)
7042 if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
7048 else if (((element == EL_SP_INFOTRON ||
7049 element == EL_SP_ZONK) &&
7050 (smashed == EL_SP_SNIKSNAK ||
7051 smashed == EL_SP_ELECTRON ||
7052 smashed == EL_SP_DISK_ORANGE)) ||
7053 (element == EL_SP_INFOTRON &&
7054 smashed == EL_SP_DISK_YELLOW))
7059 else if (CAN_SMASH_EVERYTHING(element))
7061 if (IS_CLASSIC_ENEMY(smashed) ||
7062 CAN_EXPLODE_SMASHED(smashed))
7067 else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
7069 if (smashed == EL_LAMP ||
7070 smashed == EL_LAMP_ACTIVE)
7075 else if (smashed == EL_NUT)
7077 Feld[x][y + 1] = EL_NUT_BREAKING;
7078 PlayLevelSound(x, y, SND_NUT_BREAKING);
7079 RaiseScoreElement(EL_NUT);
7082 else if (smashed == EL_PEARL)
7084 ResetGfxAnimation(x, y);
7086 Feld[x][y + 1] = EL_PEARL_BREAKING;
7087 PlayLevelSound(x, y, SND_PEARL_BREAKING);
7090 else if (smashed == EL_DIAMOND)
7092 Feld[x][y + 1] = EL_DIAMOND_BREAKING;
7093 PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
7096 else if (IS_BELT_SWITCH(smashed))
7098 ToggleBeltSwitch(x, y + 1);
7100 else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
7101 smashed == EL_SWITCHGATE_SWITCH_DOWN ||
7102 smashed == EL_DC_SWITCHGATE_SWITCH_UP ||
7103 smashed == EL_DC_SWITCHGATE_SWITCH_DOWN)
7105 ToggleSwitchgateSwitch(x, y + 1);
7107 else if (smashed == EL_LIGHT_SWITCH ||
7108 smashed == EL_LIGHT_SWITCH_ACTIVE)
7110 ToggleLightSwitch(x, y + 1);
7115 TestIfElementSmashesCustomElement(x, y, MV_DOWN);
7118 CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
7120 CheckElementChangeBySide(x, y + 1, smashed, element,
7121 CE_SWITCHED, CH_SIDE_TOP);
7122 CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
7128 CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
7133 /* play sound of magic wall / mill */
7135 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
7136 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ||
7137 Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE))
7139 if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
7140 PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
7141 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
7142 PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
7143 else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
7144 PlayLevelSound(x, y, SND_DC_MAGIC_WALL_FILLING);
7149 /* play sound of object that hits the ground */
7150 if (last_line || object_hit)
7151 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
7154 inline static void TurnRoundExt(int x, int y)
7166 { 0, 0 }, { 0, 0 }, { 0, 0 },
7171 int left, right, back;
7175 { MV_DOWN, MV_UP, MV_RIGHT },
7176 { MV_UP, MV_DOWN, MV_LEFT },
7178 { MV_LEFT, MV_RIGHT, MV_DOWN },
7182 { MV_RIGHT, MV_LEFT, MV_UP }
7185 int element = Feld[x][y];
7186 int move_pattern = element_info[element].move_pattern;
7188 int old_move_dir = MovDir[x][y];
7189 int left_dir = turn[old_move_dir].left;
7190 int right_dir = turn[old_move_dir].right;
7191 int back_dir = turn[old_move_dir].back;
7193 int left_dx = move_xy[left_dir].dx, left_dy = move_xy[left_dir].dy;
7194 int right_dx = move_xy[right_dir].dx, right_dy = move_xy[right_dir].dy;
7195 int move_dx = move_xy[old_move_dir].dx, move_dy = move_xy[old_move_dir].dy;
7196 int back_dx = move_xy[back_dir].dx, back_dy = move_xy[back_dir].dy;
7198 int left_x = x + left_dx, left_y = y + left_dy;
7199 int right_x = x + right_dx, right_y = y + right_dy;
7200 int move_x = x + move_dx, move_y = y + move_dy;
7204 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
7206 TestIfBadThingTouchesOtherBadThing(x, y);
7208 if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
7209 MovDir[x][y] = right_dir;
7210 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
7211 MovDir[x][y] = left_dir;
7213 if (element == EL_BUG && MovDir[x][y] != old_move_dir)
7215 else if (element == EL_BD_BUTTERFLY) /* && MovDir[x][y] == left_dir) */
7218 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
7220 TestIfBadThingTouchesOtherBadThing(x, y);
7222 if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
7223 MovDir[x][y] = left_dir;
7224 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
7225 MovDir[x][y] = right_dir;
7227 if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
7229 else if (element == EL_BD_FIREFLY) /* && MovDir[x][y] == right_dir) */
7232 else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
7234 TestIfBadThingTouchesOtherBadThing(x, y);
7236 if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
7237 MovDir[x][y] = left_dir;
7238 else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
7239 MovDir[x][y] = right_dir;
7241 if (MovDir[x][y] != old_move_dir)
7244 else if (element == EL_YAMYAM)
7246 boolean can_turn_left = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
7247 boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
7249 if (can_turn_left && can_turn_right)
7250 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7251 else if (can_turn_left)
7252 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7253 else if (can_turn_right)
7254 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7256 MovDir[x][y] = back_dir;
7258 MovDelay[x][y] = 16 + 16 * RND(3);
7260 else if (element == EL_DARK_YAMYAM)
7262 boolean can_turn_left = DARK_YAMYAM_CAN_ENTER_FIELD(element,
7264 boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
7267 if (can_turn_left && can_turn_right)
7268 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7269 else if (can_turn_left)
7270 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7271 else if (can_turn_right)
7272 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7274 MovDir[x][y] = back_dir;
7276 MovDelay[x][y] = 16 + 16 * RND(3);
7278 else if (element == EL_PACMAN)
7280 boolean can_turn_left = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
7281 boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
7283 if (can_turn_left && can_turn_right)
7284 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7285 else if (can_turn_left)
7286 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7287 else if (can_turn_right)
7288 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7290 MovDir[x][y] = back_dir;
7292 MovDelay[x][y] = 6 + RND(40);
7294 else if (element == EL_PIG)
7296 boolean can_turn_left = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
7297 boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
7298 boolean can_move_on = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
7299 boolean should_turn_left, should_turn_right, should_move_on;
7301 int rnd = RND(rnd_value);
7303 should_turn_left = (can_turn_left &&
7305 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
7306 y + back_dy + left_dy)));
7307 should_turn_right = (can_turn_right &&
7309 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
7310 y + back_dy + right_dy)));
7311 should_move_on = (can_move_on &&
7314 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
7315 y + move_dy + left_dy) ||
7316 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
7317 y + move_dy + right_dy)));
7319 if (should_turn_left || should_turn_right || should_move_on)
7321 if (should_turn_left && should_turn_right && should_move_on)
7322 MovDir[x][y] = (rnd < rnd_value / 3 ? left_dir :
7323 rnd < 2 * rnd_value / 3 ? right_dir :
7325 else if (should_turn_left && should_turn_right)
7326 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
7327 else if (should_turn_left && should_move_on)
7328 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
7329 else if (should_turn_right && should_move_on)
7330 MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
7331 else if (should_turn_left)
7332 MovDir[x][y] = left_dir;
7333 else if (should_turn_right)
7334 MovDir[x][y] = right_dir;
7335 else if (should_move_on)
7336 MovDir[x][y] = old_move_dir;
7338 else if (can_move_on && rnd > rnd_value / 8)
7339 MovDir[x][y] = old_move_dir;
7340 else if (can_turn_left && can_turn_right)
7341 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
7342 else if (can_turn_left && rnd > rnd_value / 8)
7343 MovDir[x][y] = left_dir;
7344 else if (can_turn_right && rnd > rnd_value/8)
7345 MovDir[x][y] = right_dir;
7347 MovDir[x][y] = back_dir;
7349 xx = x + move_xy[MovDir[x][y]].dx;
7350 yy = y + move_xy[MovDir[x][y]].dy;
7352 if (!IN_LEV_FIELD(xx, yy) ||
7353 (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy])))
7354 MovDir[x][y] = old_move_dir;
7358 else if (element == EL_DRAGON)
7360 boolean can_turn_left = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
7361 boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
7362 boolean can_move_on = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
7364 int rnd = RND(rnd_value);
7366 if (can_move_on && rnd > rnd_value / 8)
7367 MovDir[x][y] = old_move_dir;
7368 else if (can_turn_left && can_turn_right)
7369 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
7370 else if (can_turn_left && rnd > rnd_value / 8)
7371 MovDir[x][y] = left_dir;
7372 else if (can_turn_right && rnd > rnd_value / 8)
7373 MovDir[x][y] = right_dir;
7375 MovDir[x][y] = back_dir;
7377 xx = x + move_xy[MovDir[x][y]].dx;
7378 yy = y + move_xy[MovDir[x][y]].dy;
7380 if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
7381 MovDir[x][y] = old_move_dir;
7385 else if (element == EL_MOLE)
7387 boolean can_move_on =
7388 (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
7389 IS_AMOEBOID(Feld[move_x][move_y]) ||
7390 Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
7393 boolean can_turn_left =
7394 (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
7395 IS_AMOEBOID(Feld[left_x][left_y])));
7397 boolean can_turn_right =
7398 (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
7399 IS_AMOEBOID(Feld[right_x][right_y])));
7401 if (can_turn_left && can_turn_right)
7402 MovDir[x][y] = (RND(2) ? left_dir : right_dir);
7403 else if (can_turn_left)
7404 MovDir[x][y] = left_dir;
7406 MovDir[x][y] = right_dir;
7409 if (MovDir[x][y] != old_move_dir)
7412 else if (element == EL_BALLOON)
7414 MovDir[x][y] = game.wind_direction;
7417 else if (element == EL_SPRING)
7419 #if USE_NEW_SPRING_BUMPER
7420 if (MovDir[x][y] & MV_HORIZONTAL)
7422 if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
7423 !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
7425 Feld[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
7426 ResetGfxAnimation(move_x, move_y);
7427 TEST_DrawLevelField(move_x, move_y);
7429 MovDir[x][y] = back_dir;
7431 else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
7432 SPRING_CAN_ENTER_FIELD(element, x, y + 1))
7433 MovDir[x][y] = MV_NONE;
7436 if (MovDir[x][y] & MV_HORIZONTAL &&
7437 (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
7438 SPRING_CAN_ENTER_FIELD(element, x, y + 1)))
7439 MovDir[x][y] = MV_NONE;
7444 else if (element == EL_ROBOT ||
7445 element == EL_SATELLITE ||
7446 element == EL_PENGUIN ||
7447 element == EL_EMC_ANDROID)
7449 int attr_x = -1, attr_y = -1;
7460 for (i = 0; i < MAX_PLAYERS; i++)
7462 struct PlayerInfo *player = &stored_player[i];
7463 int jx = player->jx, jy = player->jy;
7465 if (!player->active)
7469 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7477 if (element == EL_ROBOT && ZX >= 0 && ZY >= 0 &&
7478 (Feld[ZX][ZY] == EL_ROBOT_WHEEL_ACTIVE ||
7479 game.engine_version < VERSION_IDENT(3,1,0,0)))
7485 if (element == EL_PENGUIN)
7488 static int xy[4][2] =
7496 for (i = 0; i < NUM_DIRECTIONS; i++)
7498 int ex = x + xy[i][0];
7499 int ey = y + xy[i][1];
7501 if (IN_LEV_FIELD(ex, ey) && (Feld[ex][ey] == EL_EXIT_OPEN ||
7502 Feld[ex][ey] == EL_EM_EXIT_OPEN ||
7503 Feld[ex][ey] == EL_STEEL_EXIT_OPEN ||
7504 Feld[ex][ey] == EL_EM_STEEL_EXIT_OPEN))
7513 MovDir[x][y] = MV_NONE;
7515 MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
7516 else if (attr_x > x)
7517 MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
7519 MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
7520 else if (attr_y > y)
7521 MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
7523 if (element == EL_ROBOT)
7527 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7528 MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
7529 Moving2Blocked(x, y, &newx, &newy);
7531 if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
7532 MovDelay[x][y] = 8 + 8 * !RND(3);
7534 MovDelay[x][y] = 16;
7536 else if (element == EL_PENGUIN)
7542 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7544 boolean first_horiz = RND(2);
7545 int new_move_dir = MovDir[x][y];
7548 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7549 Moving2Blocked(x, y, &newx, &newy);
7551 if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
7555 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7556 Moving2Blocked(x, y, &newx, &newy);
7558 if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
7561 MovDir[x][y] = old_move_dir;
7565 else if (element == EL_SATELLITE)
7571 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7573 boolean first_horiz = RND(2);
7574 int new_move_dir = MovDir[x][y];
7577 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7578 Moving2Blocked(x, y, &newx, &newy);
7580 if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
7584 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7585 Moving2Blocked(x, y, &newx, &newy);
7587 if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
7590 MovDir[x][y] = old_move_dir;
7594 else if (element == EL_EMC_ANDROID)
7596 static int check_pos[16] =
7598 -1, /* 0 => (invalid) */
7599 7, /* 1 => MV_LEFT */
7600 3, /* 2 => MV_RIGHT */
7601 -1, /* 3 => (invalid) */
7603 0, /* 5 => MV_LEFT | MV_UP */
7604 2, /* 6 => MV_RIGHT | MV_UP */
7605 -1, /* 7 => (invalid) */
7606 5, /* 8 => MV_DOWN */
7607 6, /* 9 => MV_LEFT | MV_DOWN */
7608 4, /* 10 => MV_RIGHT | MV_DOWN */
7609 -1, /* 11 => (invalid) */
7610 -1, /* 12 => (invalid) */
7611 -1, /* 13 => (invalid) */
7612 -1, /* 14 => (invalid) */
7613 -1, /* 15 => (invalid) */
7621 { -1, -1, MV_LEFT | MV_UP },
7623 { +1, -1, MV_RIGHT | MV_UP },
7624 { +1, 0, MV_RIGHT },
7625 { +1, +1, MV_RIGHT | MV_DOWN },
7627 { -1, +1, MV_LEFT | MV_DOWN },
7630 int start_pos, check_order;
7631 boolean can_clone = FALSE;
7634 /* check if there is any free field around current position */
7635 for (i = 0; i < 8; i++)
7637 int newx = x + check_xy[i].dx;
7638 int newy = y + check_xy[i].dy;
7640 if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7648 if (can_clone) /* randomly find an element to clone */
7652 start_pos = check_pos[RND(8)];
7653 check_order = (RND(2) ? -1 : +1);
7655 for (i = 0; i < 8; i++)
7657 int pos_raw = start_pos + i * check_order;
7658 int pos = (pos_raw + 8) % 8;
7659 int newx = x + check_xy[pos].dx;
7660 int newy = y + check_xy[pos].dy;
7662 if (ANDROID_CAN_CLONE_FIELD(newx, newy))
7664 element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
7665 element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
7667 Store[x][y] = Feld[newx][newy];
7676 if (can_clone) /* randomly find a direction to move */
7680 start_pos = check_pos[RND(8)];
7681 check_order = (RND(2) ? -1 : +1);
7683 for (i = 0; i < 8; i++)
7685 int pos_raw = start_pos + i * check_order;
7686 int pos = (pos_raw + 8) % 8;
7687 int newx = x + check_xy[pos].dx;
7688 int newy = y + check_xy[pos].dy;
7689 int new_move_dir = check_xy[pos].dir;
7691 if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7693 MovDir[x][y] = new_move_dir;
7694 MovDelay[x][y] = level.android_clone_time * 8 + 1;
7703 if (can_clone) /* cloning and moving successful */
7706 /* cannot clone -- try to move towards player */
7708 start_pos = check_pos[MovDir[x][y] & 0x0f];
7709 check_order = (RND(2) ? -1 : +1);
7711 for (i = 0; i < 3; i++)
7713 /* first check start_pos, then previous/next or (next/previous) pos */
7714 int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
7715 int pos = (pos_raw + 8) % 8;
7716 int newx = x + check_xy[pos].dx;
7717 int newy = y + check_xy[pos].dy;
7718 int new_move_dir = check_xy[pos].dir;
7720 if (IS_PLAYER(newx, newy))
7723 if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7725 MovDir[x][y] = new_move_dir;
7726 MovDelay[x][y] = level.android_move_time * 8 + 1;
7733 else if (move_pattern == MV_TURNING_LEFT ||
7734 move_pattern == MV_TURNING_RIGHT ||
7735 move_pattern == MV_TURNING_LEFT_RIGHT ||
7736 move_pattern == MV_TURNING_RIGHT_LEFT ||
7737 move_pattern == MV_TURNING_RANDOM ||
7738 move_pattern == MV_ALL_DIRECTIONS)
7740 boolean can_turn_left =
7741 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
7742 boolean can_turn_right =
7743 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
7745 if (element_info[element].move_stepsize == 0) /* "not moving" */
7748 if (move_pattern == MV_TURNING_LEFT)
7749 MovDir[x][y] = left_dir;
7750 else if (move_pattern == MV_TURNING_RIGHT)
7751 MovDir[x][y] = right_dir;
7752 else if (move_pattern == MV_TURNING_LEFT_RIGHT)
7753 MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
7754 else if (move_pattern == MV_TURNING_RIGHT_LEFT)
7755 MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
7756 else if (move_pattern == MV_TURNING_RANDOM)
7757 MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
7758 can_turn_right && !can_turn_left ? right_dir :
7759 RND(2) ? left_dir : right_dir);
7760 else if (can_turn_left && can_turn_right)
7761 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7762 else if (can_turn_left)
7763 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7764 else if (can_turn_right)
7765 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7767 MovDir[x][y] = back_dir;
7769 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7771 else if (move_pattern == MV_HORIZONTAL ||
7772 move_pattern == MV_VERTICAL)
7774 if (move_pattern & old_move_dir)
7775 MovDir[x][y] = back_dir;
7776 else if (move_pattern == MV_HORIZONTAL)
7777 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
7778 else if (move_pattern == MV_VERTICAL)
7779 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
7781 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7783 else if (move_pattern & MV_ANY_DIRECTION)
7785 MovDir[x][y] = move_pattern;
7786 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7788 else if (move_pattern & MV_WIND_DIRECTION)
7790 MovDir[x][y] = game.wind_direction;
7791 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7793 else if (move_pattern == MV_ALONG_LEFT_SIDE)
7795 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
7796 MovDir[x][y] = left_dir;
7797 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7798 MovDir[x][y] = right_dir;
7800 if (MovDir[x][y] != old_move_dir)
7801 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7803 else if (move_pattern == MV_ALONG_RIGHT_SIDE)
7805 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
7806 MovDir[x][y] = right_dir;
7807 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7808 MovDir[x][y] = left_dir;
7810 if (MovDir[x][y] != old_move_dir)
7811 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7813 else if (move_pattern == MV_TOWARDS_PLAYER ||
7814 move_pattern == MV_AWAY_FROM_PLAYER)
7816 int attr_x = -1, attr_y = -1;
7818 boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
7829 for (i = 0; i < MAX_PLAYERS; i++)
7831 struct PlayerInfo *player = &stored_player[i];
7832 int jx = player->jx, jy = player->jy;
7834 if (!player->active)
7838 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7846 MovDir[x][y] = MV_NONE;
7848 MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
7849 else if (attr_x > x)
7850 MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
7852 MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
7853 else if (attr_y > y)
7854 MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
7856 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7858 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7860 boolean first_horiz = RND(2);
7861 int new_move_dir = MovDir[x][y];
7863 if (element_info[element].move_stepsize == 0) /* "not moving" */
7865 first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
7866 MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7872 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7873 Moving2Blocked(x, y, &newx, &newy);
7875 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7879 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7880 Moving2Blocked(x, y, &newx, &newy);
7882 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7885 MovDir[x][y] = old_move_dir;
7888 else if (move_pattern == MV_WHEN_PUSHED ||
7889 move_pattern == MV_WHEN_DROPPED)
7891 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7892 MovDir[x][y] = MV_NONE;
7896 else if (move_pattern & MV_MAZE_RUNNER_STYLE)
7898 static int test_xy[7][2] =
7908 static int test_dir[7] =
7918 boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
7919 int move_preference = -1000000; /* start with very low preference */
7920 int new_move_dir = MV_NONE;
7921 int start_test = RND(4);
7924 for (i = 0; i < NUM_DIRECTIONS; i++)
7926 int move_dir = test_dir[start_test + i];
7927 int move_dir_preference;
7929 xx = x + test_xy[start_test + i][0];
7930 yy = y + test_xy[start_test + i][1];
7932 if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
7933 (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
7935 new_move_dir = move_dir;
7940 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
7943 move_dir_preference = -1 * RunnerVisit[xx][yy];
7944 if (hunter_mode && PlayerVisit[xx][yy] > 0)
7945 move_dir_preference = PlayerVisit[xx][yy];
7947 if (move_dir_preference > move_preference)
7949 /* prefer field that has not been visited for the longest time */
7950 move_preference = move_dir_preference;
7951 new_move_dir = move_dir;
7953 else if (move_dir_preference == move_preference &&
7954 move_dir == old_move_dir)
7956 /* prefer last direction when all directions are preferred equally */
7957 move_preference = move_dir_preference;
7958 new_move_dir = move_dir;
7962 MovDir[x][y] = new_move_dir;
7963 if (old_move_dir != new_move_dir)
7964 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7968 static void TurnRound(int x, int y)
7970 int direction = MovDir[x][y];
7974 GfxDir[x][y] = MovDir[x][y];
7976 if (direction != MovDir[x][y])
7980 GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
7982 ResetGfxFrame(x, y, FALSE);
7985 static boolean JustBeingPushed(int x, int y)
7989 for (i = 0; i < MAX_PLAYERS; i++)
7991 struct PlayerInfo *player = &stored_player[i];
7993 if (player->active && player->is_pushing && player->MovPos)
7995 int next_jx = player->jx + (player->jx - player->last_jx);
7996 int next_jy = player->jy + (player->jy - player->last_jy);
7998 if (x == next_jx && y == next_jy)
8006 void StartMoving(int x, int y)
8008 boolean started_moving = FALSE; /* some elements can fall _and_ move */
8009 int element = Feld[x][y];
8014 if (MovDelay[x][y] == 0)
8015 GfxAction[x][y] = ACTION_DEFAULT;
8017 if (CAN_FALL(element) && y < lev_fieldy - 1)
8019 if ((x > 0 && IS_PLAYER(x - 1, y)) ||
8020 (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
8021 if (JustBeingPushed(x, y))
8024 if (element == EL_QUICKSAND_FULL)
8026 if (IS_FREE(x, y + 1))
8028 InitMovingField(x, y, MV_DOWN);
8029 started_moving = TRUE;
8031 Feld[x][y] = EL_QUICKSAND_EMPTYING;
8032 #if USE_QUICKSAND_BD_ROCK_BUGFIX
8033 if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
8034 Store[x][y] = EL_ROCK;
8036 Store[x][y] = EL_ROCK;
8039 PlayLevelSoundAction(x, y, ACTION_EMPTYING);
8041 else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
8043 if (!MovDelay[x][y])
8045 MovDelay[x][y] = TILEY + 1;
8047 ResetGfxAnimation(x, y);
8048 ResetGfxAnimation(x, y + 1);
8053 DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
8054 DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
8061 Feld[x][y] = EL_QUICKSAND_EMPTY;
8062 Feld[x][y + 1] = EL_QUICKSAND_FULL;
8063 Store[x][y + 1] = Store[x][y];
8066 PlayLevelSoundAction(x, y, ACTION_FILLING);
8068 else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
8070 if (!MovDelay[x][y])
8072 MovDelay[x][y] = TILEY + 1;
8074 ResetGfxAnimation(x, y);
8075 ResetGfxAnimation(x, y + 1);
8080 DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
8081 DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
8088 Feld[x][y] = EL_QUICKSAND_EMPTY;
8089 Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
8090 Store[x][y + 1] = Store[x][y];
8093 PlayLevelSoundAction(x, y, ACTION_FILLING);
8096 else if (element == EL_QUICKSAND_FAST_FULL)
8098 if (IS_FREE(x, y + 1))
8100 InitMovingField(x, y, MV_DOWN);
8101 started_moving = TRUE;
8103 Feld[x][y] = EL_QUICKSAND_FAST_EMPTYING;
8104 #if USE_QUICKSAND_BD_ROCK_BUGFIX
8105 if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
8106 Store[x][y] = EL_ROCK;
8108 Store[x][y] = EL_ROCK;
8111 PlayLevelSoundAction(x, y, ACTION_EMPTYING);
8113 else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
8115 if (!MovDelay[x][y])
8117 MovDelay[x][y] = TILEY + 1;
8119 ResetGfxAnimation(x, y);
8120 ResetGfxAnimation(x, y + 1);
8125 DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
8126 DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
8133 Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
8134 Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
8135 Store[x][y + 1] = Store[x][y];
8138 PlayLevelSoundAction(x, y, ACTION_FILLING);
8140 else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
8142 if (!MovDelay[x][y])
8144 MovDelay[x][y] = TILEY + 1;
8146 ResetGfxAnimation(x, y);
8147 ResetGfxAnimation(x, y + 1);
8152 DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
8153 DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
8160 Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
8161 Feld[x][y + 1] = EL_QUICKSAND_FULL;
8162 Store[x][y + 1] = Store[x][y];
8165 PlayLevelSoundAction(x, y, ACTION_FILLING);
8168 else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
8169 Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
8171 InitMovingField(x, y, MV_DOWN);
8172 started_moving = TRUE;
8174 Feld[x][y] = EL_QUICKSAND_FILLING;
8175 Store[x][y] = element;
8177 PlayLevelSoundAction(x, y, ACTION_FILLING);
8179 else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
8180 Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
8182 InitMovingField(x, y, MV_DOWN);
8183 started_moving = TRUE;
8185 Feld[x][y] = EL_QUICKSAND_FAST_FILLING;
8186 Store[x][y] = element;
8188 PlayLevelSoundAction(x, y, ACTION_FILLING);
8190 else if (element == EL_MAGIC_WALL_FULL)
8192 if (IS_FREE(x, y + 1))
8194 InitMovingField(x, y, MV_DOWN);
8195 started_moving = TRUE;
8197 Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
8198 Store[x][y] = EL_CHANGED(Store[x][y]);
8200 else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
8202 if (!MovDelay[x][y])
8203 MovDelay[x][y] = TILEY / 4 + 1;
8212 Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
8213 Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
8214 Store[x][y + 1] = EL_CHANGED(Store[x][y]);
8218 else if (element == EL_BD_MAGIC_WALL_FULL)
8220 if (IS_FREE(x, y + 1))
8222 InitMovingField(x, y, MV_DOWN);
8223 started_moving = TRUE;
8225 Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
8226 Store[x][y] = EL_CHANGED_BD(Store[x][y]);
8228 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
8230 if (!MovDelay[x][y])
8231 MovDelay[x][y] = TILEY / 4 + 1;
8240 Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
8241 Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
8242 Store[x][y + 1] = EL_CHANGED_BD(Store[x][y]);
8246 else if (element == EL_DC_MAGIC_WALL_FULL)
8248 if (IS_FREE(x, y + 1))
8250 InitMovingField(x, y, MV_DOWN);
8251 started_moving = TRUE;
8253 Feld[x][y] = EL_DC_MAGIC_WALL_EMPTYING;
8254 Store[x][y] = EL_CHANGED_DC(Store[x][y]);
8256 else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
8258 if (!MovDelay[x][y])
8259 MovDelay[x][y] = TILEY / 4 + 1;
8268 Feld[x][y] = EL_DC_MAGIC_WALL_ACTIVE;
8269 Feld[x][y + 1] = EL_DC_MAGIC_WALL_FULL;
8270 Store[x][y + 1] = EL_CHANGED_DC(Store[x][y]);
8274 else if ((CAN_PASS_MAGIC_WALL(element) &&
8275 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
8276 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)) ||
8277 (CAN_PASS_DC_MAGIC_WALL(element) &&
8278 (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)))
8281 InitMovingField(x, y, MV_DOWN);
8282 started_moving = TRUE;
8285 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
8286 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ? EL_BD_MAGIC_WALL_FILLING :
8287 EL_DC_MAGIC_WALL_FILLING);
8288 Store[x][y] = element;
8290 else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
8292 SplashAcid(x, y + 1);
8294 InitMovingField(x, y, MV_DOWN);
8295 started_moving = TRUE;
8297 Store[x][y] = EL_ACID;
8300 #if USE_FIX_IMPACT_COLLISION
8301 (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
8302 CheckImpact[x][y] && !IS_FREE(x, y + 1)) ||
8304 (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
8305 CheckCollision[x][y] && !IS_FREE(x, y + 1)) ||
8307 (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
8308 CAN_FALL(element) && WasJustFalling[x][y] &&
8309 (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
8311 (game.engine_version < VERSION_IDENT(2,2,0,7) &&
8312 CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
8313 (Feld[x][y + 1] == EL_BLOCKED)))
8315 /* this is needed for a special case not covered by calling "Impact()"
8316 from "ContinueMoving()": if an element moves to a tile directly below
8317 another element which was just falling on that tile (which was empty
8318 in the previous frame), the falling element above would just stop
8319 instead of smashing the element below (in previous version, the above
8320 element was just checked for "moving" instead of "falling", resulting
8321 in incorrect smashes caused by horizontal movement of the above
8322 element; also, the case of the player being the element to smash was
8323 simply not covered here... :-/ ) */
8325 CheckCollision[x][y] = 0;
8326 CheckImpact[x][y] = 0;
8330 else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
8332 if (MovDir[x][y] == MV_NONE)
8334 InitMovingField(x, y, MV_DOWN);
8335 started_moving = TRUE;
8338 else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
8340 if (WasJustFalling[x][y]) /* prevent animation from being restarted */
8341 MovDir[x][y] = MV_DOWN;
8343 InitMovingField(x, y, MV_DOWN);
8344 started_moving = TRUE;
8346 else if (element == EL_AMOEBA_DROP)
8348 Feld[x][y] = EL_AMOEBA_GROWING;
8349 Store[x][y] = EL_AMOEBA_WET;
8351 else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
8352 (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
8353 !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
8354 element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
8356 boolean can_fall_left = (x > 0 && IS_FREE(x - 1, y) &&
8357 (IS_FREE(x - 1, y + 1) ||
8358 Feld[x - 1][y + 1] == EL_ACID));
8359 boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
8360 (IS_FREE(x + 1, y + 1) ||
8361 Feld[x + 1][y + 1] == EL_ACID));
8362 boolean can_fall_any = (can_fall_left || can_fall_right);
8363 boolean can_fall_both = (can_fall_left && can_fall_right);
8364 int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
8366 #if USE_NEW_ALL_SLIPPERY
8367 if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
8369 if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
8370 can_fall_right = FALSE;
8371 else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
8372 can_fall_left = FALSE;
8373 else if (slippery_type == SLIPPERY_ONLY_LEFT)
8374 can_fall_right = FALSE;
8375 else if (slippery_type == SLIPPERY_ONLY_RIGHT)
8376 can_fall_left = FALSE;
8378 can_fall_any = (can_fall_left || can_fall_right);
8379 can_fall_both = FALSE;
8382 if (can_fall_any && IS_CUSTOM_ELEMENT(Feld[x][y + 1]))
8384 if (slippery_type == SLIPPERY_ONLY_LEFT)
8385 can_fall_right = FALSE;
8386 else if (slippery_type == SLIPPERY_ONLY_RIGHT)
8387 can_fall_left = FALSE;
8388 else if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
8389 can_fall_right = FALSE;
8390 else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
8391 can_fall_left = FALSE;
8393 can_fall_any = (can_fall_left || can_fall_right);
8394 can_fall_both = (can_fall_left && can_fall_right);
8398 #if USE_NEW_ALL_SLIPPERY
8400 #if USE_NEW_SP_SLIPPERY
8401 /* !!! better use the same properties as for custom elements here !!! */
8402 else if (game.engine_version >= VERSION_IDENT(3,1,1,0) &&
8403 can_fall_both && IS_SP_ELEMENT(Feld[x][y + 1]))
8405 can_fall_right = FALSE; /* slip down on left side */
8406 can_fall_both = FALSE;
8411 #if USE_NEW_ALL_SLIPPERY
8414 if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
8415 can_fall_right = FALSE; /* slip down on left side */
8417 can_fall_left = !(can_fall_right = RND(2));
8419 can_fall_both = FALSE;
8424 if (game.emulation == EMU_BOULDERDASH ||
8425 element == EL_BD_ROCK || element == EL_BD_DIAMOND)
8426 can_fall_right = FALSE; /* slip down on left side */
8428 can_fall_left = !(can_fall_right = RND(2));
8430 can_fall_both = FALSE;
8436 /* if not determined otherwise, prefer left side for slipping down */
8437 InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
8438 started_moving = TRUE;
8442 else if (IS_BELT_ACTIVE(Feld[x][y + 1]) && !CAN_MOVE(element))
8444 else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
8447 boolean left_is_free = (x > 0 && IS_FREE(x - 1, y));
8448 boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
8449 int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
8450 int belt_dir = game.belt_dir[belt_nr];
8452 if ((belt_dir == MV_LEFT && left_is_free) ||
8453 (belt_dir == MV_RIGHT && right_is_free))
8455 int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
8457 InitMovingField(x, y, belt_dir);
8458 started_moving = TRUE;
8460 Pushed[x][y] = TRUE;
8461 Pushed[nextx][y] = TRUE;
8463 GfxAction[x][y] = ACTION_DEFAULT;
8467 MovDir[x][y] = 0; /* if element was moving, stop it */
8472 /* not "else if" because of elements that can fall and move (EL_SPRING) */
8474 if (CAN_MOVE(element) && !started_moving && MovDir[x][y] != MV_NONE)
8476 if (CAN_MOVE(element) && !started_moving)
8479 int move_pattern = element_info[element].move_pattern;
8484 if (MovDir[x][y] == MV_NONE)
8486 printf("StartMoving(): %d,%d: element %d ['%s'] not moving\n",
8487 x, y, element, element_info[element].token_name);
8488 printf("StartMoving(): This should never happen!\n");
8493 Moving2Blocked(x, y, &newx, &newy);
8495 if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
8498 if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
8499 CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
8501 WasJustMoving[x][y] = 0;
8502 CheckCollision[x][y] = 0;
8504 TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
8506 if (Feld[x][y] != element) /* element has changed */
8510 if (!MovDelay[x][y]) /* start new movement phase */
8512 /* all objects that can change their move direction after each step
8513 (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
8515 if (element != EL_YAMYAM &&
8516 element != EL_DARK_YAMYAM &&
8517 element != EL_PACMAN &&
8518 !(move_pattern & MV_ANY_DIRECTION) &&
8519 move_pattern != MV_TURNING_LEFT &&
8520 move_pattern != MV_TURNING_RIGHT &&
8521 move_pattern != MV_TURNING_LEFT_RIGHT &&
8522 move_pattern != MV_TURNING_RIGHT_LEFT &&
8523 move_pattern != MV_TURNING_RANDOM)
8527 if (MovDelay[x][y] && (element == EL_BUG ||
8528 element == EL_SPACESHIP ||
8529 element == EL_SP_SNIKSNAK ||
8530 element == EL_SP_ELECTRON ||
8531 element == EL_MOLE))
8532 TEST_DrawLevelField(x, y);
8536 if (MovDelay[x][y]) /* wait some time before next movement */
8540 if (element == EL_ROBOT ||
8541 element == EL_YAMYAM ||
8542 element == EL_DARK_YAMYAM)
8544 DrawLevelElementAnimationIfNeeded(x, y, element);
8545 PlayLevelSoundAction(x, y, ACTION_WAITING);
8547 else if (element == EL_SP_ELECTRON)
8548 DrawLevelElementAnimationIfNeeded(x, y, element);
8549 else if (element == EL_DRAGON)
8552 int dir = MovDir[x][y];
8553 int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
8554 int dy = (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
8555 int graphic = (dir == MV_LEFT ? IMG_FLAMES_1_LEFT :
8556 dir == MV_RIGHT ? IMG_FLAMES_1_RIGHT :
8557 dir == MV_UP ? IMG_FLAMES_1_UP :
8558 dir == MV_DOWN ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
8559 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
8561 GfxAction[x][y] = ACTION_ATTACKING;
8563 if (IS_PLAYER(x, y))
8564 DrawPlayerField(x, y);
8566 TEST_DrawLevelField(x, y);
8568 PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
8570 for (i = 1; i <= 3; i++)
8572 int xx = x + i * dx;
8573 int yy = y + i * dy;
8574 int sx = SCREENX(xx);
8575 int sy = SCREENY(yy);
8576 int flame_graphic = graphic + (i - 1);
8578 if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
8583 int flamed = MovingOrBlocked2Element(xx, yy);
8587 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
8589 else if (IS_MOVING(xx, yy) || IS_BLOCKED(xx, yy))
8590 RemoveMovingField(xx, yy);
8592 RemoveField(xx, yy);
8594 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
8597 RemoveMovingField(xx, yy);
8600 ChangeDelay[xx][yy] = 0;
8602 Feld[xx][yy] = EL_FLAMES;
8604 if (IN_SCR_FIELD(sx, sy))
8606 TEST_DrawLevelFieldCrumbled(xx, yy);
8607 DrawGraphic(sx, sy, flame_graphic, frame);
8612 if (Feld[xx][yy] == EL_FLAMES)
8613 Feld[xx][yy] = EL_EMPTY;
8614 TEST_DrawLevelField(xx, yy);
8619 if (MovDelay[x][y]) /* element still has to wait some time */
8621 PlayLevelSoundAction(x, y, ACTION_WAITING);
8627 /* now make next step */
8629 Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
8631 if (DONT_COLLIDE_WITH(element) &&
8632 IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
8633 !PLAYER_ENEMY_PROTECTED(newx, newy))
8635 TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
8640 else if (CAN_MOVE_INTO_ACID(element) &&
8641 IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID &&
8642 !IS_MV_DIAGONAL(MovDir[x][y]) &&
8643 (MovDir[x][y] == MV_DOWN ||
8644 game.engine_version >= VERSION_IDENT(3,1,0,0)))
8646 SplashAcid(newx, newy);
8647 Store[x][y] = EL_ACID;
8649 else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
8651 if (Feld[newx][newy] == EL_EXIT_OPEN ||
8652 Feld[newx][newy] == EL_EM_EXIT_OPEN ||
8653 Feld[newx][newy] == EL_STEEL_EXIT_OPEN ||
8654 Feld[newx][newy] == EL_EM_STEEL_EXIT_OPEN)
8657 TEST_DrawLevelField(x, y);
8659 PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
8660 if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
8661 DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
8663 local_player->friends_still_needed--;
8664 if (!local_player->friends_still_needed &&
8665 !local_player->GameOver && AllPlayersGone)
8666 PlayerWins(local_player);
8670 else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
8672 if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MP_MOVING)
8673 TEST_DrawLevelField(newx, newy);
8675 GfxDir[x][y] = MovDir[x][y] = MV_NONE;
8677 else if (!IS_FREE(newx, newy))
8679 GfxAction[x][y] = ACTION_WAITING;
8681 if (IS_PLAYER(x, y))
8682 DrawPlayerField(x, y);
8684 TEST_DrawLevelField(x, y);
8689 else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
8691 if (IS_FOOD_PIG(Feld[newx][newy]))
8693 if (IS_MOVING(newx, newy))
8694 RemoveMovingField(newx, newy);
8697 Feld[newx][newy] = EL_EMPTY;
8698 TEST_DrawLevelField(newx, newy);
8701 PlayLevelSound(x, y, SND_PIG_DIGGING);
8703 else if (!IS_FREE(newx, newy))
8705 if (IS_PLAYER(x, y))
8706 DrawPlayerField(x, y);
8708 TEST_DrawLevelField(x, y);
8713 else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
8715 if (Store[x][y] != EL_EMPTY)
8717 boolean can_clone = FALSE;
8720 /* check if element to clone is still there */
8721 for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
8723 if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == Store[x][y])
8731 /* cannot clone or target field not free anymore -- do not clone */
8732 if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8733 Store[x][y] = EL_EMPTY;
8736 if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8738 if (IS_MV_DIAGONAL(MovDir[x][y]))
8740 int diagonal_move_dir = MovDir[x][y];
8741 int stored = Store[x][y];
8742 int change_delay = 8;
8745 /* android is moving diagonally */
8747 CreateField(x, y, EL_DIAGONAL_SHRINKING);
8749 Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
8750 GfxElement[x][y] = EL_EMC_ANDROID;
8751 GfxAction[x][y] = ACTION_SHRINKING;
8752 GfxDir[x][y] = diagonal_move_dir;
8753 ChangeDelay[x][y] = change_delay;
8755 graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
8758 DrawLevelGraphicAnimation(x, y, graphic);
8759 PlayLevelSoundAction(x, y, ACTION_SHRINKING);
8761 if (Feld[newx][newy] == EL_ACID)
8763 SplashAcid(newx, newy);
8768 CreateField(newx, newy, EL_DIAGONAL_GROWING);
8770 Store[newx][newy] = EL_EMC_ANDROID;
8771 GfxElement[newx][newy] = EL_EMC_ANDROID;
8772 GfxAction[newx][newy] = ACTION_GROWING;
8773 GfxDir[newx][newy] = diagonal_move_dir;
8774 ChangeDelay[newx][newy] = change_delay;
8776 graphic = el_act_dir2img(GfxElement[newx][newy],
8777 GfxAction[newx][newy], GfxDir[newx][newy]);
8779 DrawLevelGraphicAnimation(newx, newy, graphic);
8780 PlayLevelSoundAction(newx, newy, ACTION_GROWING);
8786 Feld[newx][newy] = EL_EMPTY;
8787 TEST_DrawLevelField(newx, newy);
8789 PlayLevelSoundAction(x, y, ACTION_DIGGING);
8792 else if (!IS_FREE(newx, newy))
8795 if (IS_PLAYER(x, y))
8796 DrawPlayerField(x, y);
8798 TEST_DrawLevelField(x, y);
8804 else if (IS_CUSTOM_ELEMENT(element) &&
8805 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
8808 if (!DigFieldByCE(newx, newy, element))
8811 int new_element = Feld[newx][newy];
8813 if (!IS_FREE(newx, newy))
8815 int action = (IS_DIGGABLE(new_element) ? ACTION_DIGGING :
8816 IS_COLLECTIBLE(new_element) ? ACTION_COLLECTING :
8819 /* no element can dig solid indestructible elements */
8820 if (IS_INDESTRUCTIBLE(new_element) &&
8821 !IS_DIGGABLE(new_element) &&
8822 !IS_COLLECTIBLE(new_element))
8825 if (AmoebaNr[newx][newy] &&
8826 (new_element == EL_AMOEBA_FULL ||
8827 new_element == EL_BD_AMOEBA ||
8828 new_element == EL_AMOEBA_GROWING))
8830 AmoebaCnt[AmoebaNr[newx][newy]]--;
8831 AmoebaCnt2[AmoebaNr[newx][newy]]--;
8834 if (IS_MOVING(newx, newy))
8835 RemoveMovingField(newx, newy);
8838 RemoveField(newx, newy);
8839 TEST_DrawLevelField(newx, newy);
8842 /* if digged element was about to explode, prevent the explosion */
8843 ExplodeField[newx][newy] = EX_TYPE_NONE;
8845 PlayLevelSoundAction(x, y, action);
8848 Store[newx][newy] = EL_EMPTY;
8851 /* this makes it possible to leave the removed element again */
8852 if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
8853 Store[newx][newy] = new_element;
8855 if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
8857 int move_leave_element = element_info[element].move_leave_element;
8859 /* this makes it possible to leave the removed element again */
8860 Store[newx][newy] = (move_leave_element == EL_TRIGGER_ELEMENT ?
8861 new_element : move_leave_element);
8867 if (move_pattern & MV_MAZE_RUNNER_STYLE)
8869 RunnerVisit[x][y] = FrameCounter;
8870 PlayerVisit[x][y] /= 8; /* expire player visit path */
8873 else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
8875 if (!IS_FREE(newx, newy))
8877 if (IS_PLAYER(x, y))
8878 DrawPlayerField(x, y);
8880 TEST_DrawLevelField(x, y);
8886 boolean wanna_flame = !RND(10);
8887 int dx = newx - x, dy = newy - y;
8888 int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
8889 int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
8890 int element1 = (IN_LEV_FIELD(newx1, newy1) ?
8891 MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
8892 int element2 = (IN_LEV_FIELD(newx2, newy2) ?
8893 MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
8896 IS_CLASSIC_ENEMY(element1) ||
8897 IS_CLASSIC_ENEMY(element2)) &&
8898 element1 != EL_DRAGON && element2 != EL_DRAGON &&
8899 element1 != EL_FLAMES && element2 != EL_FLAMES)
8901 ResetGfxAnimation(x, y);
8902 GfxAction[x][y] = ACTION_ATTACKING;
8904 if (IS_PLAYER(x, y))
8905 DrawPlayerField(x, y);
8907 TEST_DrawLevelField(x, y);
8909 PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
8911 MovDelay[x][y] = 50;
8915 RemoveField(newx, newy);
8917 Feld[newx][newy] = EL_FLAMES;
8918 if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
8921 RemoveField(newx1, newy1);
8923 Feld[newx1][newy1] = EL_FLAMES;
8925 if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
8928 RemoveField(newx2, newy2);
8930 Feld[newx2][newy2] = EL_FLAMES;
8937 else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8938 Feld[newx][newy] == EL_DIAMOND)
8940 if (IS_MOVING(newx, newy))
8941 RemoveMovingField(newx, newy);
8944 Feld[newx][newy] = EL_EMPTY;
8945 TEST_DrawLevelField(newx, newy);
8948 PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
8950 else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8951 IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
8953 if (AmoebaNr[newx][newy])
8955 AmoebaCnt2[AmoebaNr[newx][newy]]--;
8956 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
8957 Feld[newx][newy] == EL_BD_AMOEBA)
8958 AmoebaCnt[AmoebaNr[newx][newy]]--;
8963 if (IS_MOVING(newx, newy) || IS_BLOCKED(newx, newy))
8965 RemoveMovingField(newx, newy);
8968 if (IS_MOVING(newx, newy))
8970 RemoveMovingField(newx, newy);
8975 Feld[newx][newy] = EL_EMPTY;
8976 TEST_DrawLevelField(newx, newy);
8979 PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
8981 else if ((element == EL_PACMAN || element == EL_MOLE)
8982 && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
8984 if (AmoebaNr[newx][newy])
8986 AmoebaCnt2[AmoebaNr[newx][newy]]--;
8987 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
8988 Feld[newx][newy] == EL_BD_AMOEBA)
8989 AmoebaCnt[AmoebaNr[newx][newy]]--;
8992 if (element == EL_MOLE)
8994 Feld[newx][newy] = EL_AMOEBA_SHRINKING;
8995 PlayLevelSound(x, y, SND_MOLE_DIGGING);
8997 ResetGfxAnimation(x, y);
8998 GfxAction[x][y] = ACTION_DIGGING;
8999 TEST_DrawLevelField(x, y);
9001 MovDelay[newx][newy] = 0; /* start amoeba shrinking delay */
9003 return; /* wait for shrinking amoeba */
9005 else /* element == EL_PACMAN */
9007 Feld[newx][newy] = EL_EMPTY;
9008 TEST_DrawLevelField(newx, newy);
9009 PlayLevelSound(x, y, SND_PACMAN_DIGGING);
9012 else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
9013 (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
9014 (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
9016 /* wait for shrinking amoeba to completely disappear */
9019 else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
9021 /* object was running against a wall */
9026 /* !!! NEW "CE_BLOCKED" STUFF !!! -- DOES NOT WORK YET... !!! */
9027 if (move_pattern & MV_ANY_DIRECTION &&
9028 move_pattern == MovDir[x][y])
9030 int blocking_element =
9031 (IN_LEV_FIELD(newx, newy) ? Feld[newx][newy] : BorderElement);
9033 CheckElementChangeBySide(x, y, element, blocking_element, CE_BLOCKED,
9036 element = Feld[x][y]; /* element might have changed */
9040 if (GFX_ELEMENT(element) != EL_SAND) /* !!! FIX THIS (crumble) !!! */
9041 DrawLevelElementAnimation(x, y, element);
9043 if (DONT_TOUCH(element))
9044 TestIfBadThingTouchesPlayer(x, y);
9049 InitMovingField(x, y, MovDir[x][y]);
9051 PlayLevelSoundAction(x, y, ACTION_MOVING);
9055 ContinueMoving(x, y);
9058 void ContinueMoving(int x, int y)
9060 int element = Feld[x][y];
9061 struct ElementInfo *ei = &element_info[element];
9062 int direction = MovDir[x][y];
9063 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
9064 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
9065 int newx = x + dx, newy = y + dy;
9066 int stored = Store[x][y];
9067 int stored_new = Store[newx][newy];
9068 boolean pushed_by_player = (Pushed[x][y] && IS_PLAYER(x, y));
9069 boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
9070 boolean last_line = (newy == lev_fieldy - 1);
9072 MovPos[x][y] += getElementMoveStepsize(x, y);
9074 if (pushed_by_player) /* special case: moving object pushed by player */
9075 MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
9077 if (ABS(MovPos[x][y]) < TILEX)
9080 int ee = Feld[x][y];
9081 int gg = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
9082 int ff = getGraphicAnimationFrame(gg, GfxFrame[x][y]);
9084 printf("::: %d.%d: moving %d ... [%d, %d, %d] [%d, %d, %d]\n",
9085 x, y, ABS(MovPos[x][y]),
9087 GfxAction[x][y], GfxDir[x][y], GfxFrame[x][y]);
9090 TEST_DrawLevelField(x, y);
9092 return; /* element is still moving */
9095 /* element reached destination field */
9097 Feld[x][y] = EL_EMPTY;
9098 Feld[newx][newy] = element;
9099 MovPos[x][y] = 0; /* force "not moving" for "crumbled sand" */
9101 if (Store[x][y] == EL_ACID) /* element is moving into acid pool */
9103 element = Feld[newx][newy] = EL_ACID;
9105 else if (element == EL_MOLE)
9107 Feld[x][y] = EL_SAND;
9109 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
9111 else if (element == EL_QUICKSAND_FILLING)
9113 element = Feld[newx][newy] = get_next_element(element);
9114 Store[newx][newy] = Store[x][y];
9116 else if (element == EL_QUICKSAND_EMPTYING)
9118 Feld[x][y] = get_next_element(element);
9119 element = Feld[newx][newy] = Store[x][y];
9121 else if (element == EL_QUICKSAND_FAST_FILLING)
9123 element = Feld[newx][newy] = get_next_element(element);
9124 Store[newx][newy] = Store[x][y];
9126 else if (element == EL_QUICKSAND_FAST_EMPTYING)
9128 Feld[x][y] = get_next_element(element);
9129 element = Feld[newx][newy] = Store[x][y];
9131 else if (element == EL_MAGIC_WALL_FILLING)
9133 element = Feld[newx][newy] = get_next_element(element);
9134 if (!game.magic_wall_active)
9135 element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
9136 Store[newx][newy] = Store[x][y];
9138 else if (element == EL_MAGIC_WALL_EMPTYING)
9140 Feld[x][y] = get_next_element(element);
9141 if (!game.magic_wall_active)
9142 Feld[x][y] = EL_MAGIC_WALL_DEAD;
9143 element = Feld[newx][newy] = Store[x][y];
9145 #if USE_NEW_CUSTOM_VALUE
9146 InitField(newx, newy, FALSE);
9149 else if (element == EL_BD_MAGIC_WALL_FILLING)
9151 element = Feld[newx][newy] = get_next_element(element);
9152 if (!game.magic_wall_active)
9153 element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
9154 Store[newx][newy] = Store[x][y];
9156 else if (element == EL_BD_MAGIC_WALL_EMPTYING)
9158 Feld[x][y] = get_next_element(element);
9159 if (!game.magic_wall_active)
9160 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
9161 element = Feld[newx][newy] = Store[x][y];
9163 #if USE_NEW_CUSTOM_VALUE
9164 InitField(newx, newy, FALSE);
9167 else if (element == EL_DC_MAGIC_WALL_FILLING)
9169 element = Feld[newx][newy] = get_next_element(element);
9170 if (!game.magic_wall_active)
9171 element = Feld[newx][newy] = EL_DC_MAGIC_WALL_DEAD;
9172 Store[newx][newy] = Store[x][y];
9174 else if (element == EL_DC_MAGIC_WALL_EMPTYING)
9176 Feld[x][y] = get_next_element(element);
9177 if (!game.magic_wall_active)
9178 Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
9179 element = Feld[newx][newy] = Store[x][y];
9181 #if USE_NEW_CUSTOM_VALUE
9182 InitField(newx, newy, FALSE);
9185 else if (element == EL_AMOEBA_DROPPING)
9187 Feld[x][y] = get_next_element(element);
9188 element = Feld[newx][newy] = Store[x][y];
9190 else if (element == EL_SOKOBAN_OBJECT)
9193 Feld[x][y] = Back[x][y];
9195 if (Back[newx][newy])
9196 Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
9198 Back[x][y] = Back[newx][newy] = 0;
9201 Store[x][y] = EL_EMPTY;
9206 MovDelay[newx][newy] = 0;
9208 if (CAN_CHANGE_OR_HAS_ACTION(element))
9210 /* copy element change control values to new field */
9211 ChangeDelay[newx][newy] = ChangeDelay[x][y];
9212 ChangePage[newx][newy] = ChangePage[x][y];
9213 ChangeCount[newx][newy] = ChangeCount[x][y];
9214 ChangeEvent[newx][newy] = ChangeEvent[x][y];
9217 #if USE_NEW_CUSTOM_VALUE
9218 CustomValue[newx][newy] = CustomValue[x][y];
9221 ChangeDelay[x][y] = 0;
9222 ChangePage[x][y] = -1;
9223 ChangeCount[x][y] = 0;
9224 ChangeEvent[x][y] = -1;
9226 #if USE_NEW_CUSTOM_VALUE
9227 CustomValue[x][y] = 0;
9230 /* copy animation control values to new field */
9231 GfxFrame[newx][newy] = GfxFrame[x][y];
9232 GfxRandom[newx][newy] = GfxRandom[x][y]; /* keep same random value */
9233 GfxAction[newx][newy] = GfxAction[x][y]; /* keep action one frame */
9234 GfxDir[newx][newy] = GfxDir[x][y]; /* keep element direction */
9236 Pushed[x][y] = Pushed[newx][newy] = FALSE;
9238 /* some elements can leave other elements behind after moving */
9240 if (ei->move_leave_element != EL_EMPTY &&
9241 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
9242 (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
9244 if (IS_CUSTOM_ELEMENT(element) && ei->move_leave_element != EL_EMPTY &&
9245 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
9246 (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
9249 int move_leave_element = ei->move_leave_element;
9253 /* this makes it possible to leave the removed element again */
9254 if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
9255 move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
9257 /* this makes it possible to leave the removed element again */
9258 if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
9259 move_leave_element = stored;
9262 /* this makes it possible to leave the removed element again */
9263 if (ei->move_leave_type == LEAVE_TYPE_LIMITED &&
9264 ei->move_leave_element == EL_TRIGGER_ELEMENT)
9265 move_leave_element = stored;
9268 Feld[x][y] = move_leave_element;
9270 if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
9271 MovDir[x][y] = direction;
9273 InitField(x, y, FALSE);
9275 if (GFX_CRUMBLED(Feld[x][y]))
9276 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
9278 if (ELEM_IS_PLAYER(move_leave_element))
9279 RelocatePlayer(x, y, move_leave_element);
9282 /* do this after checking for left-behind element */
9283 ResetGfxAnimation(x, y); /* reset animation values for old field */
9285 if (!CAN_MOVE(element) ||
9286 (CAN_FALL(element) && direction == MV_DOWN &&
9287 (element == EL_SPRING ||
9288 element_info[element].move_pattern == MV_WHEN_PUSHED ||
9289 element_info[element].move_pattern == MV_WHEN_DROPPED)))
9290 GfxDir[x][y] = MovDir[newx][newy] = 0;
9292 TEST_DrawLevelField(x, y);
9293 TEST_DrawLevelField(newx, newy);
9295 Stop[newx][newy] = TRUE; /* ignore this element until the next frame */
9297 /* prevent pushed element from moving on in pushed direction */
9298 if (pushed_by_player && CAN_MOVE(element) &&
9299 element_info[element].move_pattern & MV_ANY_DIRECTION &&
9300 !(element_info[element].move_pattern & direction))
9301 TurnRound(newx, newy);
9303 /* prevent elements on conveyor belt from moving on in last direction */
9304 if (pushed_by_conveyor && CAN_FALL(element) &&
9305 direction & MV_HORIZONTAL)
9306 MovDir[newx][newy] = 0;
9308 if (!pushed_by_player)
9310 int nextx = newx + dx, nexty = newy + dy;
9311 boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
9313 WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
9315 if (CAN_FALL(element) && direction == MV_DOWN)
9316 WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
9318 if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
9319 CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
9321 #if USE_FIX_IMPACT_COLLISION
9322 if (CAN_FALL(element) && direction == MV_DOWN && check_collision_again)
9323 CheckImpact[newx][newy] = CHECK_DELAY_IMPACT;
9327 if (DONT_TOUCH(element)) /* object may be nasty to player or others */
9329 TestIfBadThingTouchesPlayer(newx, newy);
9330 TestIfBadThingTouchesFriend(newx, newy);
9332 if (!IS_CUSTOM_ELEMENT(element))
9333 TestIfBadThingTouchesOtherBadThing(newx, newy);
9335 else if (element == EL_PENGUIN)
9336 TestIfFriendTouchesBadThing(newx, newy);
9338 if (DONT_GET_HIT_BY(element))
9340 TestIfGoodThingGetsHitByBadThing(newx, newy, direction);
9343 /* give the player one last chance (one more frame) to move away */
9344 if (CAN_FALL(element) && direction == MV_DOWN &&
9345 (last_line || (!IS_FREE(x, newy + 1) &&
9346 (!IS_PLAYER(x, newy + 1) ||
9347 game.engine_version < VERSION_IDENT(3,1,1,0)))))
9350 if (pushed_by_player && !game.use_change_when_pushing_bug)
9352 int push_side = MV_DIR_OPPOSITE(direction);
9353 struct PlayerInfo *player = PLAYERINFO(x, y);
9355 CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
9356 player->index_bit, push_side);
9357 CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
9358 player->index_bit, push_side);
9361 if (element == EL_EMC_ANDROID && pushed_by_player) /* make another move */
9362 MovDelay[newx][newy] = 1;
9364 CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
9366 TestIfElementTouchesCustomElement(x, y); /* empty or new element */
9369 if (ChangePage[newx][newy] != -1) /* delayed change */
9371 int page = ChangePage[newx][newy];
9372 struct ElementChangeInfo *change = &ei->change_page[page];
9374 ChangePage[newx][newy] = -1;
9376 if (change->can_change)
9378 if (ChangeElement(newx, newy, element, page))
9380 if (change->post_change_function)
9381 change->post_change_function(newx, newy);
9385 if (change->has_action)
9386 ExecuteCustomElementAction(newx, newy, element, page);
9390 TestIfElementHitsCustomElement(newx, newy, direction);
9391 TestIfPlayerTouchesCustomElement(newx, newy);
9392 TestIfElementTouchesCustomElement(newx, newy);
9394 if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
9395 IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
9396 CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
9397 MV_DIR_OPPOSITE(direction));
9400 int AmoebeNachbarNr(int ax, int ay)
9403 int element = Feld[ax][ay];
9405 static int xy[4][2] =
9413 for (i = 0; i < NUM_DIRECTIONS; i++)
9415 int x = ax + xy[i][0];
9416 int y = ay + xy[i][1];
9418 if (!IN_LEV_FIELD(x, y))
9421 if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
9422 group_nr = AmoebaNr[x][y];
9428 void AmoebenVereinigen(int ax, int ay)
9430 int i, x, y, xx, yy;
9431 int new_group_nr = AmoebaNr[ax][ay];
9432 static int xy[4][2] =
9440 if (new_group_nr == 0)
9443 for (i = 0; i < NUM_DIRECTIONS; i++)
9448 if (!IN_LEV_FIELD(x, y))
9451 if ((Feld[x][y] == EL_AMOEBA_FULL ||
9452 Feld[x][y] == EL_BD_AMOEBA ||
9453 Feld[x][y] == EL_AMOEBA_DEAD) &&
9454 AmoebaNr[x][y] != new_group_nr)
9456 int old_group_nr = AmoebaNr[x][y];
9458 if (old_group_nr == 0)
9461 AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
9462 AmoebaCnt[old_group_nr] = 0;
9463 AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
9464 AmoebaCnt2[old_group_nr] = 0;
9466 SCAN_PLAYFIELD(xx, yy)
9468 if (AmoebaNr[xx][yy] == old_group_nr)
9469 AmoebaNr[xx][yy] = new_group_nr;
9475 void AmoebeUmwandeln(int ax, int ay)
9479 if (Feld[ax][ay] == EL_AMOEBA_DEAD)
9481 int group_nr = AmoebaNr[ax][ay];
9486 printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
9487 printf("AmoebeUmwandeln(): This should never happen!\n");
9492 SCAN_PLAYFIELD(x, y)
9494 if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
9497 Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
9501 PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
9502 SND_AMOEBA_TURNING_TO_GEM :
9503 SND_AMOEBA_TURNING_TO_ROCK));
9508 static int xy[4][2] =
9516 for (i = 0; i < NUM_DIRECTIONS; i++)
9521 if (!IN_LEV_FIELD(x, y))
9524 if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
9526 PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
9527 SND_AMOEBA_TURNING_TO_GEM :
9528 SND_AMOEBA_TURNING_TO_ROCK));
9535 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
9538 int group_nr = AmoebaNr[ax][ay];
9539 boolean done = FALSE;
9544 printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
9545 printf("AmoebeUmwandelnBD(): This should never happen!\n");
9550 SCAN_PLAYFIELD(x, y)
9552 if (AmoebaNr[x][y] == group_nr &&
9553 (Feld[x][y] == EL_AMOEBA_DEAD ||
9554 Feld[x][y] == EL_BD_AMOEBA ||
9555 Feld[x][y] == EL_AMOEBA_GROWING))
9558 Feld[x][y] = new_element;
9559 InitField(x, y, FALSE);
9560 TEST_DrawLevelField(x, y);
9566 PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
9567 SND_BD_AMOEBA_TURNING_TO_ROCK :
9568 SND_BD_AMOEBA_TURNING_TO_GEM));
9571 void AmoebeWaechst(int x, int y)
9573 static unsigned int sound_delay = 0;
9574 static unsigned int sound_delay_value = 0;
9576 if (!MovDelay[x][y]) /* start new growing cycle */
9580 if (DelayReached(&sound_delay, sound_delay_value))
9582 PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
9583 sound_delay_value = 30;
9587 if (MovDelay[x][y]) /* wait some time before growing bigger */
9590 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9592 int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
9593 6 - MovDelay[x][y]);
9595 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
9598 if (!MovDelay[x][y])
9600 Feld[x][y] = Store[x][y];
9602 TEST_DrawLevelField(x, y);
9607 void AmoebaDisappearing(int x, int y)
9609 static unsigned int sound_delay = 0;
9610 static unsigned int sound_delay_value = 0;
9612 if (!MovDelay[x][y]) /* start new shrinking cycle */
9616 if (DelayReached(&sound_delay, sound_delay_value))
9617 sound_delay_value = 30;
9620 if (MovDelay[x][y]) /* wait some time before shrinking */
9623 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9625 int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
9626 6 - MovDelay[x][y]);
9628 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
9631 if (!MovDelay[x][y])
9633 Feld[x][y] = EL_EMPTY;
9634 TEST_DrawLevelField(x, y);
9636 /* don't let mole enter this field in this cycle;
9637 (give priority to objects falling to this field from above) */
9643 void AmoebeAbleger(int ax, int ay)
9646 int element = Feld[ax][ay];
9647 int graphic = el2img(element);
9648 int newax = ax, neway = ay;
9649 boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
9650 static int xy[4][2] =
9658 if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
9660 Feld[ax][ay] = EL_AMOEBA_DEAD;
9661 TEST_DrawLevelField(ax, ay);
9665 if (IS_ANIMATED(graphic))
9666 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9668 if (!MovDelay[ax][ay]) /* start making new amoeba field */
9669 MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
9671 if (MovDelay[ax][ay]) /* wait some time before making new amoeba */
9674 if (MovDelay[ax][ay])
9678 if (can_drop) /* EL_AMOEBA_WET or EL_EMC_DRIPPER */
9681 int x = ax + xy[start][0];
9682 int y = ay + xy[start][1];
9684 if (!IN_LEV_FIELD(x, y))
9687 if (IS_FREE(x, y) ||
9688 CAN_GROW_INTO(Feld[x][y]) ||
9689 Feld[x][y] == EL_QUICKSAND_EMPTY ||
9690 Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
9696 if (newax == ax && neway == ay)
9699 else /* normal or "filled" (BD style) amoeba */
9702 boolean waiting_for_player = FALSE;
9704 for (i = 0; i < NUM_DIRECTIONS; i++)
9706 int j = (start + i) % 4;
9707 int x = ax + xy[j][0];
9708 int y = ay + xy[j][1];
9710 if (!IN_LEV_FIELD(x, y))
9713 if (IS_FREE(x, y) ||
9714 CAN_GROW_INTO(Feld[x][y]) ||
9715 Feld[x][y] == EL_QUICKSAND_EMPTY ||
9716 Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
9722 else if (IS_PLAYER(x, y))
9723 waiting_for_player = TRUE;
9726 if (newax == ax && neway == ay) /* amoeba cannot grow */
9728 if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
9730 Feld[ax][ay] = EL_AMOEBA_DEAD;
9731 TEST_DrawLevelField(ax, ay);
9732 AmoebaCnt[AmoebaNr[ax][ay]]--;
9734 if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0) /* amoeba is completely dead */
9736 if (element == EL_AMOEBA_FULL)
9737 AmoebeUmwandeln(ax, ay);
9738 else if (element == EL_BD_AMOEBA)
9739 AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
9744 else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
9746 /* amoeba gets larger by growing in some direction */
9748 int new_group_nr = AmoebaNr[ax][ay];
9751 if (new_group_nr == 0)
9753 printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
9754 printf("AmoebeAbleger(): This should never happen!\n");
9759 AmoebaNr[newax][neway] = new_group_nr;
9760 AmoebaCnt[new_group_nr]++;
9761 AmoebaCnt2[new_group_nr]++;
9763 /* if amoeba touches other amoeba(s) after growing, unify them */
9764 AmoebenVereinigen(newax, neway);
9766 if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
9768 AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
9774 if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
9775 (neway == lev_fieldy - 1 && newax != ax))
9777 Feld[newax][neway] = EL_AMOEBA_GROWING; /* creation of new amoeba */
9778 Store[newax][neway] = element;
9780 else if (neway == ay || element == EL_EMC_DRIPPER)
9782 Feld[newax][neway] = EL_AMOEBA_DROP; /* drop left/right of amoeba */
9784 PlayLevelSoundAction(newax, neway, ACTION_GROWING);
9788 InitMovingField(ax, ay, MV_DOWN); /* drop dripping from amoeba */
9789 Feld[ax][ay] = EL_AMOEBA_DROPPING;
9790 Store[ax][ay] = EL_AMOEBA_DROP;
9791 ContinueMoving(ax, ay);
9795 TEST_DrawLevelField(newax, neway);
9798 void Life(int ax, int ay)
9802 int element = Feld[ax][ay];
9803 int graphic = el2img(element);
9804 int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
9806 boolean changed = FALSE;
9808 if (IS_ANIMATED(graphic))
9809 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9814 if (!MovDelay[ax][ay]) /* start new "game of life" cycle */
9815 MovDelay[ax][ay] = life_time;
9817 if (MovDelay[ax][ay]) /* wait some time before next cycle */
9820 if (MovDelay[ax][ay])
9824 for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
9826 int xx = ax+x1, yy = ay+y1;
9829 if (!IN_LEV_FIELD(xx, yy))
9832 for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
9834 int x = xx+x2, y = yy+y2;
9836 if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
9839 if (((Feld[x][y] == element ||
9840 (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
9842 (IS_FREE(x, y) && Stop[x][y]))
9846 if (xx == ax && yy == ay) /* field in the middle */
9848 if (nachbarn < life_parameter[0] ||
9849 nachbarn > life_parameter[1])
9851 Feld[xx][yy] = EL_EMPTY;
9853 TEST_DrawLevelField(xx, yy);
9854 Stop[xx][yy] = TRUE;
9858 else if (IS_FREE(xx, yy) || CAN_GROW_INTO(Feld[xx][yy]))
9859 { /* free border field */
9860 if (nachbarn >= life_parameter[2] &&
9861 nachbarn <= life_parameter[3])
9863 Feld[xx][yy] = element;
9864 MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
9866 TEST_DrawLevelField(xx, yy);
9867 Stop[xx][yy] = TRUE;
9874 PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
9875 SND_GAME_OF_LIFE_GROWING);
9878 static void InitRobotWheel(int x, int y)
9880 ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
9883 static void RunRobotWheel(int x, int y)
9885 PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
9888 static void StopRobotWheel(int x, int y)
9890 if (ZX == x && ZY == y)
9894 game.robot_wheel_active = FALSE;
9898 static void InitTimegateWheel(int x, int y)
9900 ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
9903 static void RunTimegateWheel(int x, int y)
9905 PlayLevelSound(x, y, SND_CLASS_TIMEGATE_SWITCH_ACTIVE);
9908 static void InitMagicBallDelay(int x, int y)
9911 ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
9913 ChangeDelay[x][y] = level.ball_time * FRAMES_PER_SECOND + 1;
9917 static void ActivateMagicBall(int bx, int by)
9921 if (level.ball_random)
9923 int pos_border = RND(8); /* select one of the eight border elements */
9924 int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
9925 int xx = pos_content % 3;
9926 int yy = pos_content / 3;
9931 if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
9932 CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9936 for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
9938 int xx = x - bx + 1;
9939 int yy = y - by + 1;
9941 if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
9942 CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9946 game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
9949 void CheckExit(int x, int y)
9951 if (local_player->gems_still_needed > 0 ||
9952 local_player->sokobanfields_still_needed > 0 ||
9953 local_player->lights_still_needed > 0)
9955 int element = Feld[x][y];
9956 int graphic = el2img(element);
9958 if (IS_ANIMATED(graphic))
9959 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9964 if (AllPlayersGone) /* do not re-open exit door closed after last player */
9967 Feld[x][y] = EL_EXIT_OPENING;
9969 PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
9972 void CheckExitEM(int x, int y)
9974 if (local_player->gems_still_needed > 0 ||
9975 local_player->sokobanfields_still_needed > 0 ||
9976 local_player->lights_still_needed > 0)
9978 int element = Feld[x][y];
9979 int graphic = el2img(element);
9981 if (IS_ANIMATED(graphic))
9982 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9987 if (AllPlayersGone) /* do not re-open exit door closed after last player */
9990 Feld[x][y] = EL_EM_EXIT_OPENING;
9992 PlayLevelSoundNearest(x, y, SND_CLASS_EM_EXIT_OPENING);
9995 void CheckExitSteel(int x, int y)
9997 if (local_player->gems_still_needed > 0 ||
9998 local_player->sokobanfields_still_needed > 0 ||
9999 local_player->lights_still_needed > 0)
10001 int element = Feld[x][y];
10002 int graphic = el2img(element);
10004 if (IS_ANIMATED(graphic))
10005 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10010 if (AllPlayersGone) /* do not re-open exit door closed after last player */
10013 Feld[x][y] = EL_STEEL_EXIT_OPENING;
10015 PlayLevelSoundNearest(x, y, SND_CLASS_STEEL_EXIT_OPENING);
10018 void CheckExitSteelEM(int x, int y)
10020 if (local_player->gems_still_needed > 0 ||
10021 local_player->sokobanfields_still_needed > 0 ||
10022 local_player->lights_still_needed > 0)
10024 int element = Feld[x][y];
10025 int graphic = el2img(element);
10027 if (IS_ANIMATED(graphic))
10028 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10033 if (AllPlayersGone) /* do not re-open exit door closed after last player */
10036 Feld[x][y] = EL_EM_STEEL_EXIT_OPENING;
10038 PlayLevelSoundNearest(x, y, SND_CLASS_EM_STEEL_EXIT_OPENING);
10041 void CheckExitSP(int x, int y)
10043 if (local_player->gems_still_needed > 0)
10045 int element = Feld[x][y];
10046 int graphic = el2img(element);
10048 if (IS_ANIMATED(graphic))
10049 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10054 if (AllPlayersGone) /* do not re-open exit door closed after last player */
10057 Feld[x][y] = EL_SP_EXIT_OPENING;
10059 PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
10062 static void CloseAllOpenTimegates()
10066 SCAN_PLAYFIELD(x, y)
10068 int element = Feld[x][y];
10070 if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
10072 Feld[x][y] = EL_TIMEGATE_CLOSING;
10074 PlayLevelSoundAction(x, y, ACTION_CLOSING);
10079 void DrawTwinkleOnField(int x, int y)
10081 if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
10084 if (Feld[x][y] == EL_BD_DIAMOND)
10087 if (MovDelay[x][y] == 0) /* next animation frame */
10088 MovDelay[x][y] = 11 * !GetSimpleRandom(500);
10090 if (MovDelay[x][y] != 0) /* wait some time before next frame */
10094 DrawLevelElementAnimation(x, y, Feld[x][y]);
10096 if (MovDelay[x][y] != 0)
10098 int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
10099 10 - MovDelay[x][y]);
10101 DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
10106 void MauerWaechst(int x, int y)
10110 if (!MovDelay[x][y]) /* next animation frame */
10111 MovDelay[x][y] = 3 * delay;
10113 if (MovDelay[x][y]) /* wait some time before next frame */
10117 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
10119 int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
10120 int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
10122 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
10125 if (!MovDelay[x][y])
10127 if (MovDir[x][y] == MV_LEFT)
10129 if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
10130 TEST_DrawLevelField(x - 1, y);
10132 else if (MovDir[x][y] == MV_RIGHT)
10134 if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
10135 TEST_DrawLevelField(x + 1, y);
10137 else if (MovDir[x][y] == MV_UP)
10139 if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
10140 TEST_DrawLevelField(x, y - 1);
10144 if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
10145 TEST_DrawLevelField(x, y + 1);
10148 Feld[x][y] = Store[x][y];
10150 GfxDir[x][y] = MovDir[x][y] = MV_NONE;
10151 TEST_DrawLevelField(x, y);
10156 void MauerAbleger(int ax, int ay)
10158 int element = Feld[ax][ay];
10159 int graphic = el2img(element);
10160 boolean oben_frei = FALSE, unten_frei = FALSE;
10161 boolean links_frei = FALSE, rechts_frei = FALSE;
10162 boolean oben_massiv = FALSE, unten_massiv = FALSE;
10163 boolean links_massiv = FALSE, rechts_massiv = FALSE;
10164 boolean new_wall = FALSE;
10166 if (IS_ANIMATED(graphic))
10167 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
10169 if (!MovDelay[ax][ay]) /* start building new wall */
10170 MovDelay[ax][ay] = 6;
10172 if (MovDelay[ax][ay]) /* wait some time before building new wall */
10174 MovDelay[ax][ay]--;
10175 if (MovDelay[ax][ay])
10179 if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
10181 if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
10183 if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
10185 if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
10186 rechts_frei = TRUE;
10188 if (element == EL_EXPANDABLE_WALL_VERTICAL ||
10189 element == EL_EXPANDABLE_WALL_ANY)
10193 Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
10194 Store[ax][ay-1] = element;
10195 GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
10196 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
10197 DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
10198 IMG_EXPANDABLE_WALL_GROWING_UP, 0);
10203 Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
10204 Store[ax][ay+1] = element;
10205 GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
10206 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
10207 DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
10208 IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
10213 if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
10214 element == EL_EXPANDABLE_WALL_ANY ||
10215 element == EL_EXPANDABLE_WALL ||
10216 element == EL_BD_EXPANDABLE_WALL)
10220 Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
10221 Store[ax-1][ay] = element;
10222 GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
10223 if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
10224 DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
10225 IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
10231 Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
10232 Store[ax+1][ay] = element;
10233 GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
10234 if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
10235 DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
10236 IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
10241 if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
10242 TEST_DrawLevelField(ax, ay);
10244 if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
10245 oben_massiv = TRUE;
10246 if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
10247 unten_massiv = TRUE;
10248 if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
10249 links_massiv = TRUE;
10250 if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
10251 rechts_massiv = TRUE;
10253 if (((oben_massiv && unten_massiv) ||
10254 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
10255 element == EL_EXPANDABLE_WALL) &&
10256 ((links_massiv && rechts_massiv) ||
10257 element == EL_EXPANDABLE_WALL_VERTICAL))
10258 Feld[ax][ay] = EL_WALL;
10261 PlayLevelSoundAction(ax, ay, ACTION_GROWING);
10264 void MauerAblegerStahl(int ax, int ay)
10266 int element = Feld[ax][ay];
10267 int graphic = el2img(element);
10268 boolean oben_frei = FALSE, unten_frei = FALSE;
10269 boolean links_frei = FALSE, rechts_frei = FALSE;
10270 boolean oben_massiv = FALSE, unten_massiv = FALSE;
10271 boolean links_massiv = FALSE, rechts_massiv = FALSE;
10272 boolean new_wall = FALSE;
10274 if (IS_ANIMATED(graphic))
10275 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
10277 if (!MovDelay[ax][ay]) /* start building new wall */
10278 MovDelay[ax][ay] = 6;
10280 if (MovDelay[ax][ay]) /* wait some time before building new wall */
10282 MovDelay[ax][ay]--;
10283 if (MovDelay[ax][ay])
10287 if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
10289 if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
10291 if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
10293 if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
10294 rechts_frei = TRUE;
10296 if (element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
10297 element == EL_EXPANDABLE_STEELWALL_ANY)
10301 Feld[ax][ay-1] = EL_EXPANDABLE_STEELWALL_GROWING;
10302 Store[ax][ay-1] = element;
10303 GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
10304 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
10305 DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
10306 IMG_EXPANDABLE_STEELWALL_GROWING_UP, 0);
10311 Feld[ax][ay+1] = EL_EXPANDABLE_STEELWALL_GROWING;
10312 Store[ax][ay+1] = element;
10313 GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
10314 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
10315 DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
10316 IMG_EXPANDABLE_STEELWALL_GROWING_DOWN, 0);
10321 if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
10322 element == EL_EXPANDABLE_STEELWALL_ANY)
10326 Feld[ax-1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
10327 Store[ax-1][ay] = element;
10328 GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
10329 if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
10330 DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
10331 IMG_EXPANDABLE_STEELWALL_GROWING_LEFT, 0);
10337 Feld[ax+1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
10338 Store[ax+1][ay] = element;
10339 GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
10340 if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
10341 DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
10342 IMG_EXPANDABLE_STEELWALL_GROWING_RIGHT, 0);
10347 if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
10348 oben_massiv = TRUE;
10349 if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
10350 unten_massiv = TRUE;
10351 if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
10352 links_massiv = TRUE;
10353 if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
10354 rechts_massiv = TRUE;
10356 if (((oben_massiv && unten_massiv) ||
10357 element == EL_EXPANDABLE_STEELWALL_HORIZONTAL) &&
10358 ((links_massiv && rechts_massiv) ||
10359 element == EL_EXPANDABLE_STEELWALL_VERTICAL))
10360 Feld[ax][ay] = EL_STEELWALL;
10363 PlayLevelSoundAction(ax, ay, ACTION_GROWING);
10366 void CheckForDragon(int x, int y)
10369 boolean dragon_found = FALSE;
10370 static int xy[4][2] =
10378 for (i = 0; i < NUM_DIRECTIONS; i++)
10380 for (j = 0; j < 4; j++)
10382 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
10384 if (IN_LEV_FIELD(xx, yy) &&
10385 (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
10387 if (Feld[xx][yy] == EL_DRAGON)
10388 dragon_found = TRUE;
10397 for (i = 0; i < NUM_DIRECTIONS; i++)
10399 for (j = 0; j < 3; j++)
10401 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
10403 if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
10405 Feld[xx][yy] = EL_EMPTY;
10406 TEST_DrawLevelField(xx, yy);
10415 static void InitBuggyBase(int x, int y)
10417 int element = Feld[x][y];
10418 int activating_delay = FRAMES_PER_SECOND / 4;
10420 ChangeDelay[x][y] =
10421 (element == EL_SP_BUGGY_BASE ?
10422 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
10423 element == EL_SP_BUGGY_BASE_ACTIVATING ?
10425 element == EL_SP_BUGGY_BASE_ACTIVE ?
10426 1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
10429 static void WarnBuggyBase(int x, int y)
10432 static int xy[4][2] =
10440 for (i = 0; i < NUM_DIRECTIONS; i++)
10442 int xx = x + xy[i][0];
10443 int yy = y + xy[i][1];
10445 if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
10447 PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
10454 static void InitTrap(int x, int y)
10456 ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
10459 static void ActivateTrap(int x, int y)
10461 PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
10464 static void ChangeActiveTrap(int x, int y)
10466 int graphic = IMG_TRAP_ACTIVE;
10468 /* if new animation frame was drawn, correct crumbled sand border */
10469 if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
10470 TEST_DrawLevelFieldCrumbled(x, y);
10473 static int getSpecialActionElement(int element, int number, int base_element)
10475 return (element != EL_EMPTY ? element :
10476 number != -1 ? base_element + number - 1 :
10480 static int getModifiedActionNumber(int value_old, int operator, int operand,
10481 int value_min, int value_max)
10483 int value_new = (operator == CA_MODE_SET ? operand :
10484 operator == CA_MODE_ADD ? value_old + operand :
10485 operator == CA_MODE_SUBTRACT ? value_old - operand :
10486 operator == CA_MODE_MULTIPLY ? value_old * operand :
10487 operator == CA_MODE_DIVIDE ? value_old / MAX(1, operand) :
10488 operator == CA_MODE_MODULO ? value_old % MAX(1, operand) :
10491 return (value_new < value_min ? value_min :
10492 value_new > value_max ? value_max :
10496 static void ExecuteCustomElementAction(int x, int y, int element, int page)
10498 struct ElementInfo *ei = &element_info[element];
10499 struct ElementChangeInfo *change = &ei->change_page[page];
10500 int target_element = change->target_element;
10501 int action_type = change->action_type;
10502 int action_mode = change->action_mode;
10503 int action_arg = change->action_arg;
10504 int action_element = change->action_element;
10507 if (!change->has_action)
10510 /* ---------- determine action paramater values -------------------------- */
10512 int level_time_value =
10513 (level.time > 0 ? TimeLeft :
10516 int action_arg_element_raw =
10517 (action_arg == CA_ARG_PLAYER_TRIGGER ? change->actual_trigger_player :
10518 action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
10519 action_arg == CA_ARG_ELEMENT_TARGET ? change->target_element :
10520 action_arg == CA_ARG_ELEMENT_ACTION ? change->action_element :
10521 action_arg == CA_ARG_INVENTORY_RM_TRIGGER ? change->actual_trigger_element:
10522 action_arg == CA_ARG_INVENTORY_RM_TARGET ? change->target_element :
10523 action_arg == CA_ARG_INVENTORY_RM_ACTION ? change->action_element :
10525 int action_arg_element = GetElementFromGroupElement(action_arg_element_raw);
10528 if (action_arg_element_raw == EL_GROUP_START)
10529 printf("::: %d,%d: %d ('%s')\n", x, y, element, EL_NAME(element));
10532 int action_arg_direction =
10533 (action_arg >= CA_ARG_DIRECTION_LEFT &&
10534 action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
10535 action_arg == CA_ARG_DIRECTION_TRIGGER ?
10536 change->actual_trigger_side :
10537 action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
10538 MV_DIR_OPPOSITE(change->actual_trigger_side) :
10541 int action_arg_number_min =
10542 (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
10545 int action_arg_number_max =
10546 (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
10547 action_type == CA_SET_LEVEL_GEMS ? 999 :
10548 action_type == CA_SET_LEVEL_TIME ? 9999 :
10549 action_type == CA_SET_LEVEL_SCORE ? 99999 :
10550 action_type == CA_SET_CE_VALUE ? 9999 :
10551 action_type == CA_SET_CE_SCORE ? 9999 :
10554 int action_arg_number_reset =
10555 (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize[0] :
10556 action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
10557 action_type == CA_SET_LEVEL_TIME ? level.time :
10558 action_type == CA_SET_LEVEL_SCORE ? 0 :
10559 #if USE_NEW_CUSTOM_VALUE
10560 action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
10562 action_type == CA_SET_CE_VALUE ? ei->custom_value_initial :
10564 action_type == CA_SET_CE_SCORE ? 0 :
10567 int action_arg_number =
10568 (action_arg <= CA_ARG_MAX ? action_arg :
10569 action_arg >= CA_ARG_SPEED_NOT_MOVING &&
10570 action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
10571 action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
10572 action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
10573 action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
10574 action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
10575 #if USE_NEW_CUSTOM_VALUE
10576 action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
10578 action_arg == CA_ARG_NUMBER_CE_VALUE ? ei->custom_value_initial :
10580 action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
10581 action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
10582 action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
10583 action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? local_player->gems_still_needed :
10584 action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? local_player->score :
10585 action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
10586 action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
10587 action_arg == CA_ARG_ELEMENT_CV_ACTION ? GET_NEW_CE_VALUE(action_element):
10588 action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
10589 action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
10590 action_arg == CA_ARG_ELEMENT_CS_ACTION ? GET_CE_SCORE(action_element) :
10591 action_arg == CA_ARG_ELEMENT_NR_TARGET ? change->target_element :
10592 action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
10593 action_arg == CA_ARG_ELEMENT_NR_ACTION ? change->action_element :
10596 int action_arg_number_old =
10597 (action_type == CA_SET_LEVEL_GEMS ? local_player->gems_still_needed :
10598 action_type == CA_SET_LEVEL_TIME ? TimeLeft :
10599 action_type == CA_SET_LEVEL_SCORE ? local_player->score :
10600 action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
10601 action_type == CA_SET_CE_SCORE ? ei->collect_score :
10604 int action_arg_number_new =
10605 getModifiedActionNumber(action_arg_number_old,
10606 action_mode, action_arg_number,
10607 action_arg_number_min, action_arg_number_max);
10610 int trigger_player_bits =
10611 (change->actual_trigger_player_bits != CH_PLAYER_NONE ?
10612 change->actual_trigger_player_bits : change->trigger_player);
10614 int trigger_player_bits =
10615 (change->actual_trigger_player >= EL_PLAYER_1 &&
10616 change->actual_trigger_player <= EL_PLAYER_4 ?
10617 (1 << (change->actual_trigger_player - EL_PLAYER_1)) :
10621 int action_arg_player_bits =
10622 (action_arg >= CA_ARG_PLAYER_1 &&
10623 action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
10624 action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
10625 action_arg == CA_ARG_PLAYER_ACTION ? 1 << GET_PLAYER_NR(action_element) :
10628 /* ---------- execute action -------------------------------------------- */
10630 switch (action_type)
10637 /* ---------- level actions ------------------------------------------- */
10639 case CA_RESTART_LEVEL:
10641 game.restart_level = TRUE;
10646 case CA_SHOW_ENVELOPE:
10648 int element = getSpecialActionElement(action_arg_element,
10649 action_arg_number, EL_ENVELOPE_1);
10651 if (IS_ENVELOPE(element))
10652 local_player->show_envelope = element;
10657 case CA_SET_LEVEL_TIME:
10659 if (level.time > 0) /* only modify limited time value */
10661 TimeLeft = action_arg_number_new;
10664 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
10666 DisplayGameControlValues();
10668 DrawGameValue_Time(TimeLeft);
10671 if (!TimeLeft && setup.time_limit)
10672 for (i = 0; i < MAX_PLAYERS; i++)
10673 KillPlayer(&stored_player[i]);
10679 case CA_SET_LEVEL_SCORE:
10681 local_player->score = action_arg_number_new;
10684 game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
10686 DisplayGameControlValues();
10688 DrawGameValue_Score(local_player->score);
10694 case CA_SET_LEVEL_GEMS:
10696 local_player->gems_still_needed = action_arg_number_new;
10699 game_panel_controls[GAME_PANEL_GEMS].value = local_player->gems_still_needed;
10701 DisplayGameControlValues();
10703 DrawGameValue_Emeralds(local_player->gems_still_needed);
10709 #if !USE_PLAYER_GRAVITY
10710 case CA_SET_LEVEL_GRAVITY:
10712 game.gravity = (action_arg == CA_ARG_GRAVITY_OFF ? FALSE :
10713 action_arg == CA_ARG_GRAVITY_ON ? TRUE :
10714 action_arg == CA_ARG_GRAVITY_TOGGLE ? !game.gravity :
10720 case CA_SET_LEVEL_WIND:
10722 game.wind_direction = action_arg_direction;
10727 case CA_SET_LEVEL_RANDOM_SEED:
10730 /* ensure that setting a new random seed while playing is predictable */
10731 InitRND(action_arg_number_new ? action_arg_number_new : RND(1000000) + 1);
10733 InitRND(action_arg_number_new);
10737 printf("::: %d -> %d\n", action_arg_number_new, RND(10));
10745 for (i = 0; i < 9; i++)
10746 printf("%d, ", RND(2));
10754 /* ---------- player actions ------------------------------------------ */
10756 case CA_MOVE_PLAYER:
10758 /* automatically move to the next field in specified direction */
10759 for (i = 0; i < MAX_PLAYERS; i++)
10760 if (trigger_player_bits & (1 << i))
10761 stored_player[i].programmed_action = action_arg_direction;
10766 case CA_EXIT_PLAYER:
10768 for (i = 0; i < MAX_PLAYERS; i++)
10769 if (action_arg_player_bits & (1 << i))
10770 PlayerWins(&stored_player[i]);
10775 case CA_KILL_PLAYER:
10777 for (i = 0; i < MAX_PLAYERS; i++)
10778 if (action_arg_player_bits & (1 << i))
10779 KillPlayer(&stored_player[i]);
10784 case CA_SET_PLAYER_KEYS:
10786 int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
10787 int element = getSpecialActionElement(action_arg_element,
10788 action_arg_number, EL_KEY_1);
10790 if (IS_KEY(element))
10792 for (i = 0; i < MAX_PLAYERS; i++)
10794 if (trigger_player_bits & (1 << i))
10796 stored_player[i].key[KEY_NR(element)] = key_state;
10798 DrawGameDoorValues();
10806 case CA_SET_PLAYER_SPEED:
10809 printf("::: trigger_player_bits == %d\n", trigger_player_bits);
10812 for (i = 0; i < MAX_PLAYERS; i++)
10814 if (trigger_player_bits & (1 << i))
10816 int move_stepsize = TILEX / stored_player[i].move_delay_value;
10818 if (action_arg == CA_ARG_SPEED_FASTER &&
10819 stored_player[i].cannot_move)
10821 action_arg_number = STEPSIZE_VERY_SLOW;
10823 else if (action_arg == CA_ARG_SPEED_SLOWER ||
10824 action_arg == CA_ARG_SPEED_FASTER)
10826 action_arg_number = 2;
10827 action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
10830 else if (action_arg == CA_ARG_NUMBER_RESET)
10832 action_arg_number = level.initial_player_stepsize[i];
10836 getModifiedActionNumber(move_stepsize,
10839 action_arg_number_min,
10840 action_arg_number_max);
10842 SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
10849 case CA_SET_PLAYER_SHIELD:
10851 for (i = 0; i < MAX_PLAYERS; i++)
10853 if (trigger_player_bits & (1 << i))
10855 if (action_arg == CA_ARG_SHIELD_OFF)
10857 stored_player[i].shield_normal_time_left = 0;
10858 stored_player[i].shield_deadly_time_left = 0;
10860 else if (action_arg == CA_ARG_SHIELD_NORMAL)
10862 stored_player[i].shield_normal_time_left = 999999;
10864 else if (action_arg == CA_ARG_SHIELD_DEADLY)
10866 stored_player[i].shield_normal_time_left = 999999;
10867 stored_player[i].shield_deadly_time_left = 999999;
10875 #if USE_PLAYER_GRAVITY
10876 case CA_SET_PLAYER_GRAVITY:
10878 for (i = 0; i < MAX_PLAYERS; i++)
10880 if (trigger_player_bits & (1 << i))
10882 stored_player[i].gravity =
10883 (action_arg == CA_ARG_GRAVITY_OFF ? FALSE :
10884 action_arg == CA_ARG_GRAVITY_ON ? TRUE :
10885 action_arg == CA_ARG_GRAVITY_TOGGLE ? !stored_player[i].gravity :
10886 stored_player[i].gravity);
10894 case CA_SET_PLAYER_ARTWORK:
10896 for (i = 0; i < MAX_PLAYERS; i++)
10898 if (trigger_player_bits & (1 << i))
10900 int artwork_element = action_arg_element;
10902 if (action_arg == CA_ARG_ELEMENT_RESET)
10904 (level.use_artwork_element[i] ? level.artwork_element[i] :
10905 stored_player[i].element_nr);
10907 #if USE_GFX_RESET_PLAYER_ARTWORK
10908 if (stored_player[i].artwork_element != artwork_element)
10909 stored_player[i].Frame = 0;
10912 stored_player[i].artwork_element = artwork_element;
10914 SetPlayerWaiting(&stored_player[i], FALSE);
10916 /* set number of special actions for bored and sleeping animation */
10917 stored_player[i].num_special_action_bored =
10918 get_num_special_action(artwork_element,
10919 ACTION_BORING_1, ACTION_BORING_LAST);
10920 stored_player[i].num_special_action_sleeping =
10921 get_num_special_action(artwork_element,
10922 ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
10929 case CA_SET_PLAYER_INVENTORY:
10931 for (i = 0; i < MAX_PLAYERS; i++)
10933 struct PlayerInfo *player = &stored_player[i];
10936 if (trigger_player_bits & (1 << i))
10938 int inventory_element = action_arg_element;
10940 if (action_arg == CA_ARG_ELEMENT_TARGET ||
10941 action_arg == CA_ARG_ELEMENT_TRIGGER ||
10942 action_arg == CA_ARG_ELEMENT_ACTION)
10944 int element = inventory_element;
10945 int collect_count = element_info[element].collect_count_initial;
10947 if (!IS_CUSTOM_ELEMENT(element))
10950 if (collect_count == 0)
10951 player->inventory_infinite_element = element;
10953 for (k = 0; k < collect_count; k++)
10954 if (player->inventory_size < MAX_INVENTORY_SIZE)
10955 player->inventory_element[player->inventory_size++] =
10958 else if (action_arg == CA_ARG_INVENTORY_RM_TARGET ||
10959 action_arg == CA_ARG_INVENTORY_RM_TRIGGER ||
10960 action_arg == CA_ARG_INVENTORY_RM_ACTION)
10962 if (player->inventory_infinite_element != EL_UNDEFINED &&
10963 IS_EQUAL_OR_IN_GROUP(player->inventory_infinite_element,
10964 action_arg_element_raw))
10965 player->inventory_infinite_element = EL_UNDEFINED;
10967 for (k = 0, j = 0; j < player->inventory_size; j++)
10969 if (!IS_EQUAL_OR_IN_GROUP(player->inventory_element[j],
10970 action_arg_element_raw))
10971 player->inventory_element[k++] = player->inventory_element[j];
10974 player->inventory_size = k;
10976 else if (action_arg == CA_ARG_INVENTORY_RM_FIRST)
10978 if (player->inventory_size > 0)
10980 for (j = 0; j < player->inventory_size - 1; j++)
10981 player->inventory_element[j] = player->inventory_element[j + 1];
10983 player->inventory_size--;
10986 else if (action_arg == CA_ARG_INVENTORY_RM_LAST)
10988 if (player->inventory_size > 0)
10989 player->inventory_size--;
10991 else if (action_arg == CA_ARG_INVENTORY_RM_ALL)
10993 player->inventory_infinite_element = EL_UNDEFINED;
10994 player->inventory_size = 0;
10996 else if (action_arg == CA_ARG_INVENTORY_RESET)
10998 player->inventory_infinite_element = EL_UNDEFINED;
10999 player->inventory_size = 0;
11001 if (level.use_initial_inventory[i])
11003 for (j = 0; j < level.initial_inventory_size[i]; j++)
11005 int element = level.initial_inventory_content[i][j];
11006 int collect_count = element_info[element].collect_count_initial;
11008 if (!IS_CUSTOM_ELEMENT(element))
11011 if (collect_count == 0)
11012 player->inventory_infinite_element = element;
11014 for (k = 0; k < collect_count; k++)
11015 if (player->inventory_size < MAX_INVENTORY_SIZE)
11016 player->inventory_element[player->inventory_size++] =
11027 /* ---------- CE actions ---------------------------------------------- */
11029 case CA_SET_CE_VALUE:
11031 #if USE_NEW_CUSTOM_VALUE
11032 int last_ce_value = CustomValue[x][y];
11034 CustomValue[x][y] = action_arg_number_new;
11036 if (CustomValue[x][y] != last_ce_value)
11038 CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_CHANGES);
11039 CheckTriggeredElementChange(x, y, element, CE_VALUE_CHANGES_OF_X);
11041 if (CustomValue[x][y] == 0)
11043 CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
11044 CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
11052 case CA_SET_CE_SCORE:
11054 #if USE_NEW_CUSTOM_VALUE
11055 int last_ce_score = ei->collect_score;
11057 ei->collect_score = action_arg_number_new;
11059 if (ei->collect_score != last_ce_score)
11061 CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
11062 CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
11064 if (ei->collect_score == 0)
11068 CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
11069 CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
11072 This is a very special case that seems to be a mixture between
11073 CheckElementChange() and CheckTriggeredElementChange(): while
11074 the first one only affects single elements that are triggered
11075 directly, the second one affects multiple elements in the playfield
11076 that are triggered indirectly by another element. This is a third
11077 case: Changing the CE score always affects multiple identical CEs,
11078 so every affected CE must be checked, not only the single CE for
11079 which the CE score was changed in the first place (as every instance
11080 of that CE shares the same CE score, and therefore also can change)!
11082 SCAN_PLAYFIELD(xx, yy)
11084 if (Feld[xx][yy] == element)
11085 CheckElementChange(xx, yy, element, EL_UNDEFINED,
11086 CE_SCORE_GETS_ZERO);
11095 case CA_SET_CE_ARTWORK:
11097 int artwork_element = action_arg_element;
11098 boolean reset_frame = FALSE;
11101 if (action_arg == CA_ARG_ELEMENT_RESET)
11102 artwork_element = (ei->use_gfx_element ? ei->gfx_element_initial :
11105 if (ei->gfx_element != artwork_element)
11106 reset_frame = TRUE;
11108 ei->gfx_element = artwork_element;
11110 SCAN_PLAYFIELD(xx, yy)
11112 if (Feld[xx][yy] == element)
11116 ResetGfxAnimation(xx, yy);
11117 ResetRandomAnimationValue(xx, yy);
11120 TEST_DrawLevelField(xx, yy);
11127 /* ---------- engine actions ------------------------------------------ */
11129 case CA_SET_ENGINE_SCAN_MODE:
11131 InitPlayfieldScanMode(action_arg);
11141 static void CreateFieldExt(int x, int y, int element, boolean is_change)
11143 int old_element = Feld[x][y];
11144 int new_element = GetElementFromGroupElement(element);
11145 int previous_move_direction = MovDir[x][y];
11146 #if USE_NEW_CUSTOM_VALUE
11147 int last_ce_value = CustomValue[x][y];
11149 boolean player_explosion_protected = PLAYER_EXPLOSION_PROTECTED(x, y);
11150 boolean new_element_is_player = ELEM_IS_PLAYER(new_element);
11151 boolean add_player_onto_element = (new_element_is_player &&
11152 #if USE_CODE_THAT_BREAKS_SNAKE_BITE
11153 /* this breaks SnakeBite when a snake is
11154 halfway through a door that closes */
11155 /* NOW FIXED AT LEVEL INIT IN files.c */
11156 new_element != EL_SOKOBAN_FIELD_PLAYER &&
11158 IS_WALKABLE(old_element));
11161 /* check if element under the player changes from accessible to unaccessible
11162 (needed for special case of dropping element which then changes) */
11163 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
11164 IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
11172 if (!add_player_onto_element)
11174 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
11175 RemoveMovingField(x, y);
11179 Feld[x][y] = new_element;
11181 #if !USE_GFX_RESET_GFX_ANIMATION
11182 ResetGfxAnimation(x, y);
11183 ResetRandomAnimationValue(x, y);
11186 if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
11187 MovDir[x][y] = previous_move_direction;
11189 #if USE_NEW_CUSTOM_VALUE
11190 if (element_info[new_element].use_last_ce_value)
11191 CustomValue[x][y] = last_ce_value;
11194 InitField_WithBug1(x, y, FALSE);
11196 new_element = Feld[x][y]; /* element may have changed */
11198 #if USE_GFX_RESET_GFX_ANIMATION
11199 ResetGfxAnimation(x, y);
11200 ResetRandomAnimationValue(x, y);
11203 TEST_DrawLevelField(x, y);
11205 if (GFX_CRUMBLED(new_element))
11206 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
11210 /* check if element under the player changes from accessible to unaccessible
11211 (needed for special case of dropping element which then changes) */
11212 /* (must be checked after creating new element for walkable group elements) */
11213 #if USE_FIX_KILLED_BY_NON_WALKABLE
11214 if (IS_PLAYER(x, y) && !player_explosion_protected &&
11215 IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
11222 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
11223 IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
11232 /* "ChangeCount" not set yet to allow "entered by player" change one time */
11233 if (new_element_is_player)
11234 RelocatePlayer(x, y, new_element);
11237 ChangeCount[x][y]++; /* count number of changes in the same frame */
11239 TestIfBadThingTouchesPlayer(x, y);
11240 TestIfPlayerTouchesCustomElement(x, y);
11241 TestIfElementTouchesCustomElement(x, y);
11244 static void CreateField(int x, int y, int element)
11246 CreateFieldExt(x, y, element, FALSE);
11249 static void CreateElementFromChange(int x, int y, int element)
11251 element = GET_VALID_RUNTIME_ELEMENT(element);
11253 #if USE_STOP_CHANGED_ELEMENTS
11254 if (game.engine_version >= VERSION_IDENT(3,2,0,7))
11256 int old_element = Feld[x][y];
11258 /* prevent changed element from moving in same engine frame
11259 unless both old and new element can either fall or move */
11260 if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
11261 (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
11266 CreateFieldExt(x, y, element, TRUE);
11269 static boolean ChangeElement(int x, int y, int element, int page)
11271 struct ElementInfo *ei = &element_info[element];
11272 struct ElementChangeInfo *change = &ei->change_page[page];
11273 int ce_value = CustomValue[x][y];
11274 int ce_score = ei->collect_score;
11275 int target_element;
11276 int old_element = Feld[x][y];
11278 /* always use default change event to prevent running into a loop */
11279 if (ChangeEvent[x][y] == -1)
11280 ChangeEvent[x][y] = CE_DELAY;
11282 if (ChangeEvent[x][y] == CE_DELAY)
11284 /* reset actual trigger element, trigger player and action element */
11285 change->actual_trigger_element = EL_EMPTY;
11286 change->actual_trigger_player = EL_EMPTY;
11287 change->actual_trigger_player_bits = CH_PLAYER_NONE;
11288 change->actual_trigger_side = CH_SIDE_NONE;
11289 change->actual_trigger_ce_value = 0;
11290 change->actual_trigger_ce_score = 0;
11293 /* do not change elements more than a specified maximum number of changes */
11294 if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
11297 ChangeCount[x][y]++; /* count number of changes in the same frame */
11299 if (change->explode)
11306 if (change->use_target_content)
11308 boolean complete_replace = TRUE;
11309 boolean can_replace[3][3];
11312 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
11315 boolean is_walkable;
11316 boolean is_diggable;
11317 boolean is_collectible;
11318 boolean is_removable;
11319 boolean is_destructible;
11320 int ex = x + xx - 1;
11321 int ey = y + yy - 1;
11322 int content_element = change->target_content.e[xx][yy];
11325 can_replace[xx][yy] = TRUE;
11327 if (ex == x && ey == y) /* do not check changing element itself */
11330 if (content_element == EL_EMPTY_SPACE)
11332 can_replace[xx][yy] = FALSE; /* do not replace border with space */
11337 if (!IN_LEV_FIELD(ex, ey))
11339 can_replace[xx][yy] = FALSE;
11340 complete_replace = FALSE;
11347 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
11348 e = MovingOrBlocked2Element(ex, ey);
11350 is_empty = (IS_FREE(ex, ey) ||
11351 (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
11353 is_walkable = (is_empty || IS_WALKABLE(e));
11354 is_diggable = (is_empty || IS_DIGGABLE(e));
11355 is_collectible = (is_empty || IS_COLLECTIBLE(e));
11356 is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
11357 is_removable = (is_diggable || is_collectible);
11359 can_replace[xx][yy] =
11360 (((change->replace_when == CP_WHEN_EMPTY && is_empty) ||
11361 (change->replace_when == CP_WHEN_WALKABLE && is_walkable) ||
11362 (change->replace_when == CP_WHEN_DIGGABLE && is_diggable) ||
11363 (change->replace_when == CP_WHEN_COLLECTIBLE && is_collectible) ||
11364 (change->replace_when == CP_WHEN_REMOVABLE && is_removable) ||
11365 (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
11366 !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
11368 if (!can_replace[xx][yy])
11369 complete_replace = FALSE;
11372 if (!change->only_if_complete || complete_replace)
11374 boolean something_has_changed = FALSE;
11376 if (change->only_if_complete && change->use_random_replace &&
11377 RND(100) < change->random_percentage)
11380 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
11382 int ex = x + xx - 1;
11383 int ey = y + yy - 1;
11384 int content_element;
11386 if (can_replace[xx][yy] && (!change->use_random_replace ||
11387 RND(100) < change->random_percentage))
11389 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
11390 RemoveMovingField(ex, ey);
11392 ChangeEvent[ex][ey] = ChangeEvent[x][y];
11394 content_element = change->target_content.e[xx][yy];
11395 target_element = GET_TARGET_ELEMENT(element, content_element, change,
11396 ce_value, ce_score);
11398 CreateElementFromChange(ex, ey, target_element);
11400 something_has_changed = TRUE;
11402 /* for symmetry reasons, freeze newly created border elements */
11403 if (ex != x || ey != y)
11404 Stop[ex][ey] = TRUE; /* no more moving in this frame */
11408 if (something_has_changed)
11410 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
11411 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
11417 target_element = GET_TARGET_ELEMENT(element, change->target_element, change,
11418 ce_value, ce_score);
11420 if (element == EL_DIAGONAL_GROWING ||
11421 element == EL_DIAGONAL_SHRINKING)
11423 target_element = Store[x][y];
11425 Store[x][y] = EL_EMPTY;
11428 CreateElementFromChange(x, y, target_element);
11430 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
11431 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
11434 /* this uses direct change before indirect change */
11435 CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
11440 #if USE_NEW_DELAYED_ACTION
11442 static void HandleElementChange(int x, int y, int page)
11444 int element = MovingOrBlocked2Element(x, y);
11445 struct ElementInfo *ei = &element_info[element];
11446 struct ElementChangeInfo *change = &ei->change_page[page];
11447 boolean handle_action_before_change = FALSE;
11450 if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
11451 !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
11454 printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
11455 x, y, element, element_info[element].token_name);
11456 printf("HandleElementChange(): This should never happen!\n");
11461 /* this can happen with classic bombs on walkable, changing elements */
11462 if (!CAN_CHANGE_OR_HAS_ACTION(element))
11465 if (!CAN_CHANGE(Back[x][y])) /* prevent permanent repetition */
11466 ChangeDelay[x][y] = 0;
11472 if (ChangeDelay[x][y] == 0) /* initialize element change */
11474 ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
11476 if (change->can_change)
11479 /* !!! not clear why graphic animation should be reset at all here !!! */
11480 /* !!! UPDATE: but is needed for correct Snake Bite tail animation !!! */
11481 #if USE_GFX_RESET_WHEN_NOT_MOVING
11482 /* when a custom element is about to change (for example by change delay),
11483 do not reset graphic animation when the custom element is moving */
11484 if (!IS_MOVING(x, y))
11487 ResetGfxAnimation(x, y);
11488 ResetRandomAnimationValue(x, y);
11492 if (change->pre_change_function)
11493 change->pre_change_function(x, y);
11497 ChangeDelay[x][y]--;
11499 if (ChangeDelay[x][y] != 0) /* continue element change */
11501 if (change->can_change)
11503 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11505 if (IS_ANIMATED(graphic))
11506 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11508 if (change->change_function)
11509 change->change_function(x, y);
11512 else /* finish element change */
11514 if (ChangePage[x][y] != -1) /* remember page from delayed change */
11516 page = ChangePage[x][y];
11517 ChangePage[x][y] = -1;
11519 change = &ei->change_page[page];
11522 if (IS_MOVING(x, y)) /* never change a running system ;-) */
11524 ChangeDelay[x][y] = 1; /* try change after next move step */
11525 ChangePage[x][y] = page; /* remember page to use for change */
11531 /* special case: set new level random seed before changing element */
11532 if (change->has_action && change->action_type == CA_SET_LEVEL_RANDOM_SEED)
11533 handle_action_before_change = TRUE;
11535 if (change->has_action && handle_action_before_change)
11536 ExecuteCustomElementAction(x, y, element, page);
11539 if (change->can_change)
11541 if (ChangeElement(x, y, element, page))
11543 if (change->post_change_function)
11544 change->post_change_function(x, y);
11548 if (change->has_action && !handle_action_before_change)
11549 ExecuteCustomElementAction(x, y, element, page);
11555 static void HandleElementChange(int x, int y, int page)
11557 int element = MovingOrBlocked2Element(x, y);
11558 struct ElementInfo *ei = &element_info[element];
11559 struct ElementChangeInfo *change = &ei->change_page[page];
11562 if (!CAN_CHANGE(element) && !CAN_CHANGE(Back[x][y]))
11565 printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
11566 x, y, element, element_info[element].token_name);
11567 printf("HandleElementChange(): This should never happen!\n");
11572 /* this can happen with classic bombs on walkable, changing elements */
11573 if (!CAN_CHANGE(element))
11576 if (!CAN_CHANGE(Back[x][y])) /* prevent permanent repetition */
11577 ChangeDelay[x][y] = 0;
11583 if (ChangeDelay[x][y] == 0) /* initialize element change */
11585 ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
11587 ResetGfxAnimation(x, y);
11588 ResetRandomAnimationValue(x, y);
11590 if (change->pre_change_function)
11591 change->pre_change_function(x, y);
11594 ChangeDelay[x][y]--;
11596 if (ChangeDelay[x][y] != 0) /* continue element change */
11598 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11600 if (IS_ANIMATED(graphic))
11601 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11603 if (change->change_function)
11604 change->change_function(x, y);
11606 else /* finish element change */
11608 if (ChangePage[x][y] != -1) /* remember page from delayed change */
11610 page = ChangePage[x][y];
11611 ChangePage[x][y] = -1;
11613 change = &ei->change_page[page];
11616 if (IS_MOVING(x, y)) /* never change a running system ;-) */
11618 ChangeDelay[x][y] = 1; /* try change after next move step */
11619 ChangePage[x][y] = page; /* remember page to use for change */
11624 if (ChangeElement(x, y, element, page))
11626 if (change->post_change_function)
11627 change->post_change_function(x, y);
11634 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
11635 int trigger_element,
11637 int trigger_player,
11641 boolean change_done_any = FALSE;
11642 int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
11645 if (!(trigger_events[trigger_element][trigger_event]))
11649 printf("::: CheckTriggeredElementChangeExt %d ... [%d, %d, %d, '%s']\n",
11650 trigger_event, recursion_loop_depth, recursion_loop_detected,
11651 recursion_loop_element, EL_NAME(recursion_loop_element));
11654 RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
11656 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
11658 int element = EL_CUSTOM_START + i;
11659 boolean change_done = FALSE;
11662 if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
11663 !HAS_ANY_CHANGE_EVENT(element, trigger_event))
11666 for (p = 0; p < element_info[element].num_change_pages; p++)
11668 struct ElementChangeInfo *change = &element_info[element].change_page[p];
11670 if (change->can_change_or_has_action &&
11671 change->has_event[trigger_event] &&
11672 change->trigger_side & trigger_side &&
11673 change->trigger_player & trigger_player &&
11674 change->trigger_page & trigger_page_bits &&
11675 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
11677 change->actual_trigger_element = trigger_element;
11678 change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
11679 change->actual_trigger_player_bits = trigger_player;
11680 change->actual_trigger_side = trigger_side;
11681 change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
11682 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11685 printf("::: TRIGGERED CHANGE FOUND: %d ['%s'], %d\n",
11686 element, EL_NAME(element), p);
11689 if ((change->can_change && !change_done) || change->has_action)
11693 SCAN_PLAYFIELD(x, y)
11695 if (Feld[x][y] == element)
11697 if (change->can_change && !change_done)
11699 #if USE_FIX_NO_ACTION_AFTER_CHANGE
11700 /* if element already changed in this frame, not only prevent
11701 another element change (checked in ChangeElement()), but
11702 also prevent additional element actions for this element */
11704 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
11705 !level.use_action_after_change_bug)
11710 printf("::: TRIGGERED CHANGE FOUND: %d ['%s'], %d -- CHANGE\n",
11711 element, EL_NAME(element), p);
11714 ChangeDelay[x][y] = 1;
11715 ChangeEvent[x][y] = trigger_event;
11717 HandleElementChange(x, y, p);
11719 #if USE_NEW_DELAYED_ACTION
11720 else if (change->has_action)
11722 #if USE_FIX_NO_ACTION_AFTER_CHANGE
11723 /* if element already changed in this frame, not only prevent
11724 another element change (checked in ChangeElement()), but
11725 also prevent additional element actions for this element */
11727 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
11728 !level.use_action_after_change_bug)
11734 printf("::: TRIGGERED CHANGE FOUND: %d ['%s'], %d -- ACTION\n",
11735 element, EL_NAME(element), p);
11738 ExecuteCustomElementAction(x, y, element, p);
11739 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11742 if (change->has_action)
11744 ExecuteCustomElementAction(x, y, element, p);
11745 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11751 if (change->can_change)
11753 change_done = TRUE;
11754 change_done_any = TRUE;
11757 printf("::: TRIGGERED CHANGE FOUND: %d ['%s'], %d -- DONE\n",
11758 element, EL_NAME(element), p);
11767 RECURSION_LOOP_DETECTION_END();
11769 return change_done_any;
11772 static boolean CheckElementChangeExt(int x, int y,
11774 int trigger_element,
11776 int trigger_player,
11779 boolean change_done = FALSE;
11782 if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
11783 !HAS_ANY_CHANGE_EVENT(element, trigger_event))
11786 if (Feld[x][y] == EL_BLOCKED)
11788 Blocked2Moving(x, y, &x, &y);
11789 element = Feld[x][y];
11793 /* check if element has already changed */
11794 if (Feld[x][y] != element)
11797 /* check if element has already changed or is about to change after moving */
11798 if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
11799 Feld[x][y] != element) ||
11801 (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
11802 (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
11803 ChangePage[x][y] != -1)))
11808 printf("::: CheckElementChangeExt %d ... [%d, %d, %d, '%s']\n",
11809 trigger_event, recursion_loop_depth, recursion_loop_detected,
11810 recursion_loop_element, EL_NAME(recursion_loop_element));
11813 RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
11816 printf("::: X: trigger_player_bits == %d\n", trigger_player);
11819 for (p = 0; p < element_info[element].num_change_pages; p++)
11821 struct ElementChangeInfo *change = &element_info[element].change_page[p];
11823 /* check trigger element for all events where the element that is checked
11824 for changing interacts with a directly adjacent element -- this is
11825 different to element changes that affect other elements to change on the
11826 whole playfield (which is handeld by CheckTriggeredElementChangeExt()) */
11827 boolean check_trigger_element =
11828 (trigger_event == CE_TOUCHING_X ||
11829 trigger_event == CE_HITTING_X ||
11830 trigger_event == CE_HIT_BY_X ||
11832 /* this one was forgotten until 3.2.3 */
11833 trigger_event == CE_DIGGING_X);
11836 if (change->can_change_or_has_action &&
11837 change->has_event[trigger_event] &&
11838 change->trigger_side & trigger_side &&
11839 change->trigger_player & trigger_player &&
11840 (!check_trigger_element ||
11841 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
11843 change->actual_trigger_element = trigger_element;
11844 change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
11845 change->actual_trigger_player_bits = trigger_player;
11846 change->actual_trigger_side = trigger_side;
11847 change->actual_trigger_ce_value = CustomValue[x][y];
11848 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11850 /* special case: trigger element not at (x,y) position for some events */
11851 if (check_trigger_element)
11863 { 0, 0 }, { 0, 0 }, { 0, 0 },
11867 int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
11868 int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
11870 change->actual_trigger_ce_value = CustomValue[xx][yy];
11871 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11874 if (change->can_change && !change_done)
11876 ChangeDelay[x][y] = 1;
11877 ChangeEvent[x][y] = trigger_event;
11879 HandleElementChange(x, y, p);
11881 change_done = TRUE;
11883 #if USE_NEW_DELAYED_ACTION
11884 else if (change->has_action)
11886 ExecuteCustomElementAction(x, y, element, p);
11887 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11890 if (change->has_action)
11892 ExecuteCustomElementAction(x, y, element, p);
11893 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11899 RECURSION_LOOP_DETECTION_END();
11901 return change_done;
11904 static void PlayPlayerSound(struct PlayerInfo *player)
11906 int jx = player->jx, jy = player->jy;
11907 int sound_element = player->artwork_element;
11908 int last_action = player->last_action_waiting;
11909 int action = player->action_waiting;
11911 if (player->is_waiting)
11913 if (action != last_action)
11914 PlayLevelSoundElementAction(jx, jy, sound_element, action);
11916 PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
11920 if (action != last_action)
11921 StopSound(element_info[sound_element].sound[last_action]);
11923 if (last_action == ACTION_SLEEPING)
11924 PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
11928 static void PlayAllPlayersSound()
11932 for (i = 0; i < MAX_PLAYERS; i++)
11933 if (stored_player[i].active)
11934 PlayPlayerSound(&stored_player[i]);
11937 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
11939 boolean last_waiting = player->is_waiting;
11940 int move_dir = player->MovDir;
11942 player->dir_waiting = move_dir;
11943 player->last_action_waiting = player->action_waiting;
11947 if (!last_waiting) /* not waiting -> waiting */
11949 player->is_waiting = TRUE;
11951 player->frame_counter_bored =
11953 game.player_boring_delay_fixed +
11954 GetSimpleRandom(game.player_boring_delay_random);
11955 player->frame_counter_sleeping =
11957 game.player_sleeping_delay_fixed +
11958 GetSimpleRandom(game.player_sleeping_delay_random);
11960 InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
11963 if (game.player_sleeping_delay_fixed +
11964 game.player_sleeping_delay_random > 0 &&
11965 player->anim_delay_counter == 0 &&
11966 player->post_delay_counter == 0 &&
11967 FrameCounter >= player->frame_counter_sleeping)
11968 player->is_sleeping = TRUE;
11969 else if (game.player_boring_delay_fixed +
11970 game.player_boring_delay_random > 0 &&
11971 FrameCounter >= player->frame_counter_bored)
11972 player->is_bored = TRUE;
11974 player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
11975 player->is_bored ? ACTION_BORING :
11978 if (player->is_sleeping && player->use_murphy)
11980 /* special case for sleeping Murphy when leaning against non-free tile */
11982 if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
11983 (Feld[player->jx - 1][player->jy] != EL_EMPTY &&
11984 !IS_MOVING(player->jx - 1, player->jy)))
11985 move_dir = MV_LEFT;
11986 else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
11987 (Feld[player->jx + 1][player->jy] != EL_EMPTY &&
11988 !IS_MOVING(player->jx + 1, player->jy)))
11989 move_dir = MV_RIGHT;
11991 player->is_sleeping = FALSE;
11993 player->dir_waiting = move_dir;
11996 if (player->is_sleeping)
11998 if (player->num_special_action_sleeping > 0)
12000 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
12002 int last_special_action = player->special_action_sleeping;
12003 int num_special_action = player->num_special_action_sleeping;
12004 int special_action =
12005 (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
12006 last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
12007 last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
12008 last_special_action + 1 : ACTION_SLEEPING);
12009 int special_graphic =
12010 el_act_dir2img(player->artwork_element, special_action, move_dir);
12012 player->anim_delay_counter =
12013 graphic_info[special_graphic].anim_delay_fixed +
12014 GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
12015 player->post_delay_counter =
12016 graphic_info[special_graphic].post_delay_fixed +
12017 GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
12019 player->special_action_sleeping = special_action;
12022 if (player->anim_delay_counter > 0)
12024 player->action_waiting = player->special_action_sleeping;
12025 player->anim_delay_counter--;
12027 else if (player->post_delay_counter > 0)
12029 player->post_delay_counter--;
12033 else if (player->is_bored)
12035 if (player->num_special_action_bored > 0)
12037 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
12039 int special_action =
12040 ACTION_BORING_1 + GetSimpleRandom(player->num_special_action_bored);
12041 int special_graphic =
12042 el_act_dir2img(player->artwork_element, special_action, move_dir);
12044 player->anim_delay_counter =
12045 graphic_info[special_graphic].anim_delay_fixed +
12046 GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
12047 player->post_delay_counter =
12048 graphic_info[special_graphic].post_delay_fixed +
12049 GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
12051 player->special_action_bored = special_action;
12054 if (player->anim_delay_counter > 0)
12056 player->action_waiting = player->special_action_bored;
12057 player->anim_delay_counter--;
12059 else if (player->post_delay_counter > 0)
12061 player->post_delay_counter--;
12066 else if (last_waiting) /* waiting -> not waiting */
12068 player->is_waiting = FALSE;
12069 player->is_bored = FALSE;
12070 player->is_sleeping = FALSE;
12072 player->frame_counter_bored = -1;
12073 player->frame_counter_sleeping = -1;
12075 player->anim_delay_counter = 0;
12076 player->post_delay_counter = 0;
12078 player->dir_waiting = player->MovDir;
12079 player->action_waiting = ACTION_DEFAULT;
12081 player->special_action_bored = ACTION_DEFAULT;
12082 player->special_action_sleeping = ACTION_DEFAULT;
12086 static void CheckSingleStepMode(struct PlayerInfo *player)
12088 if (tape.single_step && tape.recording && !tape.pausing)
12090 /* as it is called "single step mode", just return to pause mode when the
12091 player stopped moving after one tile (or never starts moving at all) */
12092 if (!player->is_moving && !player->is_pushing)
12094 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
12095 SnapField(player, 0, 0); /* stop snapping */
12100 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
12102 int left = player_action & JOY_LEFT;
12103 int right = player_action & JOY_RIGHT;
12104 int up = player_action & JOY_UP;
12105 int down = player_action & JOY_DOWN;
12106 int button1 = player_action & JOY_BUTTON_1;
12107 int button2 = player_action & JOY_BUTTON_2;
12108 int dx = (left ? -1 : right ? 1 : 0);
12109 int dy = (up ? -1 : down ? 1 : 0);
12111 if (!player->active || tape.pausing)
12117 SnapField(player, dx, dy);
12121 DropElement(player);
12123 MovePlayer(player, dx, dy);
12126 CheckSingleStepMode(player);
12128 SetPlayerWaiting(player, FALSE);
12130 return player_action;
12134 /* no actions for this player (no input at player's configured device) */
12136 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
12137 SnapField(player, 0, 0);
12138 CheckGravityMovementWhenNotMoving(player);
12140 if (player->MovPos == 0)
12141 SetPlayerWaiting(player, TRUE);
12143 if (player->MovPos == 0) /* needed for tape.playing */
12144 player->is_moving = FALSE;
12146 player->is_dropping = FALSE;
12147 player->is_dropping_pressed = FALSE;
12148 player->drop_pressed_delay = 0;
12150 CheckSingleStepMode(player);
12156 static void CheckLevelTime()
12160 /* !!! SAME CODE AS IN "GameActions()" -- FIX THIS !!! */
12161 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
12163 if (level.native_em_level->lev->home == 0) /* all players at home */
12165 PlayerWins(local_player);
12167 AllPlayersGone = TRUE;
12169 level.native_em_level->lev->home = -1;
12172 if (level.native_em_level->ply[0]->alive == 0 &&
12173 level.native_em_level->ply[1]->alive == 0 &&
12174 level.native_em_level->ply[2]->alive == 0 &&
12175 level.native_em_level->ply[3]->alive == 0) /* all dead */
12176 AllPlayersGone = TRUE;
12178 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
12180 if (game_sp.LevelSolved &&
12181 !game_sp.GameOver) /* game won */
12183 PlayerWins(local_player);
12185 game_sp.GameOver = TRUE;
12187 AllPlayersGone = TRUE;
12190 if (game_sp.GameOver) /* game lost */
12191 AllPlayersGone = TRUE;
12194 if (TimeFrames >= FRAMES_PER_SECOND)
12199 for (i = 0; i < MAX_PLAYERS; i++)
12201 struct PlayerInfo *player = &stored_player[i];
12203 if (SHIELD_ON(player))
12205 player->shield_normal_time_left--;
12207 if (player->shield_deadly_time_left > 0)
12208 player->shield_deadly_time_left--;
12212 if (!local_player->LevelSolved && !level.use_step_counter)
12220 if (TimeLeft <= 10 && setup.time_limit)
12221 PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
12224 /* this does not make sense: game_panel_controls[GAME_PANEL_TIME].value
12225 is reset from other values in UpdateGameDoorValues() -- FIX THIS */
12227 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
12229 /* (already called by UpdateAndDisplayGameControlValues() below) */
12230 // DisplayGameControlValues();
12232 DrawGameValue_Time(TimeLeft);
12235 if (!TimeLeft && setup.time_limit)
12237 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
12238 level.native_em_level->lev->killed_out_of_time = TRUE;
12240 for (i = 0; i < MAX_PLAYERS; i++)
12241 KillPlayer(&stored_player[i]);
12245 else if (game.no_time_limit && !AllPlayersGone) /* level w/o time limit */
12247 game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
12249 /* (already called by UpdateAndDisplayGameControlValues() below) */
12250 // DisplayGameControlValues();
12253 else if (game.no_time_limit && !AllPlayersGone) /* level w/o time limit */
12254 DrawGameValue_Time(TimePlayed);
12257 level.native_em_level->lev->time =
12258 (game.no_time_limit ? TimePlayed : TimeLeft);
12261 if (tape.recording || tape.playing)
12262 DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
12266 UpdateAndDisplayGameControlValues();
12268 UpdateGameDoorValues();
12269 DrawGameDoorValues();
12273 void AdvanceFrameAndPlayerCounters(int player_nr)
12277 /* advance frame counters (global frame counter and time frame counter) */
12281 /* advance player counters (counters for move delay, move animation etc.) */
12282 for (i = 0; i < MAX_PLAYERS; i++)
12284 boolean advance_player_counters = (player_nr == -1 || player_nr == i);
12285 int move_delay_value = stored_player[i].move_delay_value;
12286 int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
12288 if (!advance_player_counters) /* not all players may be affected */
12291 #if USE_NEW_PLAYER_ANIM
12292 if (move_frames == 0) /* less than one move per game frame */
12294 int stepsize = TILEX / move_delay_value;
12295 int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
12296 int count = (stored_player[i].is_moving ?
12297 ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
12299 if (count % delay == 0)
12304 stored_player[i].Frame += move_frames;
12306 if (stored_player[i].MovPos != 0)
12307 stored_player[i].StepFrame += move_frames;
12309 if (stored_player[i].move_delay > 0)
12310 stored_player[i].move_delay--;
12312 /* due to bugs in previous versions, counter must count up, not down */
12313 if (stored_player[i].push_delay != -1)
12314 stored_player[i].push_delay++;
12316 if (stored_player[i].drop_delay > 0)
12317 stored_player[i].drop_delay--;
12319 if (stored_player[i].is_dropping_pressed)
12320 stored_player[i].drop_pressed_delay++;
12324 void StartGameActions(boolean init_network_game, boolean record_tape,
12327 unsigned int new_random_seed = InitRND(random_seed);
12330 TapeStartRecording(new_random_seed);
12332 #if defined(NETWORK_AVALIABLE)
12333 if (init_network_game)
12335 SendToServer_StartPlaying();
12346 static unsigned int game_frame_delay = 0;
12347 unsigned int game_frame_delay_value;
12348 byte *recorded_player_action;
12349 byte summarized_player_action = 0;
12350 byte tape_action[MAX_PLAYERS];
12353 /* detect endless loops, caused by custom element programming */
12354 if (recursion_loop_detected && recursion_loop_depth == 0)
12356 char *message = getStringCat3("Internal Error! Element ",
12357 EL_NAME(recursion_loop_element),
12358 " caused endless loop! Quit the game?");
12360 Error(ERR_WARN, "element '%s' caused endless loop in game engine",
12361 EL_NAME(recursion_loop_element));
12363 RequestQuitGameExt(FALSE, level_editor_test_game, message);
12365 recursion_loop_detected = FALSE; /* if game should be continued */
12372 if (game.restart_level)
12373 StartGameActions(options.network, setup.autorecord, level.random_seed);
12375 /* !!! SAME CODE AS IN "CheckLevelTime()" -- FIX THIS !!! */
12376 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
12378 if (level.native_em_level->lev->home == 0) /* all players at home */
12380 PlayerWins(local_player);
12382 AllPlayersGone = TRUE;
12384 level.native_em_level->lev->home = -1;
12387 if (level.native_em_level->ply[0]->alive == 0 &&
12388 level.native_em_level->ply[1]->alive == 0 &&
12389 level.native_em_level->ply[2]->alive == 0 &&
12390 level.native_em_level->ply[3]->alive == 0) /* all dead */
12391 AllPlayersGone = TRUE;
12393 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
12395 if (game_sp.LevelSolved &&
12396 !game_sp.GameOver) /* game won */
12398 PlayerWins(local_player);
12400 game_sp.GameOver = TRUE;
12402 AllPlayersGone = TRUE;
12405 if (game_sp.GameOver) /* game lost */
12406 AllPlayersGone = TRUE;
12409 if (local_player->LevelSolved && !local_player->LevelSolved_GameEnd)
12412 if (AllPlayersGone && !TAPE_IS_STOPPED(tape))
12415 if (game_status != GAME_MODE_PLAYING) /* status might have changed */
12418 game_frame_delay_value =
12419 (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
12421 if (tape.playing && tape.warp_forward && !tape.pausing)
12422 game_frame_delay_value = 0;
12424 /* ---------- main game synchronization point ---------- */
12426 WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
12428 if (network_playing && !network_player_action_received)
12430 /* try to get network player actions in time */
12432 #if defined(NETWORK_AVALIABLE)
12433 /* last chance to get network player actions without main loop delay */
12434 HandleNetworking();
12437 /* game was quit by network peer */
12438 if (game_status != GAME_MODE_PLAYING)
12441 if (!network_player_action_received)
12442 return; /* failed to get network player actions in time */
12444 /* do not yet reset "network_player_action_received" (for tape.pausing) */
12450 /* at this point we know that we really continue executing the game */
12452 network_player_action_received = FALSE;
12454 /* when playing tape, read previously recorded player input from tape data */
12455 recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
12458 /* TapePlayAction() may return NULL when toggling to "pause before death" */
12463 if (tape.set_centered_player)
12465 game.centered_player_nr_next = tape.centered_player_nr_next;
12466 game.set_centered_player = TRUE;
12469 for (i = 0; i < MAX_PLAYERS; i++)
12471 summarized_player_action |= stored_player[i].action;
12474 if (!network_playing && (game.team_mode || tape.playing))
12475 stored_player[i].effective_action = stored_player[i].action;
12477 if (!network_playing)
12478 stored_player[i].effective_action = stored_player[i].action;
12482 #if defined(NETWORK_AVALIABLE)
12483 if (network_playing)
12484 SendToServer_MovePlayer(summarized_player_action);
12487 if (!options.network && !game.team_mode)
12488 local_player->effective_action = summarized_player_action;
12490 if (tape.recording &&
12492 setup.input_on_focus &&
12493 game.centered_player_nr != -1)
12495 for (i = 0; i < MAX_PLAYERS; i++)
12496 stored_player[i].effective_action =
12497 (i == game.centered_player_nr ? summarized_player_action : 0);
12500 if (recorded_player_action != NULL)
12501 for (i = 0; i < MAX_PLAYERS; i++)
12502 stored_player[i].effective_action = recorded_player_action[i];
12504 for (i = 0; i < MAX_PLAYERS; i++)
12506 tape_action[i] = stored_player[i].effective_action;
12509 /* (this may happen in the RND game engine if a player was not present on
12510 the playfield on level start, but appeared later from a custom element */
12511 if (tape.recording &&
12514 !tape.player_participates[i])
12515 tape.player_participates[i] = TRUE;
12517 /* (this can only happen in the R'n'D game engine) */
12518 if (tape.recording && tape_action[i] && !tape.player_participates[i])
12519 tape.player_participates[i] = TRUE; /* player just appeared from CE */
12523 /* only record actions from input devices, but not programmed actions */
12524 if (tape.recording)
12525 TapeRecordAction(tape_action);
12527 #if USE_NEW_PLAYER_ASSIGNMENTS
12529 if (game.team_mode)
12532 byte mapped_action[MAX_PLAYERS];
12534 #if DEBUG_PLAYER_ACTIONS
12536 for (i = 0; i < MAX_PLAYERS; i++)
12537 printf(" %d, ", stored_player[i].effective_action);
12540 for (i = 0; i < MAX_PLAYERS; i++)
12541 mapped_action[i] = stored_player[map_player_action[i]].effective_action;
12543 for (i = 0; i < MAX_PLAYERS; i++)
12544 stored_player[i].effective_action = mapped_action[i];
12546 #if DEBUG_PLAYER_ACTIONS
12548 for (i = 0; i < MAX_PLAYERS; i++)
12549 printf(" %d, ", stored_player[i].effective_action);
12553 #if DEBUG_PLAYER_ACTIONS
12557 for (i = 0; i < MAX_PLAYERS; i++)
12558 printf(" %d, ", stored_player[i].effective_action);
12565 printf("::: summarized_player_action == %d\n",
12566 local_player->effective_action);
12573 #if DEBUG_INIT_PLAYER
12576 printf("Player status (final):\n");
12578 for (i = 0; i < MAX_PLAYERS; i++)
12580 struct PlayerInfo *player = &stored_player[i];
12582 printf("- player %d: present == %d, connected == %d, active == %d",
12588 if (local_player == player)
12589 printf(" (local player)");
12599 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
12601 GameActions_EM_Main();
12603 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
12605 GameActions_SP_Main();
12613 void GameActions_EM_Main()
12615 byte effective_action[MAX_PLAYERS];
12616 boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
12619 for (i = 0; i < MAX_PLAYERS; i++)
12620 effective_action[i] = stored_player[i].effective_action;
12622 GameActions_EM(effective_action, warp_mode);
12626 AdvanceFrameAndPlayerCounters(-1); /* advance counters for all players */
12629 void GameActions_SP_Main()
12631 byte effective_action[MAX_PLAYERS];
12632 boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
12635 for (i = 0; i < MAX_PLAYERS; i++)
12636 effective_action[i] = stored_player[i].effective_action;
12638 GameActions_SP(effective_action, warp_mode);
12642 AdvanceFrameAndPlayerCounters(-1); /* advance counters for all players */
12645 void GameActions_RND()
12647 int magic_wall_x = 0, magic_wall_y = 0;
12648 int i, x, y, element, graphic;
12650 InitPlayfieldScanModeVars();
12652 #if USE_ONE_MORE_CHANGE_PER_FRAME
12653 if (game.engine_version >= VERSION_IDENT(3,2,0,7))
12655 SCAN_PLAYFIELD(x, y)
12657 ChangeCount[x][y] = 0;
12658 ChangeEvent[x][y] = -1;
12663 if (game.set_centered_player)
12665 boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
12667 /* switching to "all players" only possible if all players fit to screen */
12668 if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
12670 game.centered_player_nr_next = game.centered_player_nr;
12671 game.set_centered_player = FALSE;
12674 /* do not switch focus to non-existing (or non-active) player */
12675 if (game.centered_player_nr_next >= 0 &&
12676 !stored_player[game.centered_player_nr_next].active)
12678 game.centered_player_nr_next = game.centered_player_nr;
12679 game.set_centered_player = FALSE;
12683 if (game.set_centered_player &&
12684 ScreenMovPos == 0) /* screen currently aligned at tile position */
12688 if (game.centered_player_nr_next == -1)
12690 setScreenCenteredToAllPlayers(&sx, &sy);
12694 sx = stored_player[game.centered_player_nr_next].jx;
12695 sy = stored_player[game.centered_player_nr_next].jy;
12698 game.centered_player_nr = game.centered_player_nr_next;
12699 game.set_centered_player = FALSE;
12701 DrawRelocateScreen(0, 0, sx, sy, MV_NONE, TRUE, setup.quick_switch);
12702 DrawGameDoorValues();
12705 for (i = 0; i < MAX_PLAYERS; i++)
12707 int actual_player_action = stored_player[i].effective_action;
12710 /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
12711 - rnd_equinox_tetrachloride 048
12712 - rnd_equinox_tetrachloride_ii 096
12713 - rnd_emanuel_schmieg 002
12714 - doctor_sloan_ww 001, 020
12716 if (stored_player[i].MovPos == 0)
12717 CheckGravityMovement(&stored_player[i]);
12720 /* overwrite programmed action with tape action */
12721 if (stored_player[i].programmed_action)
12722 actual_player_action = stored_player[i].programmed_action;
12724 PlayerActions(&stored_player[i], actual_player_action);
12726 ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
12729 ScrollScreen(NULL, SCROLL_GO_ON);
12731 /* for backwards compatibility, the following code emulates a fixed bug that
12732 occured when pushing elements (causing elements that just made their last
12733 pushing step to already (if possible) make their first falling step in the
12734 same game frame, which is bad); this code is also needed to use the famous
12735 "spring push bug" which is used in older levels and might be wanted to be
12736 used also in newer levels, but in this case the buggy pushing code is only
12737 affecting the "spring" element and no other elements */
12739 if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
12741 for (i = 0; i < MAX_PLAYERS; i++)
12743 struct PlayerInfo *player = &stored_player[i];
12744 int x = player->jx;
12745 int y = player->jy;
12747 if (player->active && player->is_pushing && player->is_moving &&
12749 (game.engine_version < VERSION_IDENT(2,2,0,7) ||
12750 Feld[x][y] == EL_SPRING))
12752 ContinueMoving(x, y);
12754 /* continue moving after pushing (this is actually a bug) */
12755 if (!IS_MOVING(x, y))
12756 Stop[x][y] = FALSE;
12762 debug_print_timestamp(0, "start main loop profiling");
12765 SCAN_PLAYFIELD(x, y)
12767 ChangeCount[x][y] = 0;
12768 ChangeEvent[x][y] = -1;
12770 /* this must be handled before main playfield loop */
12771 if (Feld[x][y] == EL_PLAYER_IS_LEAVING)
12774 if (MovDelay[x][y] <= 0)
12778 #if USE_NEW_SNAP_DELAY
12779 if (Feld[x][y] == EL_ELEMENT_SNAPPING)
12782 if (MovDelay[x][y] <= 0)
12785 TEST_DrawLevelField(x, y);
12787 TestIfElementTouchesCustomElement(x, y); /* for empty space */
12793 if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
12795 printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
12796 printf("GameActions(): This should never happen!\n");
12798 ChangePage[x][y] = -1;
12802 Stop[x][y] = FALSE;
12803 if (WasJustMoving[x][y] > 0)
12804 WasJustMoving[x][y]--;
12805 if (WasJustFalling[x][y] > 0)
12806 WasJustFalling[x][y]--;
12807 if (CheckCollision[x][y] > 0)
12808 CheckCollision[x][y]--;
12809 if (CheckImpact[x][y] > 0)
12810 CheckImpact[x][y]--;
12814 /* reset finished pushing action (not done in ContinueMoving() to allow
12815 continuous pushing animation for elements with zero push delay) */
12816 if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
12818 ResetGfxAnimation(x, y);
12819 TEST_DrawLevelField(x, y);
12823 if (IS_BLOCKED(x, y))
12827 Blocked2Moving(x, y, &oldx, &oldy);
12828 if (!IS_MOVING(oldx, oldy))
12830 printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
12831 printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
12832 printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
12833 printf("GameActions(): This should never happen!\n");
12840 debug_print_timestamp(0, "- time for pre-main loop:");
12843 #if 0 // -------------------- !!! TEST ONLY !!! --------------------
12844 SCAN_PLAYFIELD(x, y)
12846 element = Feld[x][y];
12847 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12852 int element2 = element;
12853 int graphic2 = graphic;
12855 int element2 = Feld[x][y];
12856 int graphic2 = el_act_dir2img(element2, GfxAction[x][y], GfxDir[x][y]);
12858 int last_gfx_frame = GfxFrame[x][y];
12860 if (graphic_info[graphic2].anim_global_sync)
12861 GfxFrame[x][y] = FrameCounter;
12862 else if (ANIM_MODE(graphic2) == ANIM_CE_VALUE)
12863 GfxFrame[x][y] = CustomValue[x][y];
12864 else if (ANIM_MODE(graphic2) == ANIM_CE_SCORE)
12865 GfxFrame[x][y] = element_info[element2].collect_score;
12866 else if (ANIM_MODE(graphic2) == ANIM_CE_DELAY)
12867 GfxFrame[x][y] = ChangeDelay[x][y];
12869 if (redraw && GfxFrame[x][y] != last_gfx_frame)
12870 DrawLevelGraphicAnimation(x, y, graphic2);
12873 ResetGfxFrame(x, y, TRUE);
12877 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
12878 IS_NEXT_FRAME(GfxFrame[x][y], graphic))
12879 ResetRandomAnimationValue(x, y);
12883 SetRandomAnimationValue(x, y);
12887 PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
12890 #endif // -------------------- !!! TEST ONLY !!! --------------------
12893 debug_print_timestamp(0, "- time for TEST loop: -->");
12896 SCAN_PLAYFIELD(x, y)
12898 element = Feld[x][y];
12899 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12901 ResetGfxFrame(x, y, TRUE);
12903 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
12904 IS_NEXT_FRAME(GfxFrame[x][y], graphic))
12905 ResetRandomAnimationValue(x, y);
12907 SetRandomAnimationValue(x, y);
12909 PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
12911 if (IS_INACTIVE(element))
12913 if (IS_ANIMATED(graphic))
12914 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12919 /* this may take place after moving, so 'element' may have changed */
12920 if (IS_CHANGING(x, y) &&
12921 (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
12923 int page = element_info[element].event_page_nr[CE_DELAY];
12926 HandleElementChange(x, y, page);
12928 if (CAN_CHANGE(element))
12929 HandleElementChange(x, y, page);
12931 if (HAS_ACTION(element))
12932 ExecuteCustomElementAction(x, y, element, page);
12935 element = Feld[x][y];
12936 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12939 #if 0 // ---------------------------------------------------------------------
12941 if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
12945 element = Feld[x][y];
12946 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12948 if (IS_ANIMATED(graphic) &&
12949 !IS_MOVING(x, y) &&
12951 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12953 if (IS_GEM(element) || element == EL_SP_INFOTRON)
12954 TEST_DrawTwinkleOnField(x, y);
12956 else if (IS_MOVING(x, y))
12957 ContinueMoving(x, y);
12964 case EL_EM_EXIT_OPEN:
12965 case EL_SP_EXIT_OPEN:
12966 case EL_STEEL_EXIT_OPEN:
12967 case EL_EM_STEEL_EXIT_OPEN:
12968 case EL_SP_TERMINAL:
12969 case EL_SP_TERMINAL_ACTIVE:
12970 case EL_EXTRA_TIME:
12971 case EL_SHIELD_NORMAL:
12972 case EL_SHIELD_DEADLY:
12973 if (IS_ANIMATED(graphic))
12974 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12977 case EL_DYNAMITE_ACTIVE:
12978 case EL_EM_DYNAMITE_ACTIVE:
12979 case EL_DYNABOMB_PLAYER_1_ACTIVE:
12980 case EL_DYNABOMB_PLAYER_2_ACTIVE:
12981 case EL_DYNABOMB_PLAYER_3_ACTIVE:
12982 case EL_DYNABOMB_PLAYER_4_ACTIVE:
12983 case EL_SP_DISK_RED_ACTIVE:
12984 CheckDynamite(x, y);
12987 case EL_AMOEBA_GROWING:
12988 AmoebeWaechst(x, y);
12991 case EL_AMOEBA_SHRINKING:
12992 AmoebaDisappearing(x, y);
12995 #if !USE_NEW_AMOEBA_CODE
12996 case EL_AMOEBA_WET:
12997 case EL_AMOEBA_DRY:
12998 case EL_AMOEBA_FULL:
13000 case EL_EMC_DRIPPER:
13001 AmoebeAbleger(x, y);
13005 case EL_GAME_OF_LIFE:
13010 case EL_EXIT_CLOSED:
13014 case EL_EM_EXIT_CLOSED:
13018 case EL_STEEL_EXIT_CLOSED:
13019 CheckExitSteel(x, y);
13022 case EL_EM_STEEL_EXIT_CLOSED:
13023 CheckExitSteelEM(x, y);
13026 case EL_SP_EXIT_CLOSED:
13030 case EL_EXPANDABLE_WALL_GROWING:
13031 case EL_EXPANDABLE_STEELWALL_GROWING:
13032 MauerWaechst(x, y);
13035 case EL_EXPANDABLE_WALL:
13036 case EL_EXPANDABLE_WALL_HORIZONTAL:
13037 case EL_EXPANDABLE_WALL_VERTICAL:
13038 case EL_EXPANDABLE_WALL_ANY:
13039 case EL_BD_EXPANDABLE_WALL:
13040 MauerAbleger(x, y);
13043 case EL_EXPANDABLE_STEELWALL_HORIZONTAL:
13044 case EL_EXPANDABLE_STEELWALL_VERTICAL:
13045 case EL_EXPANDABLE_STEELWALL_ANY:
13046 MauerAblegerStahl(x, y);
13050 CheckForDragon(x, y);
13056 case EL_ELEMENT_SNAPPING:
13057 case EL_DIAGONAL_SHRINKING:
13058 case EL_DIAGONAL_GROWING:
13061 el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
13063 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
13068 if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
13069 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
13074 #else // ---------------------------------------------------------------------
13076 if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
13080 element = Feld[x][y];
13081 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
13083 if (IS_ANIMATED(graphic) &&
13084 !IS_MOVING(x, y) &&
13086 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
13088 if (IS_GEM(element) || element == EL_SP_INFOTRON)
13089 TEST_DrawTwinkleOnField(x, y);
13091 else if ((element == EL_ACID ||
13092 element == EL_EXIT_OPEN ||
13093 element == EL_EM_EXIT_OPEN ||
13094 element == EL_SP_EXIT_OPEN ||
13095 element == EL_STEEL_EXIT_OPEN ||
13096 element == EL_EM_STEEL_EXIT_OPEN ||
13097 element == EL_SP_TERMINAL ||
13098 element == EL_SP_TERMINAL_ACTIVE ||
13099 element == EL_EXTRA_TIME ||
13100 element == EL_SHIELD_NORMAL ||
13101 element == EL_SHIELD_DEADLY) &&
13102 IS_ANIMATED(graphic))
13103 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
13104 else if (IS_MOVING(x, y))
13105 ContinueMoving(x, y);
13106 else if (IS_ACTIVE_BOMB(element))
13107 CheckDynamite(x, y);
13108 else if (element == EL_AMOEBA_GROWING)
13109 AmoebeWaechst(x, y);
13110 else if (element == EL_AMOEBA_SHRINKING)
13111 AmoebaDisappearing(x, y);
13113 #if !USE_NEW_AMOEBA_CODE
13114 else if (IS_AMOEBALIVE(element))
13115 AmoebeAbleger(x, y);
13118 else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
13120 else if (element == EL_EXIT_CLOSED)
13122 else if (element == EL_EM_EXIT_CLOSED)
13124 else if (element == EL_STEEL_EXIT_CLOSED)
13125 CheckExitSteel(x, y);
13126 else if (element == EL_EM_STEEL_EXIT_CLOSED)
13127 CheckExitSteelEM(x, y);
13128 else if (element == EL_SP_EXIT_CLOSED)
13130 else if (element == EL_EXPANDABLE_WALL_GROWING ||
13131 element == EL_EXPANDABLE_STEELWALL_GROWING)
13132 MauerWaechst(x, y);
13133 else if (element == EL_EXPANDABLE_WALL ||
13134 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
13135 element == EL_EXPANDABLE_WALL_VERTICAL ||
13136 element == EL_EXPANDABLE_WALL_ANY ||
13137 element == EL_BD_EXPANDABLE_WALL)
13138 MauerAbleger(x, y);
13139 else if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
13140 element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
13141 element == EL_EXPANDABLE_STEELWALL_ANY)
13142 MauerAblegerStahl(x, y);
13143 else if (element == EL_FLAMES)
13144 CheckForDragon(x, y);
13145 else if (element == EL_EXPLOSION)
13146 ; /* drawing of correct explosion animation is handled separately */
13147 else if (element == EL_ELEMENT_SNAPPING ||
13148 element == EL_DIAGONAL_SHRINKING ||
13149 element == EL_DIAGONAL_GROWING)
13151 graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
13153 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
13155 else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
13156 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
13158 #endif // ---------------------------------------------------------------------
13160 if (IS_BELT_ACTIVE(element))
13161 PlayLevelSoundAction(x, y, ACTION_ACTIVE);
13163 if (game.magic_wall_active)
13165 int jx = local_player->jx, jy = local_player->jy;
13167 /* play the element sound at the position nearest to the player */
13168 if ((element == EL_MAGIC_WALL_FULL ||
13169 element == EL_MAGIC_WALL_ACTIVE ||
13170 element == EL_MAGIC_WALL_EMPTYING ||
13171 element == EL_BD_MAGIC_WALL_FULL ||
13172 element == EL_BD_MAGIC_WALL_ACTIVE ||
13173 element == EL_BD_MAGIC_WALL_EMPTYING ||
13174 element == EL_DC_MAGIC_WALL_FULL ||
13175 element == EL_DC_MAGIC_WALL_ACTIVE ||
13176 element == EL_DC_MAGIC_WALL_EMPTYING) &&
13177 ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
13186 debug_print_timestamp(0, "- time for MAIN loop: -->");
13189 #if USE_NEW_AMOEBA_CODE
13190 /* new experimental amoeba growth stuff */
13191 if (!(FrameCounter % 8))
13193 static unsigned int random = 1684108901;
13195 for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
13197 x = RND(lev_fieldx);
13198 y = RND(lev_fieldy);
13199 element = Feld[x][y];
13201 if (!IS_PLAYER(x,y) &&
13202 (element == EL_EMPTY ||
13203 CAN_GROW_INTO(element) ||
13204 element == EL_QUICKSAND_EMPTY ||
13205 element == EL_QUICKSAND_FAST_EMPTY ||
13206 element == EL_ACID_SPLASH_LEFT ||
13207 element == EL_ACID_SPLASH_RIGHT))
13209 if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
13210 (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
13211 (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
13212 (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
13213 Feld[x][y] = EL_AMOEBA_DROP;
13216 random = random * 129 + 1;
13222 if (game.explosions_delayed)
13225 game.explosions_delayed = FALSE;
13227 SCAN_PLAYFIELD(x, y)
13229 element = Feld[x][y];
13231 if (ExplodeField[x][y])
13232 Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
13233 else if (element == EL_EXPLOSION)
13234 Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
13236 ExplodeField[x][y] = EX_TYPE_NONE;
13239 game.explosions_delayed = TRUE;
13242 if (game.magic_wall_active)
13244 if (!(game.magic_wall_time_left % 4))
13246 int element = Feld[magic_wall_x][magic_wall_y];
13248 if (element == EL_BD_MAGIC_WALL_FULL ||
13249 element == EL_BD_MAGIC_WALL_ACTIVE ||
13250 element == EL_BD_MAGIC_WALL_EMPTYING)
13251 PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
13252 else if (element == EL_DC_MAGIC_WALL_FULL ||
13253 element == EL_DC_MAGIC_WALL_ACTIVE ||
13254 element == EL_DC_MAGIC_WALL_EMPTYING)
13255 PlayLevelSound(magic_wall_x, magic_wall_y, SND_DC_MAGIC_WALL_ACTIVE);
13257 PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
13260 if (game.magic_wall_time_left > 0)
13262 game.magic_wall_time_left--;
13264 if (!game.magic_wall_time_left)
13266 SCAN_PLAYFIELD(x, y)
13268 element = Feld[x][y];
13270 if (element == EL_MAGIC_WALL_ACTIVE ||
13271 element == EL_MAGIC_WALL_FULL)
13273 Feld[x][y] = EL_MAGIC_WALL_DEAD;
13274 TEST_DrawLevelField(x, y);
13276 else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
13277 element == EL_BD_MAGIC_WALL_FULL)
13279 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
13280 TEST_DrawLevelField(x, y);
13282 else if (element == EL_DC_MAGIC_WALL_ACTIVE ||
13283 element == EL_DC_MAGIC_WALL_FULL)
13285 Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
13286 TEST_DrawLevelField(x, y);
13290 game.magic_wall_active = FALSE;
13295 if (game.light_time_left > 0)
13297 game.light_time_left--;
13299 if (game.light_time_left == 0)
13300 RedrawAllLightSwitchesAndInvisibleElements();
13303 if (game.timegate_time_left > 0)
13305 game.timegate_time_left--;
13307 if (game.timegate_time_left == 0)
13308 CloseAllOpenTimegates();
13311 if (game.lenses_time_left > 0)
13313 game.lenses_time_left--;
13315 if (game.lenses_time_left == 0)
13316 RedrawAllInvisibleElementsForLenses();
13319 if (game.magnify_time_left > 0)
13321 game.magnify_time_left--;
13323 if (game.magnify_time_left == 0)
13324 RedrawAllInvisibleElementsForMagnifier();
13327 for (i = 0; i < MAX_PLAYERS; i++)
13329 struct PlayerInfo *player = &stored_player[i];
13331 if (SHIELD_ON(player))
13333 if (player->shield_deadly_time_left)
13334 PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
13335 else if (player->shield_normal_time_left)
13336 PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
13340 #if USE_DELAYED_GFX_REDRAW
13341 SCAN_PLAYFIELD(x, y)
13344 if (GfxRedraw[x][y] != GFX_REDRAW_NONE)
13346 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)) &&
13347 GfxRedraw[x][y] != GFX_REDRAW_NONE)
13350 /* !!! PROBLEM: THIS REDRAWS THE PLAYFIELD _AFTER_ THE SCAN, BUT TILES
13351 !!! MAY HAVE CHANGED AFTER BEING DRAWN DURING PLAYFIELD SCAN !!! */
13353 if (GfxRedraw[x][y] & GFX_REDRAW_TILE)
13354 DrawLevelField(x, y);
13356 if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED)
13357 DrawLevelFieldCrumbled(x, y);
13359 if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS)
13360 DrawLevelFieldCrumbledNeighbours(x, y);
13362 if (GfxRedraw[x][y] & GFX_REDRAW_TILE_TWINKLED)
13363 DrawTwinkleOnField(x, y);
13366 GfxRedraw[x][y] = GFX_REDRAW_NONE;
13373 PlayAllPlayersSound();
13375 if (options.debug) /* calculate frames per second */
13377 static unsigned int fps_counter = 0;
13378 static int fps_frames = 0;
13379 unsigned int fps_delay_ms = Counter() - fps_counter;
13383 if (fps_delay_ms >= 500) /* calculate fps every 0.5 seconds */
13385 global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
13388 fps_counter = Counter();
13391 redraw_mask |= REDRAW_FPS;
13394 AdvanceFrameAndPlayerCounters(-1); /* advance counters for all players */
13396 if (local_player->show_envelope != 0 && local_player->MovPos == 0)
13398 ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
13400 local_player->show_envelope = 0;
13404 debug_print_timestamp(0, "stop main loop profiling ");
13405 printf("----------------------------------------------------------\n");
13408 /* use random number generator in every frame to make it less predictable */
13409 if (game.engine_version >= VERSION_IDENT(3,1,1,0))
13413 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
13415 int min_x = x, min_y = y, max_x = x, max_y = y;
13418 for (i = 0; i < MAX_PLAYERS; i++)
13420 int jx = stored_player[i].jx, jy = stored_player[i].jy;
13422 if (!stored_player[i].active || &stored_player[i] == player)
13425 min_x = MIN(min_x, jx);
13426 min_y = MIN(min_y, jy);
13427 max_x = MAX(max_x, jx);
13428 max_y = MAX(max_y, jy);
13431 return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
13434 static boolean AllPlayersInVisibleScreen()
13438 for (i = 0; i < MAX_PLAYERS; i++)
13440 int jx = stored_player[i].jx, jy = stored_player[i].jy;
13442 if (!stored_player[i].active)
13445 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
13452 void ScrollLevel(int dx, int dy)
13455 /* (directly solved in BlitBitmap() now) */
13456 static Bitmap *bitmap_db_field2 = NULL;
13457 int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
13464 /* !!! THIS IS APPARENTLY WRONG FOR PLAYER RELOCATION !!! */
13465 /* only horizontal XOR vertical scroll direction allowed */
13466 if ((dx == 0 && dy == 0) || (dx != 0 && dy != 0))
13471 /* (directly solved in BlitBitmap() now) */
13472 if (bitmap_db_field2 == NULL)
13473 bitmap_db_field2 = CreateBitmap(FXSIZE, FYSIZE, DEFAULT_DEPTH);
13475 /* needed when blitting directly to same bitmap -- should not be needed with
13476 recent SDL libraries, but apparently does not work in 1.2.11 directly */
13477 BlitBitmap(drawto_field, bitmap_db_field2,
13478 FX + TILEX * (dx == -1) - softscroll_offset,
13479 FY + TILEY * (dy == -1) - softscroll_offset,
13480 SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
13481 SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
13482 FX + TILEX * (dx == 1) - softscroll_offset,
13483 FY + TILEY * (dy == 1) - softscroll_offset);
13484 BlitBitmap(bitmap_db_field2, drawto_field,
13485 FX + TILEX * (dx == 1) - softscroll_offset,
13486 FY + TILEY * (dy == 1) - softscroll_offset,
13487 SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
13488 SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
13489 FX + TILEX * (dx == 1) - softscroll_offset,
13490 FY + TILEY * (dy == 1) - softscroll_offset);
13495 /* !!! DOES NOT WORK FOR DIAGONAL PLAYER RELOCATION !!! */
13496 int xsize = (BX2 - BX1 + 1);
13497 int ysize = (BY2 - BY1 + 1);
13498 int start = (dx != 0 ? (dx == -1 ? BX1 : BX2) : (dy == -1 ? BY1 : BY2));
13499 int end = (dx != 0 ? (dx == -1 ? BX2 : BX1) : (dy == -1 ? BY2 : BY1));
13500 int step = (start < end ? +1 : -1);
13502 for (i = start; i != end; i += step)
13504 BlitBitmap(drawto_field, drawto_field,
13505 FX + TILEX * (dx != 0 ? i + step : 0),
13506 FY + TILEY * (dy != 0 ? i + step : 0),
13507 TILEX * (dx != 0 ? 1 : xsize),
13508 TILEY * (dy != 0 ? 1 : ysize),
13509 FX + TILEX * (dx != 0 ? i : 0),
13510 FY + TILEY * (dy != 0 ? i : 0));
13517 int softscroll_offset = (setup.soft_scrolling ? 2 * TILEX_VAR : 0);
13519 int softscroll_offset = (setup.soft_scrolling ? TILEX_VAR : 0);
13523 int softscroll_offset = (setup.soft_scrolling ? 2 * TILEX : 0);
13525 int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
13530 BlitBitmap(drawto_field, drawto_field,
13531 FX + TILEX_VAR * (dx == -1) - softscroll_offset,
13532 FY + TILEY_VAR * (dy == -1) - softscroll_offset,
13533 SXSIZE - TILEX_VAR * (dx != 0) + 2 * softscroll_offset,
13534 SYSIZE - TILEY_VAR * (dy != 0) + 2 * softscroll_offset,
13535 FX + TILEX_VAR * (dx == 1) - softscroll_offset,
13536 FY + TILEY_VAR * (dy == 1) - softscroll_offset);
13538 BlitBitmap(drawto_field, drawto_field,
13539 FX + TILEX * (dx == -1) - softscroll_offset,
13540 FY + TILEY * (dy == -1) - softscroll_offset,
13541 SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
13542 SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
13543 FX + TILEX * (dx == 1) - softscroll_offset,
13544 FY + TILEY * (dy == 1) - softscroll_offset);
13552 x = (dx == 1 ? BX1 : BX2);
13553 for (y = BY1; y <= BY2; y++)
13554 DrawScreenField(x, y);
13559 y = (dy == 1 ? BY1 : BY2);
13560 for (x = BX1; x <= BX2; x++)
13561 DrawScreenField(x, y);
13564 redraw_mask |= REDRAW_FIELD;
13567 static boolean canFallDown(struct PlayerInfo *player)
13569 int jx = player->jx, jy = player->jy;
13571 return (IN_LEV_FIELD(jx, jy + 1) &&
13572 (IS_FREE(jx, jy + 1) ||
13573 (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
13574 IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
13575 !IS_WALKABLE_INSIDE(Feld[jx][jy]));
13578 static boolean canPassField(int x, int y, int move_dir)
13580 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
13581 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
13582 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
13583 int nextx = x + dx;
13584 int nexty = y + dy;
13585 int element = Feld[x][y];
13587 return (IS_PASSABLE_FROM(element, opposite_dir) &&
13588 !CAN_MOVE(element) &&
13589 IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
13590 IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
13591 (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
13594 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
13596 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
13597 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
13598 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
13602 return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
13603 IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
13604 (IS_DIGGABLE(Feld[newx][newy]) ||
13605 IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
13606 canPassField(newx, newy, move_dir)));
13609 static void CheckGravityMovement(struct PlayerInfo *player)
13611 #if USE_PLAYER_GRAVITY
13612 if (player->gravity && !player->programmed_action)
13614 if (game.gravity && !player->programmed_action)
13617 int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
13618 int move_dir_vertical = player->effective_action & MV_VERTICAL;
13619 boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
13620 int jx = player->jx, jy = player->jy;
13621 boolean player_is_moving_to_valid_field =
13622 (!player_is_snapping &&
13623 (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
13624 canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
13625 boolean player_can_fall_down = canFallDown(player);
13627 if (player_can_fall_down &&
13628 !player_is_moving_to_valid_field)
13629 player->programmed_action = MV_DOWN;
13633 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
13635 return CheckGravityMovement(player);
13637 #if USE_PLAYER_GRAVITY
13638 if (player->gravity && !player->programmed_action)
13640 if (game.gravity && !player->programmed_action)
13643 int jx = player->jx, jy = player->jy;
13644 boolean field_under_player_is_free =
13645 (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
13646 boolean player_is_standing_on_valid_field =
13647 (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
13648 (IS_WALKABLE(Feld[jx][jy]) &&
13649 !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
13651 if (field_under_player_is_free && !player_is_standing_on_valid_field)
13652 player->programmed_action = MV_DOWN;
13657 MovePlayerOneStep()
13658 -----------------------------------------------------------------------------
13659 dx, dy: direction (non-diagonal) to try to move the player to
13660 real_dx, real_dy: direction as read from input device (can be diagonal)
13663 boolean MovePlayerOneStep(struct PlayerInfo *player,
13664 int dx, int dy, int real_dx, int real_dy)
13666 int jx = player->jx, jy = player->jy;
13667 int new_jx = jx + dx, new_jy = jy + dy;
13668 #if !USE_FIXED_DONT_RUN_INTO
13672 boolean player_can_move = !player->cannot_move;
13674 if (!player->active || (!dx && !dy))
13675 return MP_NO_ACTION;
13677 player->MovDir = (dx < 0 ? MV_LEFT :
13678 dx > 0 ? MV_RIGHT :
13680 dy > 0 ? MV_DOWN : MV_NONE);
13682 if (!IN_LEV_FIELD(new_jx, new_jy))
13683 return MP_NO_ACTION;
13685 if (!player_can_move)
13687 if (player->MovPos == 0)
13689 player->is_moving = FALSE;
13690 player->is_digging = FALSE;
13691 player->is_collecting = FALSE;
13692 player->is_snapping = FALSE;
13693 player->is_pushing = FALSE;
13698 if (!options.network && game.centered_player_nr == -1 &&
13699 !AllPlayersInSight(player, new_jx, new_jy))
13700 return MP_NO_ACTION;
13702 if (!options.network && !AllPlayersInSight(player, new_jx, new_jy))
13703 return MP_NO_ACTION;
13706 #if !USE_FIXED_DONT_RUN_INTO
13707 element = MovingOrBlocked2ElementIfNotLeaving(new_jx, new_jy);
13709 /* (moved to DigField()) */
13710 if (player_can_move && DONT_RUN_INTO(element))
13712 if (element == EL_ACID && dx == 0 && dy == 1)
13714 SplashAcid(new_jx, new_jy);
13715 Feld[jx][jy] = EL_PLAYER_1;
13716 InitMovingField(jx, jy, MV_DOWN);
13717 Store[jx][jy] = EL_ACID;
13718 ContinueMoving(jx, jy);
13719 BuryPlayer(player);
13722 TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
13728 can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
13729 if (can_move != MP_MOVING)
13732 /* check if DigField() has caused relocation of the player */
13733 if (player->jx != jx || player->jy != jy)
13734 return MP_NO_ACTION; /* <-- !!! CHECK THIS [-> MP_ACTION ?] !!! */
13736 StorePlayer[jx][jy] = 0;
13737 player->last_jx = jx;
13738 player->last_jy = jy;
13739 player->jx = new_jx;
13740 player->jy = new_jy;
13741 StorePlayer[new_jx][new_jy] = player->element_nr;
13743 if (player->move_delay_value_next != -1)
13745 player->move_delay_value = player->move_delay_value_next;
13746 player->move_delay_value_next = -1;
13750 (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
13752 player->step_counter++;
13754 PlayerVisit[jx][jy] = FrameCounter;
13756 #if USE_UFAST_PLAYER_EXIT_BUGFIX
13757 player->is_moving = TRUE;
13761 /* should better be called in MovePlayer(), but this breaks some tapes */
13762 ScrollPlayer(player, SCROLL_INIT);
13768 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
13770 int jx = player->jx, jy = player->jy;
13771 int old_jx = jx, old_jy = jy;
13772 int moved = MP_NO_ACTION;
13774 if (!player->active)
13779 if (player->MovPos == 0)
13781 player->is_moving = FALSE;
13782 player->is_digging = FALSE;
13783 player->is_collecting = FALSE;
13784 player->is_snapping = FALSE;
13785 player->is_pushing = FALSE;
13791 if (player->move_delay > 0)
13794 player->move_delay = -1; /* set to "uninitialized" value */
13796 /* store if player is automatically moved to next field */
13797 player->is_auto_moving = (player->programmed_action != MV_NONE);
13799 /* remove the last programmed player action */
13800 player->programmed_action = 0;
13802 if (player->MovPos)
13804 /* should only happen if pre-1.2 tape recordings are played */
13805 /* this is only for backward compatibility */
13807 int original_move_delay_value = player->move_delay_value;
13810 printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%d]\n",
13814 /* scroll remaining steps with finest movement resolution */
13815 player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
13817 while (player->MovPos)
13819 ScrollPlayer(player, SCROLL_GO_ON);
13820 ScrollScreen(NULL, SCROLL_GO_ON);
13822 AdvanceFrameAndPlayerCounters(player->index_nr);
13828 player->move_delay_value = original_move_delay_value;
13831 player->is_active = FALSE;
13833 if (player->last_move_dir & MV_HORIZONTAL)
13835 if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
13836 moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
13840 if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
13841 moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
13844 #if USE_FIXED_BORDER_RUNNING_GFX
13845 if (!moved && !player->is_active)
13847 player->is_moving = FALSE;
13848 player->is_digging = FALSE;
13849 player->is_collecting = FALSE;
13850 player->is_snapping = FALSE;
13851 player->is_pushing = FALSE;
13859 if (moved & MP_MOVING && !ScreenMovPos &&
13860 (player->index_nr == game.centered_player_nr ||
13861 game.centered_player_nr == -1))
13863 if (moved & MP_MOVING && !ScreenMovPos &&
13864 (player == local_player || !options.network))
13867 int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
13868 int offset = game.scroll_delay_value;
13870 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
13872 /* actual player has left the screen -- scroll in that direction */
13873 if (jx != old_jx) /* player has moved horizontally */
13874 scroll_x += (jx - old_jx);
13875 else /* player has moved vertically */
13876 scroll_y += (jy - old_jy);
13880 if (jx != old_jx) /* player has moved horizontally */
13882 if ((player->MovDir == MV_LEFT && scroll_x > jx - MIDPOSX + offset) ||
13883 (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
13884 scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
13886 /* don't scroll over playfield boundaries */
13887 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
13888 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
13890 /* don't scroll more than one field at a time */
13891 scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
13893 /* don't scroll against the player's moving direction */
13894 if ((player->MovDir == MV_LEFT && scroll_x > old_scroll_x) ||
13895 (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
13896 scroll_x = old_scroll_x;
13898 else /* player has moved vertically */
13900 if ((player->MovDir == MV_UP && scroll_y > jy - MIDPOSY + offset) ||
13901 (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
13902 scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
13904 /* don't scroll over playfield boundaries */
13905 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
13906 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
13908 /* don't scroll more than one field at a time */
13909 scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
13911 /* don't scroll against the player's moving direction */
13912 if ((player->MovDir == MV_UP && scroll_y > old_scroll_y) ||
13913 (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
13914 scroll_y = old_scroll_y;
13918 if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
13921 if (!options.network && game.centered_player_nr == -1 &&
13922 !AllPlayersInVisibleScreen())
13924 scroll_x = old_scroll_x;
13925 scroll_y = old_scroll_y;
13929 if (!options.network && !AllPlayersInVisibleScreen())
13931 scroll_x = old_scroll_x;
13932 scroll_y = old_scroll_y;
13937 ScrollScreen(player, SCROLL_INIT);
13938 ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
13943 player->StepFrame = 0;
13945 if (moved & MP_MOVING)
13947 if (old_jx != jx && old_jy == jy)
13948 player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
13949 else if (old_jx == jx && old_jy != jy)
13950 player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
13952 TEST_DrawLevelField(jx, jy); /* for "crumbled sand" */
13954 player->last_move_dir = player->MovDir;
13955 player->is_moving = TRUE;
13956 player->is_snapping = FALSE;
13957 player->is_switching = FALSE;
13958 player->is_dropping = FALSE;
13959 player->is_dropping_pressed = FALSE;
13960 player->drop_pressed_delay = 0;
13963 /* should better be called here than above, but this breaks some tapes */
13964 ScrollPlayer(player, SCROLL_INIT);
13969 CheckGravityMovementWhenNotMoving(player);
13971 player->is_moving = FALSE;
13973 /* at this point, the player is allowed to move, but cannot move right now
13974 (e.g. because of something blocking the way) -- ensure that the player
13975 is also allowed to move in the next frame (in old versions before 3.1.1,
13976 the player was forced to wait again for eight frames before next try) */
13978 if (game.engine_version >= VERSION_IDENT(3,1,1,0))
13979 player->move_delay = 0; /* allow direct movement in the next frame */
13982 if (player->move_delay == -1) /* not yet initialized by DigField() */
13983 player->move_delay = player->move_delay_value;
13985 if (game.engine_version < VERSION_IDENT(3,0,7,0))
13987 TestIfPlayerTouchesBadThing(jx, jy);
13988 TestIfPlayerTouchesCustomElement(jx, jy);
13991 if (!player->active)
13992 RemovePlayer(player);
13997 void ScrollPlayer(struct PlayerInfo *player, int mode)
13999 int jx = player->jx, jy = player->jy;
14000 int last_jx = player->last_jx, last_jy = player->last_jy;
14001 int move_stepsize = TILEX / player->move_delay_value;
14003 #if USE_NEW_PLAYER_SPEED
14004 if (!player->active)
14007 if (player->MovPos == 0 && mode == SCROLL_GO_ON) /* player not moving */
14010 if (!player->active || player->MovPos == 0)
14014 if (mode == SCROLL_INIT)
14016 player->actual_frame_counter = FrameCounter;
14017 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
14019 if ((player->block_last_field || player->block_delay_adjustment > 0) &&
14020 Feld[last_jx][last_jy] == EL_EMPTY)
14022 int last_field_block_delay = 0; /* start with no blocking at all */
14023 int block_delay_adjustment = player->block_delay_adjustment;
14025 /* if player blocks last field, add delay for exactly one move */
14026 if (player->block_last_field)
14028 last_field_block_delay += player->move_delay_value;
14030 /* when blocking enabled, prevent moving up despite gravity */
14031 #if USE_PLAYER_GRAVITY
14032 if (player->gravity && player->MovDir == MV_UP)
14033 block_delay_adjustment = -1;
14035 if (game.gravity && player->MovDir == MV_UP)
14036 block_delay_adjustment = -1;
14040 /* add block delay adjustment (also possible when not blocking) */
14041 last_field_block_delay += block_delay_adjustment;
14043 Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
14044 MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
14047 #if USE_NEW_PLAYER_SPEED
14048 if (player->MovPos != 0) /* player has not yet reached destination */
14054 else if (!FrameReached(&player->actual_frame_counter, 1))
14057 #if USE_NEW_PLAYER_SPEED
14058 if (player->MovPos != 0)
14060 player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
14061 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
14063 /* before DrawPlayer() to draw correct player graphic for this case */
14064 if (player->MovPos == 0)
14065 CheckGravityMovement(player);
14068 player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
14069 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
14071 /* before DrawPlayer() to draw correct player graphic for this case */
14072 if (player->MovPos == 0)
14073 CheckGravityMovement(player);
14076 if (player->MovPos == 0) /* player reached destination field */
14078 if (player->move_delay_reset_counter > 0)
14080 player->move_delay_reset_counter--;
14082 if (player->move_delay_reset_counter == 0)
14084 /* continue with normal speed after quickly moving through gate */
14085 HALVE_PLAYER_SPEED(player);
14087 /* be able to make the next move without delay */
14088 player->move_delay = 0;
14092 player->last_jx = jx;
14093 player->last_jy = jy;
14095 if (Feld[jx][jy] == EL_EXIT_OPEN ||
14096 Feld[jx][jy] == EL_EM_EXIT_OPEN ||
14098 Feld[jx][jy] == EL_EM_EXIT_OPENING ||
14100 Feld[jx][jy] == EL_STEEL_EXIT_OPEN ||
14101 Feld[jx][jy] == EL_EM_STEEL_EXIT_OPEN ||
14103 Feld[jx][jy] == EL_EM_STEEL_EXIT_OPENING ||
14105 Feld[jx][jy] == EL_SP_EXIT_OPEN ||
14106 Feld[jx][jy] == EL_SP_EXIT_OPENING) /* <-- special case */
14108 DrawPlayer(player); /* needed here only to cleanup last field */
14109 RemovePlayer(player);
14111 if (local_player->friends_still_needed == 0 ||
14112 IS_SP_ELEMENT(Feld[jx][jy]))
14113 PlayerWins(player);
14116 /* this breaks one level: "machine", level 000 */
14118 int move_direction = player->MovDir;
14119 int enter_side = MV_DIR_OPPOSITE(move_direction);
14120 int leave_side = move_direction;
14121 int old_jx = last_jx;
14122 int old_jy = last_jy;
14123 int old_element = Feld[old_jx][old_jy];
14124 int new_element = Feld[jx][jy];
14126 if (IS_CUSTOM_ELEMENT(old_element))
14127 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
14129 player->index_bit, leave_side);
14131 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
14132 CE_PLAYER_LEAVES_X,
14133 player->index_bit, leave_side);
14135 if (IS_CUSTOM_ELEMENT(new_element))
14136 CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
14137 player->index_bit, enter_side);
14139 CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
14140 CE_PLAYER_ENTERS_X,
14141 player->index_bit, enter_side);
14143 #if USE_FIX_CE_ACTION_WITH_PLAYER
14144 CheckTriggeredElementChangeBySide(jx, jy, player->initial_element,
14145 CE_MOVE_OF_X, move_direction);
14147 CheckTriggeredElementChangeBySide(jx, jy, player->element_nr,
14148 CE_MOVE_OF_X, move_direction);
14152 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
14154 TestIfPlayerTouchesBadThing(jx, jy);
14155 TestIfPlayerTouchesCustomElement(jx, jy);
14157 /* needed because pushed element has not yet reached its destination,
14158 so it would trigger a change event at its previous field location */
14159 if (!player->is_pushing)
14160 TestIfElementTouchesCustomElement(jx, jy); /* for empty space */
14162 if (!player->active)
14163 RemovePlayer(player);
14166 if (!local_player->LevelSolved && level.use_step_counter)
14176 if (TimeLeft <= 10 && setup.time_limit)
14177 PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
14180 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
14182 DisplayGameControlValues();
14184 DrawGameValue_Time(TimeLeft);
14187 if (!TimeLeft && setup.time_limit)
14188 for (i = 0; i < MAX_PLAYERS; i++)
14189 KillPlayer(&stored_player[i]);
14192 else if (game.no_time_limit && !AllPlayersGone) /* level w/o time limit */
14194 game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
14196 DisplayGameControlValues();
14199 else if (game.no_time_limit && !AllPlayersGone) /* level w/o time limit */
14200 DrawGameValue_Time(TimePlayed);
14204 if (tape.single_step && tape.recording && !tape.pausing &&
14205 !player->programmed_action)
14206 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
14210 void ScrollScreen(struct PlayerInfo *player, int mode)
14212 static unsigned int screen_frame_counter = 0;
14214 if (mode == SCROLL_INIT)
14216 /* set scrolling step size according to actual player's moving speed */
14217 ScrollStepSize = TILEX / player->move_delay_value;
14219 screen_frame_counter = FrameCounter;
14220 ScreenMovDir = player->MovDir;
14221 ScreenMovPos = player->MovPos;
14222 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
14225 else if (!FrameReached(&screen_frame_counter, 1))
14230 ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
14231 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
14232 redraw_mask |= REDRAW_FIELD;
14235 ScreenMovDir = MV_NONE;
14238 void TestIfPlayerTouchesCustomElement(int x, int y)
14240 static int xy[4][2] =
14247 static int trigger_sides[4][2] =
14249 /* center side border side */
14250 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
14251 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
14252 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
14253 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
14255 static int touch_dir[4] =
14257 MV_LEFT | MV_RIGHT,
14262 int center_element = Feld[x][y]; /* should always be non-moving! */
14265 for (i = 0; i < NUM_DIRECTIONS; i++)
14267 int xx = x + xy[i][0];
14268 int yy = y + xy[i][1];
14269 int center_side = trigger_sides[i][0];
14270 int border_side = trigger_sides[i][1];
14271 int border_element;
14273 if (!IN_LEV_FIELD(xx, yy))
14276 if (IS_PLAYER(x, y)) /* player found at center element */
14278 struct PlayerInfo *player = PLAYERINFO(x, y);
14280 if (game.engine_version < VERSION_IDENT(3,0,7,0))
14281 border_element = Feld[xx][yy]; /* may be moving! */
14282 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
14283 border_element = Feld[xx][yy];
14284 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
14285 border_element = MovingOrBlocked2Element(xx, yy);
14287 continue; /* center and border element do not touch */
14289 CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
14290 player->index_bit, border_side);
14291 CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
14292 CE_PLAYER_TOUCHES_X,
14293 player->index_bit, border_side);
14295 #if USE_FIX_CE_ACTION_WITH_PLAYER
14297 /* use player element that is initially defined in the level playfield,
14298 not the player element that corresponds to the runtime player number
14299 (example: a level that contains EL_PLAYER_3 as the only player would
14300 incorrectly give EL_PLAYER_1 for "player->element_nr") */
14301 int player_element = PLAYERINFO(x, y)->initial_element;
14303 CheckElementChangeBySide(xx, yy, border_element, player_element,
14304 CE_TOUCHING_X, border_side);
14308 else if (IS_PLAYER(xx, yy)) /* player found at border element */
14310 struct PlayerInfo *player = PLAYERINFO(xx, yy);
14312 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
14314 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
14315 continue; /* center and border element do not touch */
14318 CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
14319 player->index_bit, center_side);
14320 CheckTriggeredElementChangeByPlayer(x, y, center_element,
14321 CE_PLAYER_TOUCHES_X,
14322 player->index_bit, center_side);
14324 #if USE_FIX_CE_ACTION_WITH_PLAYER
14326 /* use player element that is initially defined in the level playfield,
14327 not the player element that corresponds to the runtime player number
14328 (example: a level that contains EL_PLAYER_3 as the only player would
14329 incorrectly give EL_PLAYER_1 for "player->element_nr") */
14330 int player_element = PLAYERINFO(xx, yy)->initial_element;
14332 CheckElementChangeBySide(x, y, center_element, player_element,
14333 CE_TOUCHING_X, center_side);
14342 #if USE_ELEMENT_TOUCHING_BUGFIX
14344 void TestIfElementTouchesCustomElement(int x, int y)
14346 static int xy[4][2] =
14353 static int trigger_sides[4][2] =
14355 /* center side border side */
14356 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
14357 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
14358 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
14359 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
14361 static int touch_dir[4] =
14363 MV_LEFT | MV_RIGHT,
14368 boolean change_center_element = FALSE;
14369 int center_element = Feld[x][y]; /* should always be non-moving! */
14370 int border_element_old[NUM_DIRECTIONS];
14373 for (i = 0; i < NUM_DIRECTIONS; i++)
14375 int xx = x + xy[i][0];
14376 int yy = y + xy[i][1];
14377 int border_element;
14379 border_element_old[i] = -1;
14381 if (!IN_LEV_FIELD(xx, yy))
14384 if (game.engine_version < VERSION_IDENT(3,0,7,0))
14385 border_element = Feld[xx][yy]; /* may be moving! */
14386 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
14387 border_element = Feld[xx][yy];
14388 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
14389 border_element = MovingOrBlocked2Element(xx, yy);
14391 continue; /* center and border element do not touch */
14393 border_element_old[i] = border_element;
14396 for (i = 0; i < NUM_DIRECTIONS; i++)
14398 int xx = x + xy[i][0];
14399 int yy = y + xy[i][1];
14400 int center_side = trigger_sides[i][0];
14401 int border_element = border_element_old[i];
14403 if (border_element == -1)
14406 /* check for change of border element */
14407 CheckElementChangeBySide(xx, yy, border_element, center_element,
14408 CE_TOUCHING_X, center_side);
14410 /* (center element cannot be player, so we dont have to check this here) */
14413 for (i = 0; i < NUM_DIRECTIONS; i++)
14415 int xx = x + xy[i][0];
14416 int yy = y + xy[i][1];
14417 int border_side = trigger_sides[i][1];
14418 int border_element = border_element_old[i];
14420 if (border_element == -1)
14423 /* check for change of center element (but change it only once) */
14424 if (!change_center_element)
14425 change_center_element =
14426 CheckElementChangeBySide(x, y, center_element, border_element,
14427 CE_TOUCHING_X, border_side);
14429 #if USE_FIX_CE_ACTION_WITH_PLAYER
14430 if (IS_PLAYER(xx, yy))
14432 /* use player element that is initially defined in the level playfield,
14433 not the player element that corresponds to the runtime player number
14434 (example: a level that contains EL_PLAYER_3 as the only player would
14435 incorrectly give EL_PLAYER_1 for "player->element_nr") */
14436 int player_element = PLAYERINFO(xx, yy)->initial_element;
14438 CheckElementChangeBySide(x, y, center_element, player_element,
14439 CE_TOUCHING_X, border_side);
14447 void TestIfElementTouchesCustomElement_OLD(int x, int y)
14449 static int xy[4][2] =
14456 static int trigger_sides[4][2] =
14458 /* center side border side */
14459 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
14460 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
14461 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
14462 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
14464 static int touch_dir[4] =
14466 MV_LEFT | MV_RIGHT,
14471 boolean change_center_element = FALSE;
14472 int center_element = Feld[x][y]; /* should always be non-moving! */
14475 for (i = 0; i < NUM_DIRECTIONS; i++)
14477 int xx = x + xy[i][0];
14478 int yy = y + xy[i][1];
14479 int center_side = trigger_sides[i][0];
14480 int border_side = trigger_sides[i][1];
14481 int border_element;
14483 if (!IN_LEV_FIELD(xx, yy))
14486 if (game.engine_version < VERSION_IDENT(3,0,7,0))
14487 border_element = Feld[xx][yy]; /* may be moving! */
14488 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
14489 border_element = Feld[xx][yy];
14490 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
14491 border_element = MovingOrBlocked2Element(xx, yy);
14493 continue; /* center and border element do not touch */
14495 /* check for change of center element (but change it only once) */
14496 if (!change_center_element)
14497 change_center_element =
14498 CheckElementChangeBySide(x, y, center_element, border_element,
14499 CE_TOUCHING_X, border_side);
14501 /* check for change of border element */
14502 CheckElementChangeBySide(xx, yy, border_element, center_element,
14503 CE_TOUCHING_X, center_side);
14509 void TestIfElementHitsCustomElement(int x, int y, int direction)
14511 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
14512 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
14513 int hitx = x + dx, hity = y + dy;
14514 int hitting_element = Feld[x][y];
14515 int touched_element;
14517 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
14520 touched_element = (IN_LEV_FIELD(hitx, hity) ?
14521 MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
14523 if (IN_LEV_FIELD(hitx, hity))
14525 int opposite_direction = MV_DIR_OPPOSITE(direction);
14526 int hitting_side = direction;
14527 int touched_side = opposite_direction;
14528 boolean object_hit = (!IS_MOVING(hitx, hity) ||
14529 MovDir[hitx][hity] != direction ||
14530 ABS(MovPos[hitx][hity]) <= TILEY / 2);
14536 CheckElementChangeBySide(x, y, hitting_element, touched_element,
14537 CE_HITTING_X, touched_side);
14539 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
14540 CE_HIT_BY_X, hitting_side);
14542 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
14543 CE_HIT_BY_SOMETHING, opposite_direction);
14545 #if USE_FIX_CE_ACTION_WITH_PLAYER
14546 if (IS_PLAYER(hitx, hity))
14548 /* use player element that is initially defined in the level playfield,
14549 not the player element that corresponds to the runtime player number
14550 (example: a level that contains EL_PLAYER_3 as the only player would
14551 incorrectly give EL_PLAYER_1 for "player->element_nr") */
14552 int player_element = PLAYERINFO(hitx, hity)->initial_element;
14554 CheckElementChangeBySide(x, y, hitting_element, player_element,
14555 CE_HITTING_X, touched_side);
14561 /* "hitting something" is also true when hitting the playfield border */
14562 CheckElementChangeBySide(x, y, hitting_element, touched_element,
14563 CE_HITTING_SOMETHING, direction);
14567 void TestIfElementSmashesCustomElement(int x, int y, int direction)
14569 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
14570 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
14571 int hitx = x + dx, hity = y + dy;
14572 int hitting_element = Feld[x][y];
14573 int touched_element;
14575 boolean object_hit = (IN_LEV_FIELD(hitx, hity) &&
14576 !IS_FREE(hitx, hity) &&
14577 (!IS_MOVING(hitx, hity) ||
14578 MovDir[hitx][hity] != direction ||
14579 ABS(MovPos[hitx][hity]) <= TILEY / 2));
14582 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
14586 if (IN_LEV_FIELD(hitx, hity) && !object_hit)
14590 touched_element = (IN_LEV_FIELD(hitx, hity) ?
14591 MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
14593 CheckElementChangeBySide(x, y, hitting_element, touched_element,
14594 EP_CAN_SMASH_EVERYTHING, direction);
14596 if (IN_LEV_FIELD(hitx, hity))
14598 int opposite_direction = MV_DIR_OPPOSITE(direction);
14599 int hitting_side = direction;
14600 int touched_side = opposite_direction;
14602 int touched_element = MovingOrBlocked2Element(hitx, hity);
14605 boolean object_hit = (!IS_MOVING(hitx, hity) ||
14606 MovDir[hitx][hity] != direction ||
14607 ABS(MovPos[hitx][hity]) <= TILEY / 2);
14616 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
14617 CE_SMASHED_BY_SOMETHING, opposite_direction);
14619 CheckElementChangeBySide(x, y, hitting_element, touched_element,
14620 CE_OTHER_IS_SMASHING, touched_side);
14622 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
14623 CE_OTHER_GETS_SMASHED, hitting_side);
14629 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
14631 int i, kill_x = -1, kill_y = -1;
14633 int bad_element = -1;
14634 static int test_xy[4][2] =
14641 static int test_dir[4] =
14649 for (i = 0; i < NUM_DIRECTIONS; i++)
14651 int test_x, test_y, test_move_dir, test_element;
14653 test_x = good_x + test_xy[i][0];
14654 test_y = good_y + test_xy[i][1];
14656 if (!IN_LEV_FIELD(test_x, test_y))
14660 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
14662 test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
14664 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
14665 2nd case: DONT_TOUCH style bad thing does not move away from good thing
14667 if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
14668 (DONT_TOUCH(test_element) && test_move_dir != test_dir[i]))
14672 bad_element = test_element;
14678 if (kill_x != -1 || kill_y != -1)
14680 if (IS_PLAYER(good_x, good_y))
14682 struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
14684 if (player->shield_deadly_time_left > 0 &&
14685 !IS_INDESTRUCTIBLE(bad_element))
14686 Bang(kill_x, kill_y);
14687 else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
14688 KillPlayer(player);
14691 Bang(good_x, good_y);
14695 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
14697 int i, kill_x = -1, kill_y = -1;
14698 int bad_element = Feld[bad_x][bad_y];
14699 static int test_xy[4][2] =
14706 static int touch_dir[4] =
14708 MV_LEFT | MV_RIGHT,
14713 static int test_dir[4] =
14721 if (bad_element == EL_EXPLOSION) /* skip just exploding bad things */
14724 for (i = 0; i < NUM_DIRECTIONS; i++)
14726 int test_x, test_y, test_move_dir, test_element;
14728 test_x = bad_x + test_xy[i][0];
14729 test_y = bad_y + test_xy[i][1];
14731 if (!IN_LEV_FIELD(test_x, test_y))
14735 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
14737 test_element = Feld[test_x][test_y];
14739 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
14740 2nd case: DONT_TOUCH style bad thing does not move away from good thing
14742 if ((DONT_RUN_INTO(bad_element) && bad_move_dir == test_dir[i]) ||
14743 (DONT_TOUCH(bad_element) && test_move_dir != test_dir[i]))
14745 /* good thing is player or penguin that does not move away */
14746 if (IS_PLAYER(test_x, test_y))
14748 struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
14750 if (bad_element == EL_ROBOT && player->is_moving)
14751 continue; /* robot does not kill player if he is moving */
14753 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
14755 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
14756 continue; /* center and border element do not touch */
14764 else if (test_element == EL_PENGUIN)
14774 if (kill_x != -1 || kill_y != -1)
14776 if (IS_PLAYER(kill_x, kill_y))
14778 struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
14780 if (player->shield_deadly_time_left > 0 &&
14781 !IS_INDESTRUCTIBLE(bad_element))
14782 Bang(bad_x, bad_y);
14783 else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
14784 KillPlayer(player);
14787 Bang(kill_x, kill_y);
14791 void TestIfGoodThingGetsHitByBadThing(int bad_x, int bad_y, int bad_move_dir)
14793 int bad_element = Feld[bad_x][bad_y];
14794 int dx = (bad_move_dir == MV_LEFT ? -1 : bad_move_dir == MV_RIGHT ? +1 : 0);
14795 int dy = (bad_move_dir == MV_UP ? -1 : bad_move_dir == MV_DOWN ? +1 : 0);
14796 int test_x = bad_x + dx, test_y = bad_y + dy;
14797 int test_move_dir, test_element;
14798 int kill_x = -1, kill_y = -1;
14800 if (!IN_LEV_FIELD(test_x, test_y))
14804 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
14806 test_element = Feld[test_x][test_y];
14808 if (test_move_dir != bad_move_dir)
14810 /* good thing can be player or penguin that does not move away */
14811 if (IS_PLAYER(test_x, test_y))
14813 struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
14815 /* (note: in comparison to DONT_RUN_TO and DONT_TOUCH, also handle the
14816 player as being hit when he is moving towards the bad thing, because
14817 the "get hit by" condition would be lost after the player stops) */
14818 if (player->MovPos != 0 && player->MovDir == bad_move_dir)
14819 return; /* player moves away from bad thing */
14824 else if (test_element == EL_PENGUIN)
14831 if (kill_x != -1 || kill_y != -1)
14833 if (IS_PLAYER(kill_x, kill_y))
14835 struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
14837 if (player->shield_deadly_time_left > 0 &&
14838 !IS_INDESTRUCTIBLE(bad_element))
14839 Bang(bad_x, bad_y);
14840 else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
14841 KillPlayer(player);
14844 Bang(kill_x, kill_y);
14848 void TestIfPlayerTouchesBadThing(int x, int y)
14850 TestIfGoodThingHitsBadThing(x, y, MV_NONE);
14853 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
14855 TestIfGoodThingHitsBadThing(x, y, move_dir);
14858 void TestIfBadThingTouchesPlayer(int x, int y)
14860 TestIfBadThingHitsGoodThing(x, y, MV_NONE);
14863 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
14865 TestIfBadThingHitsGoodThing(x, y, move_dir);
14868 void TestIfFriendTouchesBadThing(int x, int y)
14870 TestIfGoodThingHitsBadThing(x, y, MV_NONE);
14873 void TestIfBadThingTouchesFriend(int x, int y)
14875 TestIfBadThingHitsGoodThing(x, y, MV_NONE);
14878 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
14880 int i, kill_x = bad_x, kill_y = bad_y;
14881 static int xy[4][2] =
14889 for (i = 0; i < NUM_DIRECTIONS; i++)
14893 x = bad_x + xy[i][0];
14894 y = bad_y + xy[i][1];
14895 if (!IN_LEV_FIELD(x, y))
14898 element = Feld[x][y];
14899 if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
14900 element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
14908 if (kill_x != bad_x || kill_y != bad_y)
14909 Bang(bad_x, bad_y);
14912 void KillPlayer(struct PlayerInfo *player)
14914 int jx = player->jx, jy = player->jy;
14916 if (!player->active)
14920 printf("::: 0: killed == %d, active == %d, reanimated == %d\n",
14921 player->killed, player->active, player->reanimated);
14924 /* the following code was introduced to prevent an infinite loop when calling
14926 -> CheckTriggeredElementChangeExt()
14927 -> ExecuteCustomElementAction()
14929 -> (infinitely repeating the above sequence of function calls)
14930 which occurs when killing the player while having a CE with the setting
14931 "kill player X when explosion of <player X>"; the solution using a new
14932 field "player->killed" was chosen for backwards compatibility, although
14933 clever use of the fields "player->active" etc. would probably also work */
14935 if (player->killed)
14939 player->killed = TRUE;
14941 /* remove accessible field at the player's position */
14942 Feld[jx][jy] = EL_EMPTY;
14944 /* deactivate shield (else Bang()/Explode() would not work right) */
14945 player->shield_normal_time_left = 0;
14946 player->shield_deadly_time_left = 0;
14949 printf("::: 1: killed == %d, active == %d, reanimated == %d\n",
14950 player->killed, player->active, player->reanimated);
14956 printf("::: 2: killed == %d, active == %d, reanimated == %d\n",
14957 player->killed, player->active, player->reanimated);
14960 #if USE_PLAYER_REANIMATION
14962 if (player->reanimated) /* killed player may have been reanimated */
14963 player->killed = player->reanimated = FALSE;
14965 BuryPlayer(player);
14967 if (player->killed) /* player may have been reanimated */
14968 BuryPlayer(player);
14971 BuryPlayer(player);
14975 static void KillPlayerUnlessEnemyProtected(int x, int y)
14977 if (!PLAYER_ENEMY_PROTECTED(x, y))
14978 KillPlayer(PLAYERINFO(x, y));
14981 static void KillPlayerUnlessExplosionProtected(int x, int y)
14983 if (!PLAYER_EXPLOSION_PROTECTED(x, y))
14984 KillPlayer(PLAYERINFO(x, y));
14987 void BuryPlayer(struct PlayerInfo *player)
14989 int jx = player->jx, jy = player->jy;
14991 if (!player->active)
14994 PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
14995 PlayLevelSound(jx, jy, SND_GAME_LOSING);
14997 player->GameOver = TRUE;
14998 RemovePlayer(player);
15001 void RemovePlayer(struct PlayerInfo *player)
15003 int jx = player->jx, jy = player->jy;
15004 int i, found = FALSE;
15006 player->present = FALSE;
15007 player->active = FALSE;
15009 if (!ExplodeField[jx][jy])
15010 StorePlayer[jx][jy] = 0;
15012 if (player->is_moving)
15013 TEST_DrawLevelField(player->last_jx, player->last_jy);
15015 for (i = 0; i < MAX_PLAYERS; i++)
15016 if (stored_player[i].active)
15020 AllPlayersGone = TRUE;
15026 #if USE_NEW_SNAP_DELAY
15027 static void setFieldForSnapping(int x, int y, int element, int direction)
15029 struct ElementInfo *ei = &element_info[element];
15030 int direction_bit = MV_DIR_TO_BIT(direction);
15031 int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
15032 int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
15033 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
15035 Feld[x][y] = EL_ELEMENT_SNAPPING;
15036 MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
15038 ResetGfxAnimation(x, y);
15040 GfxElement[x][y] = element;
15041 GfxAction[x][y] = action;
15042 GfxDir[x][y] = direction;
15043 GfxFrame[x][y] = -1;
15048 =============================================================================
15049 checkDiagonalPushing()
15050 -----------------------------------------------------------------------------
15051 check if diagonal input device direction results in pushing of object
15052 (by checking if the alternative direction is walkable, diggable, ...)
15053 =============================================================================
15056 static boolean checkDiagonalPushing(struct PlayerInfo *player,
15057 int x, int y, int real_dx, int real_dy)
15059 int jx, jy, dx, dy, xx, yy;
15061 if (real_dx == 0 || real_dy == 0) /* no diagonal direction => push */
15064 /* diagonal direction: check alternative direction */
15069 xx = jx + (dx == 0 ? real_dx : 0);
15070 yy = jy + (dy == 0 ? real_dy : 0);
15072 return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
15076 =============================================================================
15078 -----------------------------------------------------------------------------
15079 x, y: field next to player (non-diagonal) to try to dig to
15080 real_dx, real_dy: direction as read from input device (can be diagonal)
15081 =============================================================================
15084 static int DigField(struct PlayerInfo *player,
15085 int oldx, int oldy, int x, int y,
15086 int real_dx, int real_dy, int mode)
15088 boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
15089 boolean player_was_pushing = player->is_pushing;
15090 boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
15091 boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
15092 int jx = oldx, jy = oldy;
15093 int dx = x - jx, dy = y - jy;
15094 int nextx = x + dx, nexty = y + dy;
15095 int move_direction = (dx == -1 ? MV_LEFT :
15096 dx == +1 ? MV_RIGHT :
15098 dy == +1 ? MV_DOWN : MV_NONE);
15099 int opposite_direction = MV_DIR_OPPOSITE(move_direction);
15100 int dig_side = MV_DIR_OPPOSITE(move_direction);
15101 int old_element = Feld[jx][jy];
15102 #if USE_FIXED_DONT_RUN_INTO
15103 int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
15109 if (is_player) /* function can also be called by EL_PENGUIN */
15111 if (player->MovPos == 0)
15113 player->is_digging = FALSE;
15114 player->is_collecting = FALSE;
15117 if (player->MovPos == 0) /* last pushing move finished */
15118 player->is_pushing = FALSE;
15120 if (mode == DF_NO_PUSH) /* player just stopped pushing */
15122 player->is_switching = FALSE;
15123 player->push_delay = -1;
15125 return MP_NO_ACTION;
15129 #if !USE_FIXED_DONT_RUN_INTO
15130 if (IS_MOVING(x, y) || IS_PLAYER(x, y))
15131 return MP_NO_ACTION;
15134 if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
15135 old_element = Back[jx][jy];
15137 /* in case of element dropped at player position, check background */
15138 else if (Back[jx][jy] != EL_EMPTY &&
15139 game.engine_version >= VERSION_IDENT(2,2,0,0))
15140 old_element = Back[jx][jy];
15142 if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
15143 return MP_NO_ACTION; /* field has no opening in this direction */
15145 if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
15146 return MP_NO_ACTION; /* field has no opening in this direction */
15148 #if USE_FIXED_DONT_RUN_INTO
15149 if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
15153 Feld[jx][jy] = player->artwork_element;
15154 InitMovingField(jx, jy, MV_DOWN);
15155 Store[jx][jy] = EL_ACID;
15156 ContinueMoving(jx, jy);
15157 BuryPlayer(player);
15159 return MP_DONT_RUN_INTO;
15163 #if USE_FIXED_DONT_RUN_INTO
15164 if (player_can_move && DONT_RUN_INTO(element))
15166 TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
15168 return MP_DONT_RUN_INTO;
15172 #if USE_FIXED_DONT_RUN_INTO
15173 if (IS_MOVING(x, y) || IS_PLAYER(x, y))
15174 return MP_NO_ACTION;
15177 #if !USE_FIXED_DONT_RUN_INTO
15178 element = Feld[x][y];
15181 collect_count = element_info[element].collect_count_initial;
15183 if (!is_player && !IS_COLLECTIBLE(element)) /* penguin cannot collect it */
15184 return MP_NO_ACTION;
15186 if (game.engine_version < VERSION_IDENT(2,2,0,0))
15187 player_can_move = player_can_move_or_snap;
15189 if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
15190 game.engine_version >= VERSION_IDENT(2,2,0,0))
15192 CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
15193 player->index_bit, dig_side);
15194 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
15195 player->index_bit, dig_side);
15197 if (element == EL_DC_LANDMINE)
15200 if (Feld[x][y] != element) /* field changed by snapping */
15203 return MP_NO_ACTION;
15206 #if USE_PLAYER_GRAVITY
15207 if (player->gravity && is_player && !player->is_auto_moving &&
15208 canFallDown(player) && move_direction != MV_DOWN &&
15209 !canMoveToValidFieldWithGravity(jx, jy, move_direction))
15210 return MP_NO_ACTION; /* player cannot walk here due to gravity */
15212 if (game.gravity && is_player && !player->is_auto_moving &&
15213 canFallDown(player) && move_direction != MV_DOWN &&
15214 !canMoveToValidFieldWithGravity(jx, jy, move_direction))
15215 return MP_NO_ACTION; /* player cannot walk here due to gravity */
15218 if (player_can_move &&
15219 IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
15221 int sound_element = SND_ELEMENT(element);
15222 int sound_action = ACTION_WALKING;
15224 if (IS_RND_GATE(element))
15226 if (!player->key[RND_GATE_NR(element)])
15227 return MP_NO_ACTION;
15229 else if (IS_RND_GATE_GRAY(element))
15231 if (!player->key[RND_GATE_GRAY_NR(element)])
15232 return MP_NO_ACTION;
15234 else if (IS_RND_GATE_GRAY_ACTIVE(element))
15236 if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
15237 return MP_NO_ACTION;
15239 else if (element == EL_EXIT_OPEN ||
15240 element == EL_EM_EXIT_OPEN ||
15242 element == EL_EM_EXIT_OPENING ||
15244 element == EL_STEEL_EXIT_OPEN ||
15245 element == EL_EM_STEEL_EXIT_OPEN ||
15247 element == EL_EM_STEEL_EXIT_OPENING ||
15249 element == EL_SP_EXIT_OPEN ||
15250 element == EL_SP_EXIT_OPENING)
15252 sound_action = ACTION_PASSING; /* player is passing exit */
15254 else if (element == EL_EMPTY)
15256 sound_action = ACTION_MOVING; /* nothing to walk on */
15259 /* play sound from background or player, whatever is available */
15260 if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
15261 PlayLevelSoundElementAction(x, y, sound_element, sound_action);
15263 PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
15265 else if (player_can_move &&
15266 IS_PASSABLE(element) && canPassField(x, y, move_direction))
15268 if (!ACCESS_FROM(element, opposite_direction))
15269 return MP_NO_ACTION; /* field not accessible from this direction */
15271 if (CAN_MOVE(element)) /* only fixed elements can be passed! */
15272 return MP_NO_ACTION;
15274 if (IS_EM_GATE(element))
15276 if (!player->key[EM_GATE_NR(element)])
15277 return MP_NO_ACTION;
15279 else if (IS_EM_GATE_GRAY(element))
15281 if (!player->key[EM_GATE_GRAY_NR(element)])
15282 return MP_NO_ACTION;
15284 else if (IS_EM_GATE_GRAY_ACTIVE(element))
15286 if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
15287 return MP_NO_ACTION;
15289 else if (IS_EMC_GATE(element))
15291 if (!player->key[EMC_GATE_NR(element)])
15292 return MP_NO_ACTION;
15294 else if (IS_EMC_GATE_GRAY(element))
15296 if (!player->key[EMC_GATE_GRAY_NR(element)])
15297 return MP_NO_ACTION;
15299 else if (IS_EMC_GATE_GRAY_ACTIVE(element))
15301 if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
15302 return MP_NO_ACTION;
15304 else if (element == EL_DC_GATE_WHITE ||
15305 element == EL_DC_GATE_WHITE_GRAY ||
15306 element == EL_DC_GATE_WHITE_GRAY_ACTIVE)
15308 if (player->num_white_keys == 0)
15309 return MP_NO_ACTION;
15311 player->num_white_keys--;
15313 else if (IS_SP_PORT(element))
15315 if (element == EL_SP_GRAVITY_PORT_LEFT ||
15316 element == EL_SP_GRAVITY_PORT_RIGHT ||
15317 element == EL_SP_GRAVITY_PORT_UP ||
15318 element == EL_SP_GRAVITY_PORT_DOWN)
15319 #if USE_PLAYER_GRAVITY
15320 player->gravity = !player->gravity;
15322 game.gravity = !game.gravity;
15324 else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
15325 element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
15326 element == EL_SP_GRAVITY_ON_PORT_UP ||
15327 element == EL_SP_GRAVITY_ON_PORT_DOWN)
15328 #if USE_PLAYER_GRAVITY
15329 player->gravity = TRUE;
15331 game.gravity = TRUE;
15333 else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
15334 element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
15335 element == EL_SP_GRAVITY_OFF_PORT_UP ||
15336 element == EL_SP_GRAVITY_OFF_PORT_DOWN)
15337 #if USE_PLAYER_GRAVITY
15338 player->gravity = FALSE;
15340 game.gravity = FALSE;
15344 /* automatically move to the next field with double speed */
15345 player->programmed_action = move_direction;
15347 if (player->move_delay_reset_counter == 0)
15349 player->move_delay_reset_counter = 2; /* two double speed steps */
15351 DOUBLE_PLAYER_SPEED(player);
15354 PlayLevelSoundAction(x, y, ACTION_PASSING);
15356 else if (player_can_move_or_snap && IS_DIGGABLE(element))
15360 if (mode != DF_SNAP)
15362 GfxElement[x][y] = GFX_ELEMENT(element);
15363 player->is_digging = TRUE;
15366 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
15368 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
15369 player->index_bit, dig_side);
15371 if (mode == DF_SNAP)
15373 #if USE_NEW_SNAP_DELAY
15374 if (level.block_snap_field)
15375 setFieldForSnapping(x, y, element, move_direction);
15377 TestIfElementTouchesCustomElement(x, y); /* for empty space */
15379 TestIfElementTouchesCustomElement(x, y); /* for empty space */
15382 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
15383 player->index_bit, dig_side);
15386 else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
15390 if (is_player && mode != DF_SNAP)
15392 GfxElement[x][y] = element;
15393 player->is_collecting = TRUE;
15396 if (element == EL_SPEED_PILL)
15398 player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
15400 else if (element == EL_EXTRA_TIME && level.time > 0)
15402 TimeLeft += level.extra_time;
15405 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
15407 DisplayGameControlValues();
15409 DrawGameValue_Time(TimeLeft);
15412 else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
15414 player->shield_normal_time_left += level.shield_normal_time;
15415 if (element == EL_SHIELD_DEADLY)
15416 player->shield_deadly_time_left += level.shield_deadly_time;
15418 else if (element == EL_DYNAMITE ||
15419 element == EL_EM_DYNAMITE ||
15420 element == EL_SP_DISK_RED)
15422 if (player->inventory_size < MAX_INVENTORY_SIZE)
15423 player->inventory_element[player->inventory_size++] = element;
15425 DrawGameDoorValues();
15427 else if (element == EL_DYNABOMB_INCREASE_NUMBER)
15429 player->dynabomb_count++;
15430 player->dynabombs_left++;
15432 else if (element == EL_DYNABOMB_INCREASE_SIZE)
15434 player->dynabomb_size++;
15436 else if (element == EL_DYNABOMB_INCREASE_POWER)
15438 player->dynabomb_xl = TRUE;
15440 else if (IS_KEY(element))
15442 player->key[KEY_NR(element)] = TRUE;
15444 DrawGameDoorValues();
15446 else if (element == EL_DC_KEY_WHITE)
15448 player->num_white_keys++;
15450 /* display white keys? */
15451 /* DrawGameDoorValues(); */
15453 else if (IS_ENVELOPE(element))
15455 player->show_envelope = element;
15457 else if (element == EL_EMC_LENSES)
15459 game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
15461 RedrawAllInvisibleElementsForLenses();
15463 else if (element == EL_EMC_MAGNIFIER)
15465 game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
15467 RedrawAllInvisibleElementsForMagnifier();
15469 else if (IS_DROPPABLE(element) ||
15470 IS_THROWABLE(element)) /* can be collected and dropped */
15474 if (collect_count == 0)
15475 player->inventory_infinite_element = element;
15477 for (i = 0; i < collect_count; i++)
15478 if (player->inventory_size < MAX_INVENTORY_SIZE)
15479 player->inventory_element[player->inventory_size++] = element;
15481 DrawGameDoorValues();
15483 else if (collect_count > 0)
15485 local_player->gems_still_needed -= collect_count;
15486 if (local_player->gems_still_needed < 0)
15487 local_player->gems_still_needed = 0;
15490 game_panel_controls[GAME_PANEL_GEMS].value = local_player->gems_still_needed;
15492 DisplayGameControlValues();
15494 DrawGameValue_Emeralds(local_player->gems_still_needed);
15498 RaiseScoreElement(element);
15499 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
15502 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
15503 player->index_bit, dig_side);
15505 if (mode == DF_SNAP)
15507 #if USE_NEW_SNAP_DELAY
15508 if (level.block_snap_field)
15509 setFieldForSnapping(x, y, element, move_direction);
15511 TestIfElementTouchesCustomElement(x, y); /* for empty space */
15513 TestIfElementTouchesCustomElement(x, y); /* for empty space */
15516 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
15517 player->index_bit, dig_side);
15520 else if (player_can_move_or_snap && IS_PUSHABLE(element))
15522 if (mode == DF_SNAP && element != EL_BD_ROCK)
15523 return MP_NO_ACTION;
15525 if (CAN_FALL(element) && dy)
15526 return MP_NO_ACTION;
15528 if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
15529 !(element == EL_SPRING && level.use_spring_bug))
15530 return MP_NO_ACTION;
15532 if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
15533 ((move_direction & MV_VERTICAL &&
15534 ((element_info[element].move_pattern & MV_LEFT &&
15535 IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
15536 (element_info[element].move_pattern & MV_RIGHT &&
15537 IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
15538 (move_direction & MV_HORIZONTAL &&
15539 ((element_info[element].move_pattern & MV_UP &&
15540 IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
15541 (element_info[element].move_pattern & MV_DOWN &&
15542 IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
15543 return MP_NO_ACTION;
15545 /* do not push elements already moving away faster than player */
15546 if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
15547 ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
15548 return MP_NO_ACTION;
15550 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
15552 if (player->push_delay_value == -1 || !player_was_pushing)
15553 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
15555 else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
15557 if (player->push_delay_value == -1)
15558 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
15560 else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
15562 if (!player->is_pushing)
15563 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
15566 player->is_pushing = TRUE;
15567 player->is_active = TRUE;
15569 if (!(IN_LEV_FIELD(nextx, nexty) &&
15570 (IS_FREE(nextx, nexty) ||
15571 (IS_SB_ELEMENT(element) &&
15572 Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY) ||
15573 (IS_CUSTOM_ELEMENT(element) &&
15574 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty)))))
15575 return MP_NO_ACTION;
15577 if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
15578 return MP_NO_ACTION;
15580 if (player->push_delay == -1) /* new pushing; restart delay */
15581 player->push_delay = 0;
15583 if (player->push_delay < player->push_delay_value &&
15584 !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
15585 element != EL_SPRING && element != EL_BALLOON)
15587 /* make sure that there is no move delay before next try to push */
15588 if (game.engine_version >= VERSION_IDENT(3,0,7,1))
15589 player->move_delay = 0;
15591 return MP_NO_ACTION;
15594 if (IS_CUSTOM_ELEMENT(element) &&
15595 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty))
15597 if (!DigFieldByCE(nextx, nexty, element))
15598 return MP_NO_ACTION;
15601 if (IS_SB_ELEMENT(element))
15603 if (element == EL_SOKOBAN_FIELD_FULL)
15605 Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
15606 local_player->sokobanfields_still_needed++;
15609 if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
15611 Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
15612 local_player->sokobanfields_still_needed--;
15615 Feld[x][y] = EL_SOKOBAN_OBJECT;
15617 if (Back[x][y] == Back[nextx][nexty])
15618 PlayLevelSoundAction(x, y, ACTION_PUSHING);
15619 else if (Back[x][y] != 0)
15620 PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
15623 PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
15627 if (local_player->sokobanfields_still_needed == 0 &&
15628 (game.emulation == EMU_SOKOBAN || level.auto_exit_sokoban))
15630 if (local_player->sokobanfields_still_needed == 0 &&
15631 game.emulation == EMU_SOKOBAN)
15634 PlayerWins(player);
15636 PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
15640 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
15642 InitMovingField(x, y, move_direction);
15643 GfxAction[x][y] = ACTION_PUSHING;
15645 if (mode == DF_SNAP)
15646 ContinueMoving(x, y);
15648 MovPos[x][y] = (dx != 0 ? dx : dy);
15650 Pushed[x][y] = TRUE;
15651 Pushed[nextx][nexty] = TRUE;
15653 if (game.engine_version < VERSION_IDENT(2,2,0,7))
15654 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
15656 player->push_delay_value = -1; /* get new value later */
15658 /* check for element change _after_ element has been pushed */
15659 if (game.use_change_when_pushing_bug)
15661 CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
15662 player->index_bit, dig_side);
15663 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
15664 player->index_bit, dig_side);
15667 else if (IS_SWITCHABLE(element))
15669 if (PLAYER_SWITCHING(player, x, y))
15671 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
15672 player->index_bit, dig_side);
15677 player->is_switching = TRUE;
15678 player->switch_x = x;
15679 player->switch_y = y;
15681 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
15683 if (element == EL_ROBOT_WHEEL)
15685 Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
15689 game.robot_wheel_active = TRUE;
15691 TEST_DrawLevelField(x, y);
15693 else if (element == EL_SP_TERMINAL)
15697 SCAN_PLAYFIELD(xx, yy)
15699 if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
15701 else if (Feld[xx][yy] == EL_SP_TERMINAL)
15702 Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
15705 else if (IS_BELT_SWITCH(element))
15707 ToggleBeltSwitch(x, y);
15709 else if (element == EL_SWITCHGATE_SWITCH_UP ||
15710 element == EL_SWITCHGATE_SWITCH_DOWN ||
15711 element == EL_DC_SWITCHGATE_SWITCH_UP ||
15712 element == EL_DC_SWITCHGATE_SWITCH_DOWN)
15714 ToggleSwitchgateSwitch(x, y);
15716 else if (element == EL_LIGHT_SWITCH ||
15717 element == EL_LIGHT_SWITCH_ACTIVE)
15719 ToggleLightSwitch(x, y);
15721 else if (element == EL_TIMEGATE_SWITCH ||
15722 element == EL_DC_TIMEGATE_SWITCH)
15724 ActivateTimegateSwitch(x, y);
15726 else if (element == EL_BALLOON_SWITCH_LEFT ||
15727 element == EL_BALLOON_SWITCH_RIGHT ||
15728 element == EL_BALLOON_SWITCH_UP ||
15729 element == EL_BALLOON_SWITCH_DOWN ||
15730 element == EL_BALLOON_SWITCH_NONE ||
15731 element == EL_BALLOON_SWITCH_ANY)
15733 game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT ? MV_LEFT :
15734 element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
15735 element == EL_BALLOON_SWITCH_UP ? MV_UP :
15736 element == EL_BALLOON_SWITCH_DOWN ? MV_DOWN :
15737 element == EL_BALLOON_SWITCH_NONE ? MV_NONE :
15740 else if (element == EL_LAMP)
15742 Feld[x][y] = EL_LAMP_ACTIVE;
15743 local_player->lights_still_needed--;
15745 ResetGfxAnimation(x, y);
15746 TEST_DrawLevelField(x, y);
15748 else if (element == EL_TIME_ORB_FULL)
15750 Feld[x][y] = EL_TIME_ORB_EMPTY;
15752 if (level.time > 0 || level.use_time_orb_bug)
15754 TimeLeft += level.time_orb_time;
15755 game.no_time_limit = FALSE;
15758 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
15760 DisplayGameControlValues();
15762 DrawGameValue_Time(TimeLeft);
15766 ResetGfxAnimation(x, y);
15767 TEST_DrawLevelField(x, y);
15769 else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
15770 element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
15774 game.ball_state = !game.ball_state;
15776 SCAN_PLAYFIELD(xx, yy)
15778 int e = Feld[xx][yy];
15780 if (game.ball_state)
15782 if (e == EL_EMC_MAGIC_BALL)
15783 CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
15784 else if (e == EL_EMC_MAGIC_BALL_SWITCH)
15785 CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
15789 if (e == EL_EMC_MAGIC_BALL_ACTIVE)
15790 CreateField(xx, yy, EL_EMC_MAGIC_BALL);
15791 else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
15792 CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
15797 CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
15798 player->index_bit, dig_side);
15800 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
15801 player->index_bit, dig_side);
15803 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
15804 player->index_bit, dig_side);
15810 if (!PLAYER_SWITCHING(player, x, y))
15812 player->is_switching = TRUE;
15813 player->switch_x = x;
15814 player->switch_y = y;
15816 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
15817 player->index_bit, dig_side);
15818 CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
15819 player->index_bit, dig_side);
15821 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
15822 player->index_bit, dig_side);
15823 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
15824 player->index_bit, dig_side);
15827 CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
15828 player->index_bit, dig_side);
15829 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
15830 player->index_bit, dig_side);
15832 return MP_NO_ACTION;
15835 player->push_delay = -1;
15837 if (is_player) /* function can also be called by EL_PENGUIN */
15839 if (Feld[x][y] != element) /* really digged/collected something */
15841 player->is_collecting = !player->is_digging;
15842 player->is_active = TRUE;
15849 static boolean DigFieldByCE(int x, int y, int digging_element)
15851 int element = Feld[x][y];
15853 if (!IS_FREE(x, y))
15855 int action = (IS_DIGGABLE(element) ? ACTION_DIGGING :
15856 IS_COLLECTIBLE(element) ? ACTION_COLLECTING :
15859 /* no element can dig solid indestructible elements */
15860 if (IS_INDESTRUCTIBLE(element) &&
15861 !IS_DIGGABLE(element) &&
15862 !IS_COLLECTIBLE(element))
15865 if (AmoebaNr[x][y] &&
15866 (element == EL_AMOEBA_FULL ||
15867 element == EL_BD_AMOEBA ||
15868 element == EL_AMOEBA_GROWING))
15870 AmoebaCnt[AmoebaNr[x][y]]--;
15871 AmoebaCnt2[AmoebaNr[x][y]]--;
15874 if (IS_MOVING(x, y))
15875 RemoveMovingField(x, y);
15879 TEST_DrawLevelField(x, y);
15882 /* if digged element was about to explode, prevent the explosion */
15883 ExplodeField[x][y] = EX_TYPE_NONE;
15885 PlayLevelSoundAction(x, y, action);
15888 Store[x][y] = EL_EMPTY;
15891 /* this makes it possible to leave the removed element again */
15892 if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element)))
15893 Store[x][y] = element;
15895 if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element)))
15897 int move_leave_element = element_info[digging_element].move_leave_element;
15899 /* this makes it possible to leave the removed element again */
15900 Store[x][y] = (move_leave_element == EL_TRIGGER_ELEMENT ?
15901 element : move_leave_element);
15908 static boolean SnapField(struct PlayerInfo *player, int dx, int dy)
15910 int jx = player->jx, jy = player->jy;
15911 int x = jx + dx, y = jy + dy;
15912 int snap_direction = (dx == -1 ? MV_LEFT :
15913 dx == +1 ? MV_RIGHT :
15915 dy == +1 ? MV_DOWN : MV_NONE);
15916 boolean can_continue_snapping = (level.continuous_snapping &&
15917 WasJustFalling[x][y] < CHECK_DELAY_FALLING);
15919 if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
15922 if (!player->active || !IN_LEV_FIELD(x, y))
15930 if (player->MovPos == 0)
15931 player->is_pushing = FALSE;
15933 player->is_snapping = FALSE;
15935 if (player->MovPos == 0)
15937 player->is_moving = FALSE;
15938 player->is_digging = FALSE;
15939 player->is_collecting = FALSE;
15945 #if USE_NEW_CONTINUOUS_SNAPPING
15946 /* prevent snapping with already pressed snap key when not allowed */
15947 if (player->is_snapping && !can_continue_snapping)
15950 if (player->is_snapping)
15954 player->MovDir = snap_direction;
15956 if (player->MovPos == 0)
15958 player->is_moving = FALSE;
15959 player->is_digging = FALSE;
15960 player->is_collecting = FALSE;
15963 player->is_dropping = FALSE;
15964 player->is_dropping_pressed = FALSE;
15965 player->drop_pressed_delay = 0;
15967 if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
15970 player->is_snapping = TRUE;
15971 player->is_active = TRUE;
15973 if (player->MovPos == 0)
15975 player->is_moving = FALSE;
15976 player->is_digging = FALSE;
15977 player->is_collecting = FALSE;
15980 if (player->MovPos != 0) /* prevent graphic bugs in versions < 2.2.0 */
15981 TEST_DrawLevelField(player->last_jx, player->last_jy);
15983 TEST_DrawLevelField(x, y);
15988 static boolean DropElement(struct PlayerInfo *player)
15990 int old_element, new_element;
15991 int dropx = player->jx, dropy = player->jy;
15992 int drop_direction = player->MovDir;
15993 int drop_side = drop_direction;
15995 int drop_element = get_next_dropped_element(player);
15997 int drop_element = (player->inventory_size > 0 ?
15998 player->inventory_element[player->inventory_size - 1] :
15999 player->inventory_infinite_element != EL_UNDEFINED ?
16000 player->inventory_infinite_element :
16001 player->dynabombs_left > 0 ?
16002 EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
16006 player->is_dropping_pressed = TRUE;
16008 /* do not drop an element on top of another element; when holding drop key
16009 pressed without moving, dropped element must move away before the next
16010 element can be dropped (this is especially important if the next element
16011 is dynamite, which can be placed on background for historical reasons) */
16012 if (PLAYER_DROPPING(player, dropx, dropy) && Feld[dropx][dropy] != EL_EMPTY)
16015 if (IS_THROWABLE(drop_element))
16017 dropx += GET_DX_FROM_DIR(drop_direction);
16018 dropy += GET_DY_FROM_DIR(drop_direction);
16020 if (!IN_LEV_FIELD(dropx, dropy))
16024 old_element = Feld[dropx][dropy]; /* old element at dropping position */
16025 new_element = drop_element; /* default: no change when dropping */
16027 /* check if player is active, not moving and ready to drop */
16028 if (!player->active || player->MovPos || player->drop_delay > 0)
16031 /* check if player has anything that can be dropped */
16032 if (new_element == EL_UNDEFINED)
16035 /* check if drop key was pressed long enough for EM style dynamite */
16036 if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
16039 /* check if anything can be dropped at the current position */
16040 if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
16043 /* collected custom elements can only be dropped on empty fields */
16044 if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
16047 if (old_element != EL_EMPTY)
16048 Back[dropx][dropy] = old_element; /* store old element on this field */
16050 ResetGfxAnimation(dropx, dropy);
16051 ResetRandomAnimationValue(dropx, dropy);
16053 if (player->inventory_size > 0 ||
16054 player->inventory_infinite_element != EL_UNDEFINED)
16056 if (player->inventory_size > 0)
16058 player->inventory_size--;
16060 DrawGameDoorValues();
16062 if (new_element == EL_DYNAMITE)
16063 new_element = EL_DYNAMITE_ACTIVE;
16064 else if (new_element == EL_EM_DYNAMITE)
16065 new_element = EL_EM_DYNAMITE_ACTIVE;
16066 else if (new_element == EL_SP_DISK_RED)
16067 new_element = EL_SP_DISK_RED_ACTIVE;
16070 Feld[dropx][dropy] = new_element;
16072 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
16073 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
16074 el2img(Feld[dropx][dropy]), 0);
16076 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
16078 /* needed if previous element just changed to "empty" in the last frame */
16079 ChangeCount[dropx][dropy] = 0; /* allow at least one more change */
16081 CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
16082 player->index_bit, drop_side);
16083 CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
16085 player->index_bit, drop_side);
16087 TestIfElementTouchesCustomElement(dropx, dropy);
16089 else /* player is dropping a dyna bomb */
16091 player->dynabombs_left--;
16093 Feld[dropx][dropy] = new_element;
16095 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
16096 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
16097 el2img(Feld[dropx][dropy]), 0);
16099 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
16102 if (Feld[dropx][dropy] == new_element) /* uninitialized unless CE change */
16103 InitField_WithBug1(dropx, dropy, FALSE);
16105 new_element = Feld[dropx][dropy]; /* element might have changed */
16107 if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
16108 element_info[new_element].move_pattern == MV_WHEN_DROPPED)
16111 int move_direction;
16115 if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
16116 MovDir[dropx][dropy] = drop_direction;
16119 move_direction = MovDir[dropx][dropy];
16120 nextx = dropx + GET_DX_FROM_DIR(move_direction);
16121 nexty = dropy + GET_DY_FROM_DIR(move_direction);
16124 ChangeCount[dropx][dropy] = 0; /* allow at least one more change */
16126 #if USE_FIX_IMPACT_COLLISION
16127 /* do not cause impact style collision by dropping elements that can fall */
16128 CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
16130 CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
16134 player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
16135 player->is_dropping = TRUE;
16137 player->drop_pressed_delay = 0;
16138 player->is_dropping_pressed = FALSE;
16140 player->drop_x = dropx;
16141 player->drop_y = dropy;
16146 /* ------------------------------------------------------------------------- */
16147 /* game sound playing functions */
16148 /* ------------------------------------------------------------------------- */
16150 static int *loop_sound_frame = NULL;
16151 static int *loop_sound_volume = NULL;
16153 void InitPlayLevelSound()
16155 int num_sounds = getSoundListSize();
16157 checked_free(loop_sound_frame);
16158 checked_free(loop_sound_volume);
16160 loop_sound_frame = checked_calloc(num_sounds * sizeof(int));
16161 loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
16164 static void PlayLevelSound(int x, int y, int nr)
16166 int sx = SCREENX(x), sy = SCREENY(y);
16167 int volume, stereo_position;
16168 int max_distance = 8;
16169 int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
16171 if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
16172 (!setup.sound_loops && IS_LOOP_SOUND(nr)))
16175 if (!IN_LEV_FIELD(x, y) ||
16176 sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
16177 sy < -max_distance || sy >= SCR_FIELDY + max_distance)
16180 volume = SOUND_MAX_VOLUME;
16182 if (!IN_SCR_FIELD(sx, sy))
16184 int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
16185 int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
16187 volume -= volume * (dx > dy ? dx : dy) / max_distance;
16190 stereo_position = (SOUND_MAX_LEFT +
16191 (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
16192 (SCR_FIELDX + 2 * max_distance));
16194 if (IS_LOOP_SOUND(nr))
16196 /* This assures that quieter loop sounds do not overwrite louder ones,
16197 while restarting sound volume comparison with each new game frame. */
16199 if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
16202 loop_sound_volume[nr] = volume;
16203 loop_sound_frame[nr] = FrameCounter;
16206 PlaySoundExt(nr, volume, stereo_position, type);
16209 static void PlayLevelSoundNearest(int x, int y, int sound_action)
16211 PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
16212 x > LEVELX(BX2) ? LEVELX(BX2) : x,
16213 y < LEVELY(BY1) ? LEVELY(BY1) :
16214 y > LEVELY(BY2) ? LEVELY(BY2) : y,
16218 static void PlayLevelSoundAction(int x, int y, int action)
16220 PlayLevelSoundElementAction(x, y, Feld[x][y], action);
16223 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
16225 int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
16227 if (sound_effect != SND_UNDEFINED)
16228 PlayLevelSound(x, y, sound_effect);
16231 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
16234 int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
16236 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
16237 PlayLevelSound(x, y, sound_effect);
16240 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
16242 int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
16244 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
16245 PlayLevelSound(x, y, sound_effect);
16248 static void StopLevelSoundActionIfLoop(int x, int y, int action)
16250 int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
16252 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
16253 StopSound(sound_effect);
16256 static void PlayLevelMusic()
16258 if (levelset.music[level_nr] != MUS_UNDEFINED)
16259 PlayMusic(levelset.music[level_nr]); /* from config file */
16261 PlayMusic(MAP_NOCONF_MUSIC(level_nr)); /* from music dir */
16264 void PlayLevelSound_EM(int xx, int yy, int element_em, int sample)
16266 int element = (element_em > -1 ? map_element_EM_to_RND(element_em) : 0);
16267 int offset = (BorderElement == EL_STEELWALL ? 1 : 0);
16268 int x = xx - 1 - offset;
16269 int y = yy - 1 - offset;
16274 PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
16278 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
16282 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
16286 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
16290 PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
16294 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
16298 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
16301 case SAMPLE_android_clone:
16302 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
16305 case SAMPLE_android_move:
16306 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
16309 case SAMPLE_spring:
16310 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
16314 PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
16318 PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
16321 case SAMPLE_eater_eat:
16322 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
16326 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
16329 case SAMPLE_collect:
16330 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
16333 case SAMPLE_diamond:
16334 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
16337 case SAMPLE_squash:
16338 /* !!! CHECK THIS !!! */
16340 PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
16342 PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
16346 case SAMPLE_wonderfall:
16347 PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
16351 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
16355 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
16359 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
16363 PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
16367 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
16371 PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
16374 case SAMPLE_wonder:
16375 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
16379 PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
16382 case SAMPLE_exit_open:
16383 PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
16386 case SAMPLE_exit_leave:
16387 PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
16390 case SAMPLE_dynamite:
16391 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
16395 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
16399 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
16403 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
16407 PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
16411 PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
16415 PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
16419 PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
16424 void PlayLevelSound_SP(int xx, int yy, int element_sp, int action_sp)
16426 int element = map_element_SP_to_RND(element_sp);
16427 int action = map_action_SP_to_RND(action_sp);
16428 int offset = (setup.sp_show_border_elements ? 0 : 1);
16429 int x = xx - offset;
16430 int y = yy - offset;
16433 printf("::: %d -> %d\n", element_sp, action_sp);
16436 PlayLevelSoundElementAction(x, y, element, action);
16439 void RaiseScore(int value)
16441 local_player->score += value;
16444 game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
16446 DisplayGameControlValues();
16448 DrawGameValue_Score(local_player->score);
16452 void RaiseScoreElement(int element)
16457 case EL_BD_DIAMOND:
16458 case EL_EMERALD_YELLOW:
16459 case EL_EMERALD_RED:
16460 case EL_EMERALD_PURPLE:
16461 case EL_SP_INFOTRON:
16462 RaiseScore(level.score[SC_EMERALD]);
16465 RaiseScore(level.score[SC_DIAMOND]);
16468 RaiseScore(level.score[SC_CRYSTAL]);
16471 RaiseScore(level.score[SC_PEARL]);
16474 case EL_BD_BUTTERFLY:
16475 case EL_SP_ELECTRON:
16476 RaiseScore(level.score[SC_BUG]);
16479 case EL_BD_FIREFLY:
16480 case EL_SP_SNIKSNAK:
16481 RaiseScore(level.score[SC_SPACESHIP]);
16484 case EL_DARK_YAMYAM:
16485 RaiseScore(level.score[SC_YAMYAM]);
16488 RaiseScore(level.score[SC_ROBOT]);
16491 RaiseScore(level.score[SC_PACMAN]);
16494 RaiseScore(level.score[SC_NUT]);
16497 case EL_EM_DYNAMITE:
16498 case EL_SP_DISK_RED:
16499 case EL_DYNABOMB_INCREASE_NUMBER:
16500 case EL_DYNABOMB_INCREASE_SIZE:
16501 case EL_DYNABOMB_INCREASE_POWER:
16502 RaiseScore(level.score[SC_DYNAMITE]);
16504 case EL_SHIELD_NORMAL:
16505 case EL_SHIELD_DEADLY:
16506 RaiseScore(level.score[SC_SHIELD]);
16508 case EL_EXTRA_TIME:
16509 RaiseScore(level.extra_time_score);
16523 case EL_DC_KEY_WHITE:
16524 RaiseScore(level.score[SC_KEY]);
16527 RaiseScore(element_info[element].collect_score);
16532 void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message)
16534 if (skip_request || Request(message, REQ_ASK | REQ_STAY_CLOSED))
16537 /* closing door required in case of envelope style request dialogs */
16539 CloseDoor(DOOR_CLOSE_1);
16542 #if defined(NETWORK_AVALIABLE)
16543 if (options.network)
16544 SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
16553 FadeSkipNextFadeIn();
16555 fading = fading_none;
16559 OpenDoor(DOOR_CLOSE_1);
16562 game_status = GAME_MODE_MAIN;
16565 DrawAndFadeInMainMenu(REDRAW_FIELD);
16573 FadeOut(REDRAW_FIELD);
16576 game_status = GAME_MODE_MAIN;
16578 DrawAndFadeInMainMenu(REDRAW_FIELD);
16582 else /* continue playing the game */
16584 if (tape.playing && tape.deactivate_display)
16585 TapeDeactivateDisplayOff(TRUE);
16587 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
16589 if (tape.playing && tape.deactivate_display)
16590 TapeDeactivateDisplayOn();
16594 void RequestQuitGame(boolean ask_if_really_quit)
16596 boolean quick_quit = (!ask_if_really_quit || level_editor_test_game);
16597 boolean skip_request = AllPlayersGone || quick_quit;
16599 RequestQuitGameExt(skip_request, quick_quit,
16600 "Do you really want to quit the game?");
16604 /* ------------------------------------------------------------------------- */
16605 /* random generator functions */
16606 /* ------------------------------------------------------------------------- */
16608 unsigned int InitEngineRandom_RND(int seed)
16610 game.num_random_calls = 0;
16613 unsigned int rnd_seed = InitEngineRandom(seed);
16615 printf("::: START RND: %d\n", rnd_seed);
16620 return InitEngineRandom(seed);
16626 unsigned int RND(int max)
16630 game.num_random_calls++;
16632 return GetEngineRandom(max);
16639 /* ------------------------------------------------------------------------- */
16640 /* game engine snapshot handling functions */
16641 /* ------------------------------------------------------------------------- */
16643 struct EngineSnapshotInfo
16645 /* runtime values for custom element collect score */
16646 int collect_score[NUM_CUSTOM_ELEMENTS];
16648 /* runtime values for group element choice position */
16649 int choice_pos[NUM_GROUP_ELEMENTS];
16651 /* runtime values for belt position animations */
16652 int belt_graphic[4][NUM_BELT_PARTS];
16653 int belt_anim_mode[4][NUM_BELT_PARTS];
16656 static struct EngineSnapshotInfo engine_snapshot_rnd;
16657 static char *snapshot_level_identifier = NULL;
16658 static int snapshot_level_nr = -1;
16660 static void SaveEngineSnapshotValues_RND()
16662 static int belt_base_active_element[4] =
16664 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
16665 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
16666 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
16667 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
16671 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
16673 int element = EL_CUSTOM_START + i;
16675 engine_snapshot_rnd.collect_score[i] = element_info[element].collect_score;
16678 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
16680 int element = EL_GROUP_START + i;
16682 engine_snapshot_rnd.choice_pos[i] = element_info[element].group->choice_pos;
16685 for (i = 0; i < 4; i++)
16687 for (j = 0; j < NUM_BELT_PARTS; j++)
16689 int element = belt_base_active_element[i] + j;
16690 int graphic = el2img(element);
16691 int anim_mode = graphic_info[graphic].anim_mode;
16693 engine_snapshot_rnd.belt_graphic[i][j] = graphic;
16694 engine_snapshot_rnd.belt_anim_mode[i][j] = anim_mode;
16699 static void LoadEngineSnapshotValues_RND()
16701 unsigned int num_random_calls = game.num_random_calls;
16704 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
16706 int element = EL_CUSTOM_START + i;
16708 element_info[element].collect_score = engine_snapshot_rnd.collect_score[i];
16711 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
16713 int element = EL_GROUP_START + i;
16715 element_info[element].group->choice_pos = engine_snapshot_rnd.choice_pos[i];
16718 for (i = 0; i < 4; i++)
16720 for (j = 0; j < NUM_BELT_PARTS; j++)
16722 int graphic = engine_snapshot_rnd.belt_graphic[i][j];
16723 int anim_mode = engine_snapshot_rnd.belt_anim_mode[i][j];
16725 graphic_info[graphic].anim_mode = anim_mode;
16729 if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
16731 InitRND(tape.random_seed);
16732 for (i = 0; i < num_random_calls; i++)
16736 if (game.num_random_calls != num_random_calls)
16738 Error(ERR_INFO, "number of random calls out of sync");
16739 Error(ERR_INFO, "number of random calls should be %d", num_random_calls);
16740 Error(ERR_INFO, "number of random calls is %d", game.num_random_calls);
16741 Error(ERR_EXIT, "this should not happen -- please debug");
16745 void FreeEngineSnapshot()
16747 FreeEngineSnapshotBuffers();
16749 setString(&snapshot_level_identifier, NULL);
16750 snapshot_level_nr = -1;
16753 void SaveEngineSnapshot()
16755 /* do not save snapshots from editor */
16756 if (level_editor_test_game)
16759 /* free previous snapshot buffers, if needed */
16760 FreeEngineSnapshotBuffers();
16763 /* copy some special values to a structure better suited for the snapshot */
16765 if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
16766 SaveEngineSnapshotValues_RND();
16767 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
16768 SaveEngineSnapshotValues_EM();
16769 if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
16770 SaveEngineSnapshotValues_SP();
16772 /* save values stored in special snapshot structure */
16774 if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
16775 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
16776 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
16777 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
16778 if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
16779 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_sp));
16781 /* copy some special values to a structure better suited for the snapshot */
16783 SaveEngineSnapshotValues_RND();
16784 SaveEngineSnapshotValues_EM();
16785 SaveEngineSnapshotValues_SP();
16787 /* save values stored in special snapshot structure */
16789 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
16790 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
16791 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_sp));
16794 /* save further RND engine values */
16796 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(stored_player));
16797 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(game));
16798 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(tape));
16800 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ZX));
16801 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ZY));
16802 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExitX));
16803 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExitY));
16805 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(FrameCounter));
16806 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimeFrames));
16807 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimePlayed));
16808 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimeLeft));
16809 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TapeTime));
16811 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenMovDir));
16812 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenMovPos));
16813 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenGfxPos));
16815 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScrollStepSize));
16817 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AllPlayersGone));
16819 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt));
16820 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2));
16822 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Feld));
16823 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovPos));
16824 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovDir));
16825 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovDelay));
16826 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeDelay));
16827 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangePage));
16828 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CustomValue));
16829 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Store));
16830 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Store2));
16831 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(StorePlayer));
16832 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Back));
16833 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaNr));
16834 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(WasJustMoving));
16835 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(WasJustFalling));
16836 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CheckCollision));
16837 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CheckImpact));
16838 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Stop));
16839 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Pushed));
16841 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeCount));
16842 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeEvent));
16844 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodePhase));
16845 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodeDelay));
16846 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodeField));
16848 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(RunnerVisit));
16849 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(PlayerVisit));
16851 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxFrame));
16852 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxRandom));
16853 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxElement));
16854 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxAction));
16855 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxDir));
16857 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(scroll_x));
16858 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(scroll_y));
16860 /* save level identification information */
16862 setString(&snapshot_level_identifier, leveldir_current->identifier);
16863 snapshot_level_nr = level_nr;
16866 ListNode *node = engine_snapshot_list_rnd;
16869 while (node != NULL)
16871 num_bytes += ((struct EngineSnapshotNodeInfo *)node->content)->size;
16876 printf("::: size of engine snapshot: %d bytes\n", num_bytes);
16880 void LoadEngineSnapshot()
16882 /* restore generically stored snapshot buffers */
16884 LoadEngineSnapshotBuffers();
16886 /* restore special values from snapshot structure */
16889 if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
16890 LoadEngineSnapshotValues_RND();
16891 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
16892 LoadEngineSnapshotValues_EM();
16893 if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
16894 LoadEngineSnapshotValues_SP();
16896 LoadEngineSnapshotValues_RND();
16897 LoadEngineSnapshotValues_EM();
16898 LoadEngineSnapshotValues_SP();
16902 printf("::: %d, %d (LoadEngineSnapshot / 1)\n", scroll_x, scroll_y);
16906 // needed if tile size was different when saving and loading engine snapshot
16907 if (local_player->present)
16909 scroll_x = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
16910 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
16911 local_player->jx - MIDPOSX);
16913 scroll_y = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
16914 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
16915 local_player->jy - MIDPOSY);
16920 printf("::: %d, %d (LoadEngineSnapshot / 1)\n", scroll_x, scroll_y);
16924 boolean CheckEngineSnapshot()
16926 return (strEqual(snapshot_level_identifier, leveldir_current->identifier) &&
16927 snapshot_level_nr == level_nr);
16931 /* ---------- new game button stuff ---------------------------------------- */
16939 } gamebutton_info[NUM_GAME_BUTTONS] =
16942 IMG_GAME_BUTTON_GFX_STOP, &game.button.stop,
16943 GAME_CTRL_ID_STOP, "stop game"
16946 IMG_GAME_BUTTON_GFX_PAUSE, &game.button.pause,
16947 GAME_CTRL_ID_PAUSE, "pause game"
16950 IMG_GAME_BUTTON_GFX_PLAY, &game.button.play,
16951 GAME_CTRL_ID_PLAY, "play game"
16954 IMG_GAME_BUTTON_GFX_SOUND_MUSIC, &game.button.sound_music,
16955 SOUND_CTRL_ID_MUSIC, "background music on/off"
16958 IMG_GAME_BUTTON_GFX_SOUND_LOOPS, &game.button.sound_loops,
16959 SOUND_CTRL_ID_LOOPS, "sound loops on/off"
16962 IMG_GAME_BUTTON_GFX_SOUND_SIMPLE, &game.button.sound_simple,
16963 SOUND_CTRL_ID_SIMPLE, "normal sounds on/off"
16966 IMG_GAME_BUTTON_GFX_SAVE, &game.button.save,
16967 GAME_CTRL_ID_SAVE, "save game"
16970 IMG_GAME_BUTTON_GFX_LOAD, &game.button.load,
16971 GAME_CTRL_ID_LOAD, "load game"
16975 void CreateGameButtons()
16979 for (i = 0; i < NUM_GAME_BUTTONS; i++)
16981 struct GraphicInfo *gfx = &graphic_info[gamebutton_info[i].graphic];
16982 struct Rect *pos = gamebutton_info[i].pos;
16983 struct GadgetInfo *gi;
16986 unsigned int event_mask;
16987 int base_x = (tape.show_game_buttons ? VX : DX);
16988 int base_y = (tape.show_game_buttons ? VY : DY);
16989 int gd_x = gfx->src_x;
16990 int gd_y = gfx->src_y;
16991 int gd_xp = gfx->src_x + gfx->pressed_xoffset;
16992 int gd_yp = gfx->src_y + gfx->pressed_yoffset;
16993 int gd_xa = gfx->src_x + gfx->active_xoffset;
16994 int gd_ya = gfx->src_y + gfx->active_yoffset;
16995 int gd_xap = gfx->src_x + gfx->active_xoffset + gfx->pressed_xoffset;
16996 int gd_yap = gfx->src_y + gfx->active_yoffset + gfx->pressed_yoffset;
16999 if (gfx->bitmap == NULL)
17001 game_gadget[id] = NULL;
17006 if (id == GAME_CTRL_ID_STOP ||
17007 id == GAME_CTRL_ID_PAUSE ||
17008 id == GAME_CTRL_ID_PLAY ||
17009 id == GAME_CTRL_ID_SAVE ||
17010 id == GAME_CTRL_ID_LOAD)
17012 button_type = GD_TYPE_NORMAL_BUTTON;
17014 event_mask = GD_EVENT_RELEASED;
17018 button_type = GD_TYPE_CHECK_BUTTON;
17020 ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
17021 (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
17022 (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
17023 event_mask = GD_EVENT_PRESSED;
17026 gi = CreateGadget(GDI_CUSTOM_ID, id,
17027 GDI_INFO_TEXT, gamebutton_info[i].infotext,
17028 GDI_X, base_x + GDI_ACTIVE_POS(pos->x),
17029 GDI_Y, base_y + GDI_ACTIVE_POS(pos->y),
17030 GDI_WIDTH, gfx->width,
17031 GDI_HEIGHT, gfx->height,
17032 GDI_TYPE, button_type,
17033 GDI_STATE, GD_BUTTON_UNPRESSED,
17034 GDI_CHECKED, checked,
17035 GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
17036 GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
17037 GDI_ALT_DESIGN_UNPRESSED, gfx->bitmap, gd_xa, gd_ya,
17038 GDI_ALT_DESIGN_PRESSED, gfx->bitmap, gd_xap, gd_yap,
17039 GDI_DIRECT_DRAW, FALSE,
17040 GDI_EVENT_MASK, event_mask,
17041 GDI_CALLBACK_ACTION, HandleGameButtons,
17045 Error(ERR_EXIT, "cannot create gadget");
17047 game_gadget[id] = gi;
17051 void FreeGameButtons()
17055 for (i = 0; i < NUM_GAME_BUTTONS; i++)
17056 FreeGadget(game_gadget[i]);
17059 void MapGameButtons()
17063 for (i = 0; i < NUM_GAME_BUTTONS; i++)
17064 MapGadget(game_gadget[i]);
17067 void UnmapGameButtons()
17071 for (i = 0; i < NUM_GAME_BUTTONS; i++)
17072 UnmapGadget(game_gadget[i]);
17075 void RedrawGameButtons()
17079 for (i = 0; i < NUM_GAME_BUTTONS; i++)
17080 RedrawGadget(game_gadget[i]);
17082 // RedrawGadget() may have set REDRAW_ALL if buttons are defined off-area
17083 redraw_mask &= ~REDRAW_ALL;
17086 static void HandleGameButtonsExt(int id)
17088 boolean handle_game_buttons =
17089 (game_status == GAME_MODE_PLAYING ||
17090 (game_status == GAME_MODE_MAIN && tape.show_game_buttons));
17092 if (!handle_game_buttons)
17097 case GAME_CTRL_ID_STOP:
17098 if (game_status == GAME_MODE_MAIN)
17104 RequestQuitGame(TRUE);
17108 case GAME_CTRL_ID_PAUSE:
17109 if (options.network && game_status == GAME_MODE_PLAYING)
17111 #if defined(NETWORK_AVALIABLE)
17113 SendToServer_ContinuePlaying();
17115 SendToServer_PausePlaying();
17119 TapeTogglePause(TAPE_TOGGLE_MANUAL);
17122 case GAME_CTRL_ID_PLAY:
17123 if (game_status == GAME_MODE_MAIN)
17125 StartGameActions(options.network, setup.autorecord, level.random_seed);
17127 else if (tape.pausing)
17129 #if defined(NETWORK_AVALIABLE)
17130 if (options.network)
17131 SendToServer_ContinuePlaying();
17135 tape.pausing = FALSE;
17136 DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF, 0);
17141 case SOUND_CTRL_ID_MUSIC:
17142 if (setup.sound_music)
17144 setup.sound_music = FALSE;
17148 else if (audio.music_available)
17150 setup.sound = setup.sound_music = TRUE;
17152 SetAudioMode(setup.sound);
17158 case SOUND_CTRL_ID_LOOPS:
17159 if (setup.sound_loops)
17160 setup.sound_loops = FALSE;
17161 else if (audio.loops_available)
17163 setup.sound = setup.sound_loops = TRUE;
17165 SetAudioMode(setup.sound);
17169 case SOUND_CTRL_ID_SIMPLE:
17170 if (setup.sound_simple)
17171 setup.sound_simple = FALSE;
17172 else if (audio.sound_available)
17174 setup.sound = setup.sound_simple = TRUE;
17176 SetAudioMode(setup.sound);
17180 case GAME_CTRL_ID_SAVE:
17184 case GAME_CTRL_ID_LOAD:
17193 static void HandleGameButtons(struct GadgetInfo *gi)
17195 HandleGameButtonsExt(gi->custom_id);
17198 void HandleSoundButtonKeys(Key key)
17201 if (key == setup.shortcut.sound_simple)
17202 ClickOnGadget(game_gadget[SOUND_CTRL_ID_SIMPLE], MB_LEFTBUTTON);
17203 else if (key == setup.shortcut.sound_loops)
17204 ClickOnGadget(game_gadget[SOUND_CTRL_ID_LOOPS], MB_LEFTBUTTON);
17205 else if (key == setup.shortcut.sound_music)
17206 ClickOnGadget(game_gadget[SOUND_CTRL_ID_MUSIC], MB_LEFTBUTTON);
17208 if (key == setup.shortcut.sound_simple)
17209 HandleGameButtonsExt(SOUND_CTRL_ID_SIMPLE);
17210 else if (key == setup.shortcut.sound_loops)
17211 HandleGameButtonsExt(SOUND_CTRL_ID_LOOPS);
17212 else if (key == setup.shortcut.sound_music)
17213 HandleGameButtonsExt(SOUND_CTRL_ID_MUSIC);