1 // ============================================================================
2 // Rocks'n'Diamonds - McDuffin Strikes Back!
3 // ----------------------------------------------------------------------------
4 // (c) 1995-2014 by Artsoft Entertainment
7 // http://www.artsoft.org/
8 // ----------------------------------------------------------------------------
10 // ============================================================================
12 #include "libgame/libgame.h"
25 #define DEBUG_INIT_PLAYER 1
26 #define DEBUG_PLAYER_ACTIONS 0
28 /* EXPERIMENTAL STUFF */
29 #define USE_NEW_AMOEBA_CODE FALSE
31 /* EXPERIMENTAL STUFF */
32 #define USE_QUICKSAND_BD_ROCK_BUGFIX 0
33 #define USE_QUICKSAND_IMPACT_BUGFIX 0
34 #define USE_DELAYED_GFX_REDRAW 0
35 #define USE_NEW_PLAYER_ASSIGNMENTS 1
37 #if USE_DELAYED_GFX_REDRAW
38 #define TEST_DrawLevelField(x, y) \
39 GfxRedraw[x][y] |= GFX_REDRAW_TILE
40 #define TEST_DrawLevelFieldCrumbled(x, y) \
41 GfxRedraw[x][y] |= GFX_REDRAW_TILE_CRUMBLED
42 #define TEST_DrawLevelFieldCrumbledNeighbours(x, y) \
43 GfxRedraw[x][y] |= GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS
44 #define TEST_DrawTwinkleOnField(x, y) \
45 GfxRedraw[x][y] |= GFX_REDRAW_TILE_TWINKLED
47 #define TEST_DrawLevelField(x, y) \
49 #define TEST_DrawLevelFieldCrumbled(x, y) \
50 DrawLevelFieldCrumbled(x, y)
51 #define TEST_DrawLevelFieldCrumbledNeighbours(x, y) \
52 DrawLevelFieldCrumbledNeighbours(x, y)
53 #define TEST_DrawTwinkleOnField(x, y) \
54 DrawTwinkleOnField(x, y)
63 /* for MovePlayer() */
64 #define MP_NO_ACTION 0
67 #define MP_DONT_RUN_INTO (MP_MOVING | MP_ACTION)
69 /* for ScrollPlayer() */
71 #define SCROLL_GO_ON 1
73 /* for Bang()/Explode() */
74 #define EX_PHASE_START 0
75 #define EX_TYPE_NONE 0
76 #define EX_TYPE_NORMAL (1 << 0)
77 #define EX_TYPE_CENTER (1 << 1)
78 #define EX_TYPE_BORDER (1 << 2)
79 #define EX_TYPE_CROSS (1 << 3)
80 #define EX_TYPE_DYNA (1 << 4)
81 #define EX_TYPE_SINGLE_TILE (EX_TYPE_CENTER | EX_TYPE_BORDER)
83 #define PANEL_OFF() (local_player->LevelSolved_PanelOff)
84 #define PANEL_DEACTIVATED(p) ((p)->x < 0 || (p)->y < 0 || PANEL_OFF())
85 #define PANEL_XPOS(p) (DX + ALIGNED_TEXT_XPOS(p))
86 #define PANEL_YPOS(p) (DY + ALIGNED_TEXT_YPOS(p))
88 /* game panel display and control definitions */
89 #define GAME_PANEL_LEVEL_NUMBER 0
90 #define GAME_PANEL_GEMS 1
91 #define GAME_PANEL_INVENTORY_COUNT 2
92 #define GAME_PANEL_INVENTORY_FIRST_1 3
93 #define GAME_PANEL_INVENTORY_FIRST_2 4
94 #define GAME_PANEL_INVENTORY_FIRST_3 5
95 #define GAME_PANEL_INVENTORY_FIRST_4 6
96 #define GAME_PANEL_INVENTORY_FIRST_5 7
97 #define GAME_PANEL_INVENTORY_FIRST_6 8
98 #define GAME_PANEL_INVENTORY_FIRST_7 9
99 #define GAME_PANEL_INVENTORY_FIRST_8 10
100 #define GAME_PANEL_INVENTORY_LAST_1 11
101 #define GAME_PANEL_INVENTORY_LAST_2 12
102 #define GAME_PANEL_INVENTORY_LAST_3 13
103 #define GAME_PANEL_INVENTORY_LAST_4 14
104 #define GAME_PANEL_INVENTORY_LAST_5 15
105 #define GAME_PANEL_INVENTORY_LAST_6 16
106 #define GAME_PANEL_INVENTORY_LAST_7 17
107 #define GAME_PANEL_INVENTORY_LAST_8 18
108 #define GAME_PANEL_KEY_1 19
109 #define GAME_PANEL_KEY_2 20
110 #define GAME_PANEL_KEY_3 21
111 #define GAME_PANEL_KEY_4 22
112 #define GAME_PANEL_KEY_5 23
113 #define GAME_PANEL_KEY_6 24
114 #define GAME_PANEL_KEY_7 25
115 #define GAME_PANEL_KEY_8 26
116 #define GAME_PANEL_KEY_WHITE 27
117 #define GAME_PANEL_KEY_WHITE_COUNT 28
118 #define GAME_PANEL_SCORE 29
119 #define GAME_PANEL_HIGHSCORE 30
120 #define GAME_PANEL_TIME 31
121 #define GAME_PANEL_TIME_HH 32
122 #define GAME_PANEL_TIME_MM 33
123 #define GAME_PANEL_TIME_SS 34
124 #define GAME_PANEL_FRAME 35
125 #define GAME_PANEL_SHIELD_NORMAL 36
126 #define GAME_PANEL_SHIELD_NORMAL_TIME 37
127 #define GAME_PANEL_SHIELD_DEADLY 38
128 #define GAME_PANEL_SHIELD_DEADLY_TIME 39
129 #define GAME_PANEL_EXIT 40
130 #define GAME_PANEL_EMC_MAGIC_BALL 41
131 #define GAME_PANEL_EMC_MAGIC_BALL_SWITCH 42
132 #define GAME_PANEL_LIGHT_SWITCH 43
133 #define GAME_PANEL_LIGHT_SWITCH_TIME 44
134 #define GAME_PANEL_TIMEGATE_SWITCH 45
135 #define GAME_PANEL_TIMEGATE_SWITCH_TIME 46
136 #define GAME_PANEL_SWITCHGATE_SWITCH 47
137 #define GAME_PANEL_EMC_LENSES 48
138 #define GAME_PANEL_EMC_LENSES_TIME 49
139 #define GAME_PANEL_EMC_MAGNIFIER 50
140 #define GAME_PANEL_EMC_MAGNIFIER_TIME 51
141 #define GAME_PANEL_BALLOON_SWITCH 52
142 #define GAME_PANEL_DYNABOMB_NUMBER 53
143 #define GAME_PANEL_DYNABOMB_SIZE 54
144 #define GAME_PANEL_DYNABOMB_POWER 55
145 #define GAME_PANEL_PENGUINS 56
146 #define GAME_PANEL_SOKOBAN_OBJECTS 57
147 #define GAME_PANEL_SOKOBAN_FIELDS 58
148 #define GAME_PANEL_ROBOT_WHEEL 59
149 #define GAME_PANEL_CONVEYOR_BELT_1 60
150 #define GAME_PANEL_CONVEYOR_BELT_2 61
151 #define GAME_PANEL_CONVEYOR_BELT_3 62
152 #define GAME_PANEL_CONVEYOR_BELT_4 63
153 #define GAME_PANEL_CONVEYOR_BELT_1_SWITCH 64
154 #define GAME_PANEL_CONVEYOR_BELT_2_SWITCH 65
155 #define GAME_PANEL_CONVEYOR_BELT_3_SWITCH 66
156 #define GAME_PANEL_CONVEYOR_BELT_4_SWITCH 67
157 #define GAME_PANEL_MAGIC_WALL 68
158 #define GAME_PANEL_MAGIC_WALL_TIME 69
159 #define GAME_PANEL_GRAVITY_STATE 70
160 #define GAME_PANEL_GRAPHIC_1 71
161 #define GAME_PANEL_GRAPHIC_2 72
162 #define GAME_PANEL_GRAPHIC_3 73
163 #define GAME_PANEL_GRAPHIC_4 74
164 #define GAME_PANEL_GRAPHIC_5 75
165 #define GAME_PANEL_GRAPHIC_6 76
166 #define GAME_PANEL_GRAPHIC_7 77
167 #define GAME_PANEL_GRAPHIC_8 78
168 #define GAME_PANEL_ELEMENT_1 79
169 #define GAME_PANEL_ELEMENT_2 80
170 #define GAME_PANEL_ELEMENT_3 81
171 #define GAME_PANEL_ELEMENT_4 82
172 #define GAME_PANEL_ELEMENT_5 83
173 #define GAME_PANEL_ELEMENT_6 84
174 #define GAME_PANEL_ELEMENT_7 85
175 #define GAME_PANEL_ELEMENT_8 86
176 #define GAME_PANEL_ELEMENT_COUNT_1 87
177 #define GAME_PANEL_ELEMENT_COUNT_2 88
178 #define GAME_PANEL_ELEMENT_COUNT_3 89
179 #define GAME_PANEL_ELEMENT_COUNT_4 90
180 #define GAME_PANEL_ELEMENT_COUNT_5 91
181 #define GAME_PANEL_ELEMENT_COUNT_6 92
182 #define GAME_PANEL_ELEMENT_COUNT_7 93
183 #define GAME_PANEL_ELEMENT_COUNT_8 94
184 #define GAME_PANEL_CE_SCORE_1 95
185 #define GAME_PANEL_CE_SCORE_2 96
186 #define GAME_PANEL_CE_SCORE_3 97
187 #define GAME_PANEL_CE_SCORE_4 98
188 #define GAME_PANEL_CE_SCORE_5 99
189 #define GAME_PANEL_CE_SCORE_6 100
190 #define GAME_PANEL_CE_SCORE_7 101
191 #define GAME_PANEL_CE_SCORE_8 102
192 #define GAME_PANEL_CE_SCORE_1_ELEMENT 103
193 #define GAME_PANEL_CE_SCORE_2_ELEMENT 104
194 #define GAME_PANEL_CE_SCORE_3_ELEMENT 105
195 #define GAME_PANEL_CE_SCORE_4_ELEMENT 106
196 #define GAME_PANEL_CE_SCORE_5_ELEMENT 107
197 #define GAME_PANEL_CE_SCORE_6_ELEMENT 108
198 #define GAME_PANEL_CE_SCORE_7_ELEMENT 109
199 #define GAME_PANEL_CE_SCORE_8_ELEMENT 110
200 #define GAME_PANEL_PLAYER_NAME 111
201 #define GAME_PANEL_LEVEL_NAME 112
202 #define GAME_PANEL_LEVEL_AUTHOR 113
204 #define NUM_GAME_PANEL_CONTROLS 114
206 struct GamePanelOrderInfo
212 static struct GamePanelOrderInfo game_panel_order[NUM_GAME_PANEL_CONTROLS];
214 struct GamePanelControlInfo
218 struct TextPosInfo *pos;
221 int value, last_value;
222 int frame, last_frame;
227 static struct GamePanelControlInfo game_panel_controls[] =
230 GAME_PANEL_LEVEL_NUMBER,
231 &game.panel.level_number,
240 GAME_PANEL_INVENTORY_COUNT,
241 &game.panel.inventory_count,
245 GAME_PANEL_INVENTORY_FIRST_1,
246 &game.panel.inventory_first[0],
250 GAME_PANEL_INVENTORY_FIRST_2,
251 &game.panel.inventory_first[1],
255 GAME_PANEL_INVENTORY_FIRST_3,
256 &game.panel.inventory_first[2],
260 GAME_PANEL_INVENTORY_FIRST_4,
261 &game.panel.inventory_first[3],
265 GAME_PANEL_INVENTORY_FIRST_5,
266 &game.panel.inventory_first[4],
270 GAME_PANEL_INVENTORY_FIRST_6,
271 &game.panel.inventory_first[5],
275 GAME_PANEL_INVENTORY_FIRST_7,
276 &game.panel.inventory_first[6],
280 GAME_PANEL_INVENTORY_FIRST_8,
281 &game.panel.inventory_first[7],
285 GAME_PANEL_INVENTORY_LAST_1,
286 &game.panel.inventory_last[0],
290 GAME_PANEL_INVENTORY_LAST_2,
291 &game.panel.inventory_last[1],
295 GAME_PANEL_INVENTORY_LAST_3,
296 &game.panel.inventory_last[2],
300 GAME_PANEL_INVENTORY_LAST_4,
301 &game.panel.inventory_last[3],
305 GAME_PANEL_INVENTORY_LAST_5,
306 &game.panel.inventory_last[4],
310 GAME_PANEL_INVENTORY_LAST_6,
311 &game.panel.inventory_last[5],
315 GAME_PANEL_INVENTORY_LAST_7,
316 &game.panel.inventory_last[6],
320 GAME_PANEL_INVENTORY_LAST_8,
321 &game.panel.inventory_last[7],
365 GAME_PANEL_KEY_WHITE,
366 &game.panel.key_white,
370 GAME_PANEL_KEY_WHITE_COUNT,
371 &game.panel.key_white_count,
380 GAME_PANEL_HIGHSCORE,
381 &game.panel.highscore,
410 GAME_PANEL_SHIELD_NORMAL,
411 &game.panel.shield_normal,
415 GAME_PANEL_SHIELD_NORMAL_TIME,
416 &game.panel.shield_normal_time,
420 GAME_PANEL_SHIELD_DEADLY,
421 &game.panel.shield_deadly,
425 GAME_PANEL_SHIELD_DEADLY_TIME,
426 &game.panel.shield_deadly_time,
435 GAME_PANEL_EMC_MAGIC_BALL,
436 &game.panel.emc_magic_ball,
440 GAME_PANEL_EMC_MAGIC_BALL_SWITCH,
441 &game.panel.emc_magic_ball_switch,
445 GAME_PANEL_LIGHT_SWITCH,
446 &game.panel.light_switch,
450 GAME_PANEL_LIGHT_SWITCH_TIME,
451 &game.panel.light_switch_time,
455 GAME_PANEL_TIMEGATE_SWITCH,
456 &game.panel.timegate_switch,
460 GAME_PANEL_TIMEGATE_SWITCH_TIME,
461 &game.panel.timegate_switch_time,
465 GAME_PANEL_SWITCHGATE_SWITCH,
466 &game.panel.switchgate_switch,
470 GAME_PANEL_EMC_LENSES,
471 &game.panel.emc_lenses,
475 GAME_PANEL_EMC_LENSES_TIME,
476 &game.panel.emc_lenses_time,
480 GAME_PANEL_EMC_MAGNIFIER,
481 &game.panel.emc_magnifier,
485 GAME_PANEL_EMC_MAGNIFIER_TIME,
486 &game.panel.emc_magnifier_time,
490 GAME_PANEL_BALLOON_SWITCH,
491 &game.panel.balloon_switch,
495 GAME_PANEL_DYNABOMB_NUMBER,
496 &game.panel.dynabomb_number,
500 GAME_PANEL_DYNABOMB_SIZE,
501 &game.panel.dynabomb_size,
505 GAME_PANEL_DYNABOMB_POWER,
506 &game.panel.dynabomb_power,
511 &game.panel.penguins,
515 GAME_PANEL_SOKOBAN_OBJECTS,
516 &game.panel.sokoban_objects,
520 GAME_PANEL_SOKOBAN_FIELDS,
521 &game.panel.sokoban_fields,
525 GAME_PANEL_ROBOT_WHEEL,
526 &game.panel.robot_wheel,
530 GAME_PANEL_CONVEYOR_BELT_1,
531 &game.panel.conveyor_belt[0],
535 GAME_PANEL_CONVEYOR_BELT_2,
536 &game.panel.conveyor_belt[1],
540 GAME_PANEL_CONVEYOR_BELT_3,
541 &game.panel.conveyor_belt[2],
545 GAME_PANEL_CONVEYOR_BELT_4,
546 &game.panel.conveyor_belt[3],
550 GAME_PANEL_CONVEYOR_BELT_1_SWITCH,
551 &game.panel.conveyor_belt_switch[0],
555 GAME_PANEL_CONVEYOR_BELT_2_SWITCH,
556 &game.panel.conveyor_belt_switch[1],
560 GAME_PANEL_CONVEYOR_BELT_3_SWITCH,
561 &game.panel.conveyor_belt_switch[2],
565 GAME_PANEL_CONVEYOR_BELT_4_SWITCH,
566 &game.panel.conveyor_belt_switch[3],
570 GAME_PANEL_MAGIC_WALL,
571 &game.panel.magic_wall,
575 GAME_PANEL_MAGIC_WALL_TIME,
576 &game.panel.magic_wall_time,
580 GAME_PANEL_GRAVITY_STATE,
581 &game.panel.gravity_state,
585 GAME_PANEL_GRAPHIC_1,
586 &game.panel.graphic[0],
590 GAME_PANEL_GRAPHIC_2,
591 &game.panel.graphic[1],
595 GAME_PANEL_GRAPHIC_3,
596 &game.panel.graphic[2],
600 GAME_PANEL_GRAPHIC_4,
601 &game.panel.graphic[3],
605 GAME_PANEL_GRAPHIC_5,
606 &game.panel.graphic[4],
610 GAME_PANEL_GRAPHIC_6,
611 &game.panel.graphic[5],
615 GAME_PANEL_GRAPHIC_7,
616 &game.panel.graphic[6],
620 GAME_PANEL_GRAPHIC_8,
621 &game.panel.graphic[7],
625 GAME_PANEL_ELEMENT_1,
626 &game.panel.element[0],
630 GAME_PANEL_ELEMENT_2,
631 &game.panel.element[1],
635 GAME_PANEL_ELEMENT_3,
636 &game.panel.element[2],
640 GAME_PANEL_ELEMENT_4,
641 &game.panel.element[3],
645 GAME_PANEL_ELEMENT_5,
646 &game.panel.element[4],
650 GAME_PANEL_ELEMENT_6,
651 &game.panel.element[5],
655 GAME_PANEL_ELEMENT_7,
656 &game.panel.element[6],
660 GAME_PANEL_ELEMENT_8,
661 &game.panel.element[7],
665 GAME_PANEL_ELEMENT_COUNT_1,
666 &game.panel.element_count[0],
670 GAME_PANEL_ELEMENT_COUNT_2,
671 &game.panel.element_count[1],
675 GAME_PANEL_ELEMENT_COUNT_3,
676 &game.panel.element_count[2],
680 GAME_PANEL_ELEMENT_COUNT_4,
681 &game.panel.element_count[3],
685 GAME_PANEL_ELEMENT_COUNT_5,
686 &game.panel.element_count[4],
690 GAME_PANEL_ELEMENT_COUNT_6,
691 &game.panel.element_count[5],
695 GAME_PANEL_ELEMENT_COUNT_7,
696 &game.panel.element_count[6],
700 GAME_PANEL_ELEMENT_COUNT_8,
701 &game.panel.element_count[7],
705 GAME_PANEL_CE_SCORE_1,
706 &game.panel.ce_score[0],
710 GAME_PANEL_CE_SCORE_2,
711 &game.panel.ce_score[1],
715 GAME_PANEL_CE_SCORE_3,
716 &game.panel.ce_score[2],
720 GAME_PANEL_CE_SCORE_4,
721 &game.panel.ce_score[3],
725 GAME_PANEL_CE_SCORE_5,
726 &game.panel.ce_score[4],
730 GAME_PANEL_CE_SCORE_6,
731 &game.panel.ce_score[5],
735 GAME_PANEL_CE_SCORE_7,
736 &game.panel.ce_score[6],
740 GAME_PANEL_CE_SCORE_8,
741 &game.panel.ce_score[7],
745 GAME_PANEL_CE_SCORE_1_ELEMENT,
746 &game.panel.ce_score_element[0],
750 GAME_PANEL_CE_SCORE_2_ELEMENT,
751 &game.panel.ce_score_element[1],
755 GAME_PANEL_CE_SCORE_3_ELEMENT,
756 &game.panel.ce_score_element[2],
760 GAME_PANEL_CE_SCORE_4_ELEMENT,
761 &game.panel.ce_score_element[3],
765 GAME_PANEL_CE_SCORE_5_ELEMENT,
766 &game.panel.ce_score_element[4],
770 GAME_PANEL_CE_SCORE_6_ELEMENT,
771 &game.panel.ce_score_element[5],
775 GAME_PANEL_CE_SCORE_7_ELEMENT,
776 &game.panel.ce_score_element[6],
780 GAME_PANEL_CE_SCORE_8_ELEMENT,
781 &game.panel.ce_score_element[7],
785 GAME_PANEL_PLAYER_NAME,
786 &game.panel.player_name,
790 GAME_PANEL_LEVEL_NAME,
791 &game.panel.level_name,
795 GAME_PANEL_LEVEL_AUTHOR,
796 &game.panel.level_author,
807 /* values for delayed check of falling and moving elements and for collision */
808 #define CHECK_DELAY_MOVING 3
809 #define CHECK_DELAY_FALLING CHECK_DELAY_MOVING
810 #define CHECK_DELAY_COLLISION 2
811 #define CHECK_DELAY_IMPACT CHECK_DELAY_COLLISION
813 /* values for initial player move delay (initial delay counter value) */
814 #define INITIAL_MOVE_DELAY_OFF -1
815 #define INITIAL_MOVE_DELAY_ON 0
817 /* values for player movement speed (which is in fact a delay value) */
818 #define MOVE_DELAY_MIN_SPEED 32
819 #define MOVE_DELAY_NORMAL_SPEED 8
820 #define MOVE_DELAY_HIGH_SPEED 4
821 #define MOVE_DELAY_MAX_SPEED 1
823 #define DOUBLE_MOVE_DELAY(x) (x = (x < MOVE_DELAY_MIN_SPEED ? x * 2 : x))
824 #define HALVE_MOVE_DELAY(x) (x = (x > MOVE_DELAY_MAX_SPEED ? x / 2 : x))
826 #define DOUBLE_PLAYER_SPEED(p) (HALVE_MOVE_DELAY( (p)->move_delay_value))
827 #define HALVE_PLAYER_SPEED(p) (DOUBLE_MOVE_DELAY((p)->move_delay_value))
829 /* values for other actions */
830 #define MOVE_STEPSIZE_NORMAL (TILEX / MOVE_DELAY_NORMAL_SPEED)
831 #define MOVE_STEPSIZE_MIN (1)
832 #define MOVE_STEPSIZE_MAX (TILEX)
834 #define GET_DX_FROM_DIR(d) ((d) == MV_LEFT ? -1 : (d) == MV_RIGHT ? 1 : 0)
835 #define GET_DY_FROM_DIR(d) ((d) == MV_UP ? -1 : (d) == MV_DOWN ? 1 : 0)
837 #define INIT_GFX_RANDOM() (GetSimpleRandom(1000000))
839 #define GET_NEW_PUSH_DELAY(e) ( (element_info[e].push_delay_fixed) + \
840 RND(element_info[e].push_delay_random))
841 #define GET_NEW_DROP_DELAY(e) ( (element_info[e].drop_delay_fixed) + \
842 RND(element_info[e].drop_delay_random))
843 #define GET_NEW_MOVE_DELAY(e) ( (element_info[e].move_delay_fixed) + \
844 RND(element_info[e].move_delay_random))
845 #define GET_MAX_MOVE_DELAY(e) ( (element_info[e].move_delay_fixed) + \
846 (element_info[e].move_delay_random))
847 #define GET_NEW_CE_VALUE(e) ( (element_info[e].ce_value_fixed_initial) +\
848 RND(element_info[e].ce_value_random_initial))
849 #define GET_CE_SCORE(e) ( (element_info[e].collect_score))
850 #define GET_CHANGE_DELAY(c) ( ((c)->delay_fixed * (c)->delay_frames) + \
851 RND((c)->delay_random * (c)->delay_frames))
852 #define GET_CE_DELAY_VALUE(c) ( ((c)->delay_fixed) + \
853 RND((c)->delay_random))
856 #define GET_VALID_RUNTIME_ELEMENT(e) \
857 ((e) >= NUM_RUNTIME_ELEMENTS ? EL_UNKNOWN : (e))
859 #define RESOLVED_REFERENCE_ELEMENT(be, e) \
860 ((be) + (e) - EL_SELF < EL_CUSTOM_START ? EL_CUSTOM_START : \
861 (be) + (e) - EL_SELF > EL_CUSTOM_END ? EL_CUSTOM_END : \
862 (be) + (e) - EL_SELF)
864 #define GET_PLAYER_FROM_BITS(p) \
865 (EL_PLAYER_1 + ((p) != PLAYER_BITS_ANY ? log_2(p) : 0))
867 #define GET_TARGET_ELEMENT(be, e, ch, cv, cs) \
868 ((e) == EL_TRIGGER_PLAYER ? (ch)->actual_trigger_player : \
869 (e) == EL_TRIGGER_ELEMENT ? (ch)->actual_trigger_element : \
870 (e) == EL_TRIGGER_CE_VALUE ? (ch)->actual_trigger_ce_value : \
871 (e) == EL_TRIGGER_CE_SCORE ? (ch)->actual_trigger_ce_score : \
872 (e) == EL_CURRENT_CE_VALUE ? (cv) : \
873 (e) == EL_CURRENT_CE_SCORE ? (cs) : \
874 (e) >= EL_PREV_CE_8 && (e) <= EL_NEXT_CE_8 ? \
875 RESOLVED_REFERENCE_ELEMENT(be, e) : \
878 #define CAN_GROW_INTO(e) \
879 ((e) == EL_SAND || (IS_DIGGABLE(e) && level.grow_into_diggable))
881 #define ELEMENT_CAN_ENTER_FIELD_BASE_X(x, y, condition) \
882 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
885 #define ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, condition) \
886 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
887 (CAN_MOVE_INTO_ACID(e) && \
888 Feld[x][y] == EL_ACID) || \
891 #define ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, condition) \
892 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) || \
893 (CAN_MOVE_INTO_ACID(e) && \
894 Feld[x][y] == EL_ACID) || \
897 #define ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, condition) \
898 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
900 (CAN_MOVE_INTO_ACID(e) && \
901 Feld[x][y] == EL_ACID) || \
902 (DONT_COLLIDE_WITH(e) && \
904 !PLAYER_ENEMY_PROTECTED(x, y))))
906 #define ELEMENT_CAN_ENTER_FIELD(e, x, y) \
907 ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, 0)
909 #define SATELLITE_CAN_ENTER_FIELD(x, y) \
910 ELEMENT_CAN_ENTER_FIELD_BASE_2(EL_SATELLITE, x, y, 0)
912 #define ANDROID_CAN_ENTER_FIELD(e, x, y) \
913 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, Feld[x][y] == EL_EMC_PLANT)
915 #define ANDROID_CAN_CLONE_FIELD(x, y) \
916 (IN_LEV_FIELD(x, y) && (CAN_BE_CLONED_BY_ANDROID(Feld[x][y]) || \
917 CAN_BE_CLONED_BY_ANDROID(EL_TRIGGER_ELEMENT)))
919 #define ENEMY_CAN_ENTER_FIELD(e, x, y) \
920 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
922 #define YAMYAM_CAN_ENTER_FIELD(e, x, y) \
923 ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, Feld[x][y] == EL_DIAMOND)
925 #define DARK_YAMYAM_CAN_ENTER_FIELD(e, x, y) \
926 ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x,y, IS_FOOD_DARK_YAMYAM(Feld[x][y]))
928 #define PACMAN_CAN_ENTER_FIELD(e, x, y) \
929 ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, IS_AMOEBOID(Feld[x][y]))
931 #define PIG_CAN_ENTER_FIELD(e, x, y) \
932 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, IS_FOOD_PIG(Feld[x][y]))
934 #define PENGUIN_CAN_ENTER_FIELD(e, x, y) \
935 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (Feld[x][y] == EL_EXIT_OPEN || \
936 Feld[x][y] == EL_EM_EXIT_OPEN || \
937 Feld[x][y] == EL_STEEL_EXIT_OPEN || \
938 Feld[x][y] == EL_EM_STEEL_EXIT_OPEN || \
939 IS_FOOD_PENGUIN(Feld[x][y])))
940 #define DRAGON_CAN_ENTER_FIELD(e, x, y) \
941 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
943 #define MOLE_CAN_ENTER_FIELD(e, x, y, condition) \
944 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (condition))
946 #define SPRING_CAN_ENTER_FIELD(e, x, y) \
947 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
949 #define SPRING_CAN_BUMP_FROM_FIELD(x, y) \
950 (IN_LEV_FIELD(x, y) && (Feld[x][y] == EL_EMC_SPRING_BUMPER || \
951 Feld[x][y] == EL_EMC_SPRING_BUMPER_ACTIVE))
953 #define MOVE_ENTER_EL(e) (element_info[e].move_enter_element)
955 #define CE_ENTER_FIELD_COND(e, x, y) \
956 (!IS_PLAYER(x, y) && \
957 IS_EQUAL_OR_IN_GROUP(Feld[x][y], MOVE_ENTER_EL(e)))
959 #define CUSTOM_ELEMENT_CAN_ENTER_FIELD(e, x, y) \
960 ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, CE_ENTER_FIELD_COND(e, x, y))
962 #define IN_LEV_FIELD_AND_IS_FREE(x, y) (IN_LEV_FIELD(x, y) && IS_FREE(x, y))
963 #define IN_LEV_FIELD_AND_NOT_FREE(x, y) (IN_LEV_FIELD(x, y) && !IS_FREE(x, y))
965 #define ACCESS_FROM(e, d) (element_info[e].access_direction &(d))
966 #define IS_WALKABLE_FROM(e, d) (IS_WALKABLE(e) && ACCESS_FROM(e, d))
967 #define IS_PASSABLE_FROM(e, d) (IS_PASSABLE(e) && ACCESS_FROM(e, d))
968 #define IS_ACCESSIBLE_FROM(e, d) (IS_ACCESSIBLE(e) && ACCESS_FROM(e, d))
970 /* game button identifiers */
971 #define GAME_CTRL_ID_STOP 0
972 #define GAME_CTRL_ID_PAUSE 1
973 #define GAME_CTRL_ID_PLAY 2
974 #define GAME_CTRL_ID_UNDO 3
975 #define GAME_CTRL_ID_REDO 4
976 #define GAME_CTRL_ID_SAVE 5
977 #define GAME_CTRL_ID_LOAD 6
978 #define SOUND_CTRL_ID_MUSIC 7
979 #define SOUND_CTRL_ID_LOOPS 8
980 #define SOUND_CTRL_ID_SIMPLE 9
982 #define NUM_GAME_BUTTONS 10
985 /* forward declaration for internal use */
987 static void CreateField(int, int, int);
989 static void ResetGfxAnimation(int, int);
991 static void SetPlayerWaiting(struct PlayerInfo *, boolean);
992 static void AdvanceFrameAndPlayerCounters(int);
994 static boolean MovePlayerOneStep(struct PlayerInfo *, int, int, int, int);
995 static boolean MovePlayer(struct PlayerInfo *, int, int);
996 static void ScrollPlayer(struct PlayerInfo *, int);
997 static void ScrollScreen(struct PlayerInfo *, int);
999 static int DigField(struct PlayerInfo *, int, int, int, int, int, int, int);
1000 static boolean DigFieldByCE(int, int, int);
1001 static boolean SnapField(struct PlayerInfo *, int, int);
1002 static boolean DropElement(struct PlayerInfo *);
1004 static void InitBeltMovement(void);
1005 static void CloseAllOpenTimegates(void);
1006 static void CheckGravityMovement(struct PlayerInfo *);
1007 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *);
1008 static void KillPlayerUnlessEnemyProtected(int, int);
1009 static void KillPlayerUnlessExplosionProtected(int, int);
1011 static void TestIfPlayerTouchesCustomElement(int, int);
1012 static void TestIfElementTouchesCustomElement(int, int);
1013 static void TestIfElementHitsCustomElement(int, int, int);
1015 static void HandleElementChange(int, int, int);
1016 static void ExecuteCustomElementAction(int, int, int, int);
1017 static boolean ChangeElement(int, int, int, int);
1019 static boolean CheckTriggeredElementChangeExt(int, int, int, int, int,int,int);
1020 #define CheckTriggeredElementChange(x, y, e, ev) \
1021 CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY,CH_SIDE_ANY, -1)
1022 #define CheckTriggeredElementChangeByPlayer(x, y, e, ev, p, s) \
1023 CheckTriggeredElementChangeExt(x, y, e, ev, p, s, -1)
1024 #define CheckTriggeredElementChangeBySide(x, y, e, ev, s) \
1025 CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, s, -1)
1026 #define CheckTriggeredElementChangeByPage(x, y, e, ev, p) \
1027 CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY, CH_SIDE_ANY, p)
1029 static boolean CheckElementChangeExt(int, int, int, int, int, int, int);
1030 #define CheckElementChange(x, y, e, te, ev) \
1031 CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, CH_SIDE_ANY)
1032 #define CheckElementChangeByPlayer(x, y, e, ev, p, s) \
1033 CheckElementChangeExt(x, y, e, EL_EMPTY, ev, p, s)
1034 #define CheckElementChangeBySide(x, y, e, te, ev, s) \
1035 CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, s)
1037 static void PlayLevelSound(int, int, int);
1038 static void PlayLevelSoundNearest(int, int, int);
1039 static void PlayLevelSoundAction(int, int, int);
1040 static void PlayLevelSoundElementAction(int, int, int, int);
1041 static void PlayLevelSoundElementActionIfLoop(int, int, int, int);
1042 static void PlayLevelSoundActionIfLoop(int, int, int);
1043 static void StopLevelSoundActionIfLoop(int, int, int);
1044 static void PlayLevelMusic();
1046 static void HandleGameButtons(struct GadgetInfo *);
1048 int AmoebeNachbarNr(int, int);
1049 void AmoebeUmwandeln(int, int);
1050 void ContinueMoving(int, int);
1051 void Bang(int, int);
1052 void InitMovDir(int, int);
1053 void InitAmoebaNr(int, int);
1054 int NewHiScore(void);
1056 void TestIfGoodThingHitsBadThing(int, int, int);
1057 void TestIfBadThingHitsGoodThing(int, int, int);
1058 void TestIfPlayerTouchesBadThing(int, int);
1059 void TestIfPlayerRunsIntoBadThing(int, int, int);
1060 void TestIfBadThingTouchesPlayer(int, int);
1061 void TestIfBadThingRunsIntoPlayer(int, int, int);
1062 void TestIfFriendTouchesBadThing(int, int);
1063 void TestIfBadThingTouchesFriend(int, int);
1064 void TestIfBadThingTouchesOtherBadThing(int, int);
1065 void TestIfGoodThingGetsHitByBadThing(int, int, int);
1067 void KillPlayer(struct PlayerInfo *);
1068 void BuryPlayer(struct PlayerInfo *);
1069 void RemovePlayer(struct PlayerInfo *);
1071 static int getInvisibleActiveFromInvisibleElement(int);
1072 static int getInvisibleFromInvisibleActiveElement(int);
1074 static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
1076 /* for detection of endless loops, caused by custom element programming */
1077 /* (using maximal playfield width x 10 is just a rough approximation) */
1078 #define MAX_ELEMENT_CHANGE_RECURSION_DEPTH (MAX_PLAYFIELD_WIDTH * 10)
1080 #define RECURSION_LOOP_DETECTION_START(e, rc) \
1082 if (recursion_loop_detected) \
1085 if (recursion_loop_depth > MAX_ELEMENT_CHANGE_RECURSION_DEPTH) \
1087 recursion_loop_detected = TRUE; \
1088 recursion_loop_element = (e); \
1091 recursion_loop_depth++; \
1094 #define RECURSION_LOOP_DETECTION_END() \
1096 recursion_loop_depth--; \
1099 static int recursion_loop_depth;
1100 static boolean recursion_loop_detected;
1101 static boolean recursion_loop_element;
1103 static int map_player_action[MAX_PLAYERS];
1106 /* ------------------------------------------------------------------------- */
1107 /* definition of elements that automatically change to other elements after */
1108 /* a specified time, eventually calling a function when changing */
1109 /* ------------------------------------------------------------------------- */
1111 /* forward declaration for changer functions */
1112 static void InitBuggyBase(int, int);
1113 static void WarnBuggyBase(int, int);
1115 static void InitTrap(int, int);
1116 static void ActivateTrap(int, int);
1117 static void ChangeActiveTrap(int, int);
1119 static void InitRobotWheel(int, int);
1120 static void RunRobotWheel(int, int);
1121 static void StopRobotWheel(int, int);
1123 static void InitTimegateWheel(int, int);
1124 static void RunTimegateWheel(int, int);
1126 static void InitMagicBallDelay(int, int);
1127 static void ActivateMagicBall(int, int);
1129 struct ChangingElementInfo
1134 void (*pre_change_function)(int x, int y);
1135 void (*change_function)(int x, int y);
1136 void (*post_change_function)(int x, int y);
1139 static struct ChangingElementInfo change_delay_list[] =
1174 EL_STEEL_EXIT_OPENING,
1182 EL_STEEL_EXIT_CLOSING,
1183 EL_STEEL_EXIT_CLOSED,
1206 EL_EM_STEEL_EXIT_OPENING,
1207 EL_EM_STEEL_EXIT_OPEN,
1214 EL_EM_STEEL_EXIT_CLOSING,
1238 EL_SWITCHGATE_OPENING,
1246 EL_SWITCHGATE_CLOSING,
1247 EL_SWITCHGATE_CLOSED,
1254 EL_TIMEGATE_OPENING,
1262 EL_TIMEGATE_CLOSING,
1271 EL_ACID_SPLASH_LEFT,
1279 EL_ACID_SPLASH_RIGHT,
1288 EL_SP_BUGGY_BASE_ACTIVATING,
1295 EL_SP_BUGGY_BASE_ACTIVATING,
1296 EL_SP_BUGGY_BASE_ACTIVE,
1303 EL_SP_BUGGY_BASE_ACTIVE,
1327 EL_ROBOT_WHEEL_ACTIVE,
1335 EL_TIMEGATE_SWITCH_ACTIVE,
1343 EL_DC_TIMEGATE_SWITCH_ACTIVE,
1344 EL_DC_TIMEGATE_SWITCH,
1351 EL_EMC_MAGIC_BALL_ACTIVE,
1352 EL_EMC_MAGIC_BALL_ACTIVE,
1359 EL_EMC_SPRING_BUMPER_ACTIVE,
1360 EL_EMC_SPRING_BUMPER,
1367 EL_DIAGONAL_SHRINKING,
1375 EL_DIAGONAL_GROWING,
1396 int push_delay_fixed, push_delay_random;
1400 { EL_SPRING, 0, 0 },
1401 { EL_BALLOON, 0, 0 },
1403 { EL_SOKOBAN_OBJECT, 2, 0 },
1404 { EL_SOKOBAN_FIELD_FULL, 2, 0 },
1405 { EL_SATELLITE, 2, 0 },
1406 { EL_SP_DISK_YELLOW, 2, 0 },
1408 { EL_UNDEFINED, 0, 0 },
1416 move_stepsize_list[] =
1418 { EL_AMOEBA_DROP, 2 },
1419 { EL_AMOEBA_DROPPING, 2 },
1420 { EL_QUICKSAND_FILLING, 1 },
1421 { EL_QUICKSAND_EMPTYING, 1 },
1422 { EL_QUICKSAND_FAST_FILLING, 2 },
1423 { EL_QUICKSAND_FAST_EMPTYING, 2 },
1424 { EL_MAGIC_WALL_FILLING, 2 },
1425 { EL_MAGIC_WALL_EMPTYING, 2 },
1426 { EL_BD_MAGIC_WALL_FILLING, 2 },
1427 { EL_BD_MAGIC_WALL_EMPTYING, 2 },
1428 { EL_DC_MAGIC_WALL_FILLING, 2 },
1429 { EL_DC_MAGIC_WALL_EMPTYING, 2 },
1431 { EL_UNDEFINED, 0 },
1439 collect_count_list[] =
1442 { EL_BD_DIAMOND, 1 },
1443 { EL_EMERALD_YELLOW, 1 },
1444 { EL_EMERALD_RED, 1 },
1445 { EL_EMERALD_PURPLE, 1 },
1447 { EL_SP_INFOTRON, 1 },
1451 { EL_UNDEFINED, 0 },
1459 access_direction_list[] =
1461 { EL_TUBE_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1462 { EL_TUBE_VERTICAL, MV_UP | MV_DOWN },
1463 { EL_TUBE_HORIZONTAL, MV_LEFT | MV_RIGHT },
1464 { EL_TUBE_VERTICAL_LEFT, MV_LEFT | MV_UP | MV_DOWN },
1465 { EL_TUBE_VERTICAL_RIGHT, MV_RIGHT | MV_UP | MV_DOWN },
1466 { EL_TUBE_HORIZONTAL_UP, MV_LEFT | MV_RIGHT | MV_UP },
1467 { EL_TUBE_HORIZONTAL_DOWN, MV_LEFT | MV_RIGHT | MV_DOWN },
1468 { EL_TUBE_LEFT_UP, MV_LEFT | MV_UP },
1469 { EL_TUBE_LEFT_DOWN, MV_LEFT | MV_DOWN },
1470 { EL_TUBE_RIGHT_UP, MV_RIGHT | MV_UP },
1471 { EL_TUBE_RIGHT_DOWN, MV_RIGHT | MV_DOWN },
1473 { EL_SP_PORT_LEFT, MV_RIGHT },
1474 { EL_SP_PORT_RIGHT, MV_LEFT },
1475 { EL_SP_PORT_UP, MV_DOWN },
1476 { EL_SP_PORT_DOWN, MV_UP },
1477 { EL_SP_PORT_HORIZONTAL, MV_LEFT | MV_RIGHT },
1478 { EL_SP_PORT_VERTICAL, MV_UP | MV_DOWN },
1479 { EL_SP_PORT_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1480 { EL_SP_GRAVITY_PORT_LEFT, MV_RIGHT },
1481 { EL_SP_GRAVITY_PORT_RIGHT, MV_LEFT },
1482 { EL_SP_GRAVITY_PORT_UP, MV_DOWN },
1483 { EL_SP_GRAVITY_PORT_DOWN, MV_UP },
1484 { EL_SP_GRAVITY_ON_PORT_LEFT, MV_RIGHT },
1485 { EL_SP_GRAVITY_ON_PORT_RIGHT, MV_LEFT },
1486 { EL_SP_GRAVITY_ON_PORT_UP, MV_DOWN },
1487 { EL_SP_GRAVITY_ON_PORT_DOWN, MV_UP },
1488 { EL_SP_GRAVITY_OFF_PORT_LEFT, MV_RIGHT },
1489 { EL_SP_GRAVITY_OFF_PORT_RIGHT, MV_LEFT },
1490 { EL_SP_GRAVITY_OFF_PORT_UP, MV_DOWN },
1491 { EL_SP_GRAVITY_OFF_PORT_DOWN, MV_UP },
1493 { EL_UNDEFINED, MV_NONE }
1496 static boolean trigger_events[MAX_NUM_ELEMENTS][NUM_CHANGE_EVENTS];
1498 #define IS_AUTO_CHANGING(e) (element_info[e].has_change_event[CE_DELAY])
1499 #define IS_JUST_CHANGING(x, y) (ChangeDelay[x][y] != 0)
1500 #define IS_CHANGING(x, y) (IS_AUTO_CHANGING(Feld[x][y]) || \
1501 IS_JUST_CHANGING(x, y))
1503 #define CE_PAGE(e, ce) (element_info[e].event_page[ce])
1505 /* static variables for playfield scan mode (scanning forward or backward) */
1506 static int playfield_scan_start_x = 0;
1507 static int playfield_scan_start_y = 0;
1508 static int playfield_scan_delta_x = 1;
1509 static int playfield_scan_delta_y = 1;
1511 #define SCAN_PLAYFIELD(x, y) for ((y) = playfield_scan_start_y; \
1512 (y) >= 0 && (y) <= lev_fieldy - 1; \
1513 (y) += playfield_scan_delta_y) \
1514 for ((x) = playfield_scan_start_x; \
1515 (x) >= 0 && (x) <= lev_fieldx - 1; \
1516 (x) += playfield_scan_delta_x)
1519 void DEBUG_SetMaximumDynamite()
1523 for (i = 0; i < MAX_INVENTORY_SIZE; i++)
1524 if (local_player->inventory_size < MAX_INVENTORY_SIZE)
1525 local_player->inventory_element[local_player->inventory_size++] =
1530 static void InitPlayfieldScanModeVars()
1532 if (game.use_reverse_scan_direction)
1534 playfield_scan_start_x = lev_fieldx - 1;
1535 playfield_scan_start_y = lev_fieldy - 1;
1537 playfield_scan_delta_x = -1;
1538 playfield_scan_delta_y = -1;
1542 playfield_scan_start_x = 0;
1543 playfield_scan_start_y = 0;
1545 playfield_scan_delta_x = 1;
1546 playfield_scan_delta_y = 1;
1550 static void InitPlayfieldScanMode(int mode)
1552 game.use_reverse_scan_direction =
1553 (mode == CA_ARG_SCAN_MODE_REVERSE ? TRUE : FALSE);
1555 InitPlayfieldScanModeVars();
1558 static int get_move_delay_from_stepsize(int move_stepsize)
1561 MIN(MAX(MOVE_STEPSIZE_MIN, move_stepsize), MOVE_STEPSIZE_MAX);
1563 /* make sure that stepsize value is always a power of 2 */
1564 move_stepsize = (1 << log_2(move_stepsize));
1566 return TILEX / move_stepsize;
1569 static void SetPlayerMoveSpeed(struct PlayerInfo *player, int move_stepsize,
1572 int player_nr = player->index_nr;
1573 int move_delay = get_move_delay_from_stepsize(move_stepsize);
1574 boolean cannot_move = (move_stepsize == STEPSIZE_NOT_MOVING ? TRUE : FALSE);
1576 /* do no immediately change move delay -- the player might just be moving */
1577 player->move_delay_value_next = move_delay;
1579 /* information if player can move must be set separately */
1580 player->cannot_move = cannot_move;
1584 player->move_delay = game.initial_move_delay[player_nr];
1585 player->move_delay_value = game.initial_move_delay_value[player_nr];
1587 player->move_delay_value_next = -1;
1589 player->move_delay_reset_counter = 0;
1593 void GetPlayerConfig()
1595 GameFrameDelay = setup.game_frame_delay;
1597 if (!audio.sound_available)
1598 setup.sound_simple = FALSE;
1600 if (!audio.loops_available)
1601 setup.sound_loops = FALSE;
1603 if (!audio.music_available)
1604 setup.sound_music = FALSE;
1606 if (!video.fullscreen_available)
1607 setup.fullscreen = FALSE;
1609 setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
1611 SetAudioMode(setup.sound);
1615 int GetElementFromGroupElement(int element)
1617 if (IS_GROUP_ELEMENT(element))
1619 struct ElementGroupInfo *group = element_info[element].group;
1620 int last_anim_random_frame = gfx.anim_random_frame;
1623 if (group->choice_mode == ANIM_RANDOM)
1624 gfx.anim_random_frame = RND(group->num_elements_resolved);
1626 element_pos = getAnimationFrame(group->num_elements_resolved, 1,
1627 group->choice_mode, 0,
1630 if (group->choice_mode == ANIM_RANDOM)
1631 gfx.anim_random_frame = last_anim_random_frame;
1633 group->choice_pos++;
1635 element = group->element_resolved[element_pos];
1641 static void InitPlayerField(int x, int y, int element, boolean init_game)
1643 if (element == EL_SP_MURPHY)
1647 if (stored_player[0].present)
1649 Feld[x][y] = EL_SP_MURPHY_CLONE;
1655 stored_player[0].initial_element = element;
1656 stored_player[0].use_murphy = TRUE;
1658 if (!level.use_artwork_element[0])
1659 stored_player[0].artwork_element = EL_SP_MURPHY;
1662 Feld[x][y] = EL_PLAYER_1;
1668 struct PlayerInfo *player = &stored_player[Feld[x][y] - EL_PLAYER_1];
1669 int jx = player->jx, jy = player->jy;
1671 player->present = TRUE;
1673 player->block_last_field = (element == EL_SP_MURPHY ?
1674 level.sp_block_last_field :
1675 level.block_last_field);
1677 /* ---------- initialize player's last field block delay --------------- */
1679 /* always start with reliable default value (no adjustment needed) */
1680 player->block_delay_adjustment = 0;
1682 /* special case 1: in Supaplex, Murphy blocks last field one more frame */
1683 if (player->block_last_field && element == EL_SP_MURPHY)
1684 player->block_delay_adjustment = 1;
1686 /* special case 2: in game engines before 3.1.1, blocking was different */
1687 if (game.use_block_last_field_bug)
1688 player->block_delay_adjustment = (player->block_last_field ? -1 : 1);
1690 if (!options.network || player->connected)
1692 player->active = TRUE;
1694 /* remove potentially duplicate players */
1695 if (StorePlayer[jx][jy] == Feld[x][y])
1696 StorePlayer[jx][jy] = 0;
1698 StorePlayer[x][y] = Feld[x][y];
1700 #if DEBUG_INIT_PLAYER
1703 printf("- player element %d activated", player->element_nr);
1704 printf(" (local player is %d and currently %s)\n",
1705 local_player->element_nr,
1706 local_player->active ? "active" : "not active");
1711 Feld[x][y] = EL_EMPTY;
1713 player->jx = player->last_jx = x;
1714 player->jy = player->last_jy = y;
1719 int player_nr = GET_PLAYER_NR(element);
1720 struct PlayerInfo *player = &stored_player[player_nr];
1722 if (player->active && player->killed)
1723 player->reanimated = TRUE; /* if player was just killed, reanimate him */
1727 static void InitField(int x, int y, boolean init_game)
1729 int element = Feld[x][y];
1738 InitPlayerField(x, y, element, init_game);
1741 case EL_SOKOBAN_FIELD_PLAYER:
1742 element = Feld[x][y] = EL_PLAYER_1;
1743 InitField(x, y, init_game);
1745 element = Feld[x][y] = EL_SOKOBAN_FIELD_EMPTY;
1746 InitField(x, y, init_game);
1749 case EL_SOKOBAN_FIELD_EMPTY:
1750 local_player->sokobanfields_still_needed++;
1754 if (x < lev_fieldx-1 && Feld[x+1][y] == EL_ACID)
1755 Feld[x][y] = EL_ACID_POOL_TOPLEFT;
1756 else if (x > 0 && Feld[x-1][y] == EL_ACID)
1757 Feld[x][y] = EL_ACID_POOL_TOPRIGHT;
1758 else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPLEFT)
1759 Feld[x][y] = EL_ACID_POOL_BOTTOMLEFT;
1760 else if (y > 0 && Feld[x][y-1] == EL_ACID)
1761 Feld[x][y] = EL_ACID_POOL_BOTTOM;
1762 else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPRIGHT)
1763 Feld[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
1772 case EL_SPACESHIP_RIGHT:
1773 case EL_SPACESHIP_UP:
1774 case EL_SPACESHIP_LEFT:
1775 case EL_SPACESHIP_DOWN:
1776 case EL_BD_BUTTERFLY:
1777 case EL_BD_BUTTERFLY_RIGHT:
1778 case EL_BD_BUTTERFLY_UP:
1779 case EL_BD_BUTTERFLY_LEFT:
1780 case EL_BD_BUTTERFLY_DOWN:
1782 case EL_BD_FIREFLY_RIGHT:
1783 case EL_BD_FIREFLY_UP:
1784 case EL_BD_FIREFLY_LEFT:
1785 case EL_BD_FIREFLY_DOWN:
1786 case EL_PACMAN_RIGHT:
1788 case EL_PACMAN_LEFT:
1789 case EL_PACMAN_DOWN:
1791 case EL_YAMYAM_LEFT:
1792 case EL_YAMYAM_RIGHT:
1794 case EL_YAMYAM_DOWN:
1795 case EL_DARK_YAMYAM:
1798 case EL_SP_SNIKSNAK:
1799 case EL_SP_ELECTRON:
1808 case EL_AMOEBA_FULL:
1813 case EL_AMOEBA_DROP:
1814 if (y == lev_fieldy - 1)
1816 Feld[x][y] = EL_AMOEBA_GROWING;
1817 Store[x][y] = EL_AMOEBA_WET;
1821 case EL_DYNAMITE_ACTIVE:
1822 case EL_SP_DISK_RED_ACTIVE:
1823 case EL_DYNABOMB_PLAYER_1_ACTIVE:
1824 case EL_DYNABOMB_PLAYER_2_ACTIVE:
1825 case EL_DYNABOMB_PLAYER_3_ACTIVE:
1826 case EL_DYNABOMB_PLAYER_4_ACTIVE:
1827 MovDelay[x][y] = 96;
1830 case EL_EM_DYNAMITE_ACTIVE:
1831 MovDelay[x][y] = 32;
1835 local_player->lights_still_needed++;
1839 local_player->friends_still_needed++;
1844 GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
1847 case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
1848 case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
1849 case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
1850 case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
1851 case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
1852 case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
1853 case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
1854 case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
1855 case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
1856 case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
1857 case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
1858 case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
1861 int belt_nr = getBeltNrFromBeltSwitchElement(Feld[x][y]);
1862 int belt_dir = getBeltDirFromBeltSwitchElement(Feld[x][y]);
1863 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Feld[x][y]);
1865 if (game.belt_dir_nr[belt_nr] == 3) /* initial value */
1867 game.belt_dir[belt_nr] = belt_dir;
1868 game.belt_dir_nr[belt_nr] = belt_dir_nr;
1870 else /* more than one switch -- set it like the first switch */
1872 Feld[x][y] = Feld[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
1877 case EL_LIGHT_SWITCH_ACTIVE:
1879 game.light_time_left = level.time_light * FRAMES_PER_SECOND;
1882 case EL_INVISIBLE_STEELWALL:
1883 case EL_INVISIBLE_WALL:
1884 case EL_INVISIBLE_SAND:
1885 if (game.light_time_left > 0 ||
1886 game.lenses_time_left > 0)
1887 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
1890 case EL_EMC_MAGIC_BALL:
1891 if (game.ball_state)
1892 Feld[x][y] = EL_EMC_MAGIC_BALL_ACTIVE;
1895 case EL_EMC_MAGIC_BALL_SWITCH:
1896 if (game.ball_state)
1897 Feld[x][y] = EL_EMC_MAGIC_BALL_SWITCH_ACTIVE;
1900 case EL_TRIGGER_PLAYER:
1901 case EL_TRIGGER_ELEMENT:
1902 case EL_TRIGGER_CE_VALUE:
1903 case EL_TRIGGER_CE_SCORE:
1905 case EL_ANY_ELEMENT:
1906 case EL_CURRENT_CE_VALUE:
1907 case EL_CURRENT_CE_SCORE:
1924 /* reference elements should not be used on the playfield */
1925 Feld[x][y] = EL_EMPTY;
1929 if (IS_CUSTOM_ELEMENT(element))
1931 if (CAN_MOVE(element))
1934 if (!element_info[element].use_last_ce_value || init_game)
1935 CustomValue[x][y] = GET_NEW_CE_VALUE(Feld[x][y]);
1937 else if (IS_GROUP_ELEMENT(element))
1939 Feld[x][y] = GetElementFromGroupElement(element);
1941 InitField(x, y, init_game);
1948 CheckTriggeredElementChange(x, y, element, CE_CREATION_OF_X);
1951 static inline void InitField_WithBug1(int x, int y, boolean init_game)
1953 InitField(x, y, init_game);
1955 /* not needed to call InitMovDir() -- already done by InitField()! */
1956 if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1957 CAN_MOVE(Feld[x][y]))
1961 static inline void InitField_WithBug2(int x, int y, boolean init_game)
1963 int old_element = Feld[x][y];
1965 InitField(x, y, init_game);
1967 /* not needed to call InitMovDir() -- already done by InitField()! */
1968 if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1969 CAN_MOVE(old_element) &&
1970 (old_element < EL_MOLE_LEFT || old_element > EL_MOLE_DOWN))
1973 /* this case is in fact a combination of not less than three bugs:
1974 first, it calls InitMovDir() for elements that can move, although this is
1975 already done by InitField(); then, it checks the element that was at this
1976 field _before_ the call to InitField() (which can change it); lastly, it
1977 was not called for "mole with direction" elements, which were treated as
1978 "cannot move" due to (fixed) wrong element initialization in "src/init.c"
1982 static int get_key_element_from_nr(int key_nr)
1984 int key_base_element = (key_nr >= STD_NUM_KEYS ? EL_EMC_KEY_5 - STD_NUM_KEYS :
1985 level.game_engine_type == GAME_ENGINE_TYPE_EM ?
1986 EL_EM_KEY_1 : EL_KEY_1);
1988 return key_base_element + key_nr;
1991 static int get_next_dropped_element(struct PlayerInfo *player)
1993 return (player->inventory_size > 0 ?
1994 player->inventory_element[player->inventory_size - 1] :
1995 player->inventory_infinite_element != EL_UNDEFINED ?
1996 player->inventory_infinite_element :
1997 player->dynabombs_left > 0 ?
1998 EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
2002 static int get_inventory_element_from_pos(struct PlayerInfo *player, int pos)
2004 /* pos >= 0: get element from bottom of the stack;
2005 pos < 0: get element from top of the stack */
2009 int min_inventory_size = -pos;
2010 int inventory_pos = player->inventory_size - min_inventory_size;
2011 int min_dynabombs_left = min_inventory_size - player->inventory_size;
2013 return (player->inventory_size >= min_inventory_size ?
2014 player->inventory_element[inventory_pos] :
2015 player->inventory_infinite_element != EL_UNDEFINED ?
2016 player->inventory_infinite_element :
2017 player->dynabombs_left >= min_dynabombs_left ?
2018 EL_DYNABOMB_PLAYER_1 + player->index_nr :
2023 int min_dynabombs_left = pos + 1;
2024 int min_inventory_size = pos + 1 - player->dynabombs_left;
2025 int inventory_pos = pos - player->dynabombs_left;
2027 return (player->inventory_infinite_element != EL_UNDEFINED ?
2028 player->inventory_infinite_element :
2029 player->dynabombs_left >= min_dynabombs_left ?
2030 EL_DYNABOMB_PLAYER_1 + player->index_nr :
2031 player->inventory_size >= min_inventory_size ?
2032 player->inventory_element[inventory_pos] :
2037 static int compareGamePanelOrderInfo(const void *object1, const void *object2)
2039 const struct GamePanelOrderInfo *gpo1 = (struct GamePanelOrderInfo *)object1;
2040 const struct GamePanelOrderInfo *gpo2 = (struct GamePanelOrderInfo *)object2;
2043 if (gpo1->sort_priority != gpo2->sort_priority)
2044 compare_result = gpo1->sort_priority - gpo2->sort_priority;
2046 compare_result = gpo1->nr - gpo2->nr;
2048 return compare_result;
2051 void InitGameControlValues()
2055 for (i = 0; game_panel_controls[i].nr != -1; i++)
2057 struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2058 struct GamePanelOrderInfo *gpo = &game_panel_order[i];
2059 struct TextPosInfo *pos = gpc->pos;
2061 int type = gpc->type;
2065 Error(ERR_INFO, "'game_panel_controls' structure corrupted at %d", i);
2066 Error(ERR_EXIT, "this should not happen -- please debug");
2069 /* force update of game controls after initialization */
2070 gpc->value = gpc->last_value = -1;
2071 gpc->frame = gpc->last_frame = -1;
2072 gpc->gfx_frame = -1;
2074 /* determine panel value width for later calculation of alignment */
2075 if (type == TYPE_INTEGER || type == TYPE_STRING)
2077 pos->width = pos->size * getFontWidth(pos->font);
2078 pos->height = getFontHeight(pos->font);
2080 else if (type == TYPE_ELEMENT)
2082 pos->width = pos->size;
2083 pos->height = pos->size;
2086 /* fill structure for game panel draw order */
2088 gpo->sort_priority = pos->sort_priority;
2091 /* sort game panel controls according to sort_priority and control number */
2092 qsort(game_panel_order, NUM_GAME_PANEL_CONTROLS,
2093 sizeof(struct GamePanelOrderInfo), compareGamePanelOrderInfo);
2096 void UpdatePlayfieldElementCount()
2098 boolean use_element_count = FALSE;
2101 /* first check if it is needed at all to calculate playfield element count */
2102 for (i = GAME_PANEL_ELEMENT_COUNT_1; i <= GAME_PANEL_ELEMENT_COUNT_8; i++)
2103 if (!PANEL_DEACTIVATED(game_panel_controls[i].pos))
2104 use_element_count = TRUE;
2106 if (!use_element_count)
2109 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2110 element_info[i].element_count = 0;
2112 SCAN_PLAYFIELD(x, y)
2114 element_info[Feld[x][y]].element_count++;
2117 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
2118 for (j = 0; j < MAX_NUM_ELEMENTS; j++)
2119 if (IS_IN_GROUP(j, i))
2120 element_info[EL_GROUP_START + i].element_count +=
2121 element_info[j].element_count;
2124 void UpdateGameControlValues()
2127 int time = (local_player->LevelSolved ?
2128 local_player->LevelSolved_CountingTime :
2129 level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2130 level.native_em_level->lev->time :
2131 level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2132 level.native_sp_level->game_sp->time_played :
2133 game.no_time_limit ? TimePlayed : TimeLeft);
2134 int score = (local_player->LevelSolved ?
2135 local_player->LevelSolved_CountingScore :
2136 level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2137 level.native_em_level->lev->score :
2138 level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2139 level.native_sp_level->game_sp->score :
2140 local_player->score);
2141 int gems = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2142 level.native_em_level->lev->required :
2143 level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2144 level.native_sp_level->game_sp->infotrons_still_needed :
2145 local_player->gems_still_needed);
2146 int exit_closed = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2147 level.native_em_level->lev->required > 0 :
2148 level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2149 level.native_sp_level->game_sp->infotrons_still_needed > 0 :
2150 local_player->gems_still_needed > 0 ||
2151 local_player->sokobanfields_still_needed > 0 ||
2152 local_player->lights_still_needed > 0);
2154 UpdatePlayfieldElementCount();
2156 /* update game panel control values */
2158 game_panel_controls[GAME_PANEL_LEVEL_NUMBER].value = level_nr;
2159 game_panel_controls[GAME_PANEL_GEMS].value = gems;
2161 game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value = 0;
2162 for (i = 0; i < MAX_NUM_KEYS; i++)
2163 game_panel_controls[GAME_PANEL_KEY_1 + i].value = EL_EMPTY;
2164 game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_EMPTY;
2165 game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value = 0;
2167 if (game.centered_player_nr == -1)
2169 for (i = 0; i < MAX_PLAYERS; i++)
2171 /* only one player in Supaplex game engine */
2172 if (level.game_engine_type == GAME_ENGINE_TYPE_SP && i > 0)
2175 for (k = 0; k < MAX_NUM_KEYS; k++)
2177 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2179 if (level.native_em_level->ply[i]->keys & (1 << k))
2180 game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2181 get_key_element_from_nr(k);
2183 else if (stored_player[i].key[k])
2184 game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2185 get_key_element_from_nr(k);
2188 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2189 game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2190 level.native_em_level->ply[i]->dynamite;
2191 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
2192 game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2193 level.native_sp_level->game_sp->red_disk_count;
2195 game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2196 stored_player[i].inventory_size;
2198 if (stored_player[i].num_white_keys > 0)
2199 game_panel_controls[GAME_PANEL_KEY_WHITE].value =
2202 game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2203 stored_player[i].num_white_keys;
2208 int player_nr = game.centered_player_nr;
2210 for (k = 0; k < MAX_NUM_KEYS; k++)
2212 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2214 if (level.native_em_level->ply[player_nr]->keys & (1 << k))
2215 game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2216 get_key_element_from_nr(k);
2218 else if (stored_player[player_nr].key[k])
2219 game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2220 get_key_element_from_nr(k);
2223 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2224 game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2225 level.native_em_level->ply[player_nr]->dynamite;
2226 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
2227 game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2228 level.native_sp_level->game_sp->red_disk_count;
2230 game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2231 stored_player[player_nr].inventory_size;
2233 if (stored_player[player_nr].num_white_keys > 0)
2234 game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_DC_KEY_WHITE;
2236 game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2237 stored_player[player_nr].num_white_keys;
2240 for (i = 0; i < NUM_PANEL_INVENTORY; i++)
2242 game_panel_controls[GAME_PANEL_INVENTORY_FIRST_1 + i].value =
2243 get_inventory_element_from_pos(local_player, i);
2244 game_panel_controls[GAME_PANEL_INVENTORY_LAST_1 + i].value =
2245 get_inventory_element_from_pos(local_player, -i - 1);
2248 game_panel_controls[GAME_PANEL_SCORE].value = score;
2249 game_panel_controls[GAME_PANEL_HIGHSCORE].value = highscore[0].Score;
2251 game_panel_controls[GAME_PANEL_TIME].value = time;
2253 game_panel_controls[GAME_PANEL_TIME_HH].value = time / 3600;
2254 game_panel_controls[GAME_PANEL_TIME_MM].value = (time / 60) % 60;
2255 game_panel_controls[GAME_PANEL_TIME_SS].value = time % 60;
2257 game_panel_controls[GAME_PANEL_FRAME].value = FrameCounter;
2259 game_panel_controls[GAME_PANEL_SHIELD_NORMAL].value =
2260 (local_player->shield_normal_time_left > 0 ? EL_SHIELD_NORMAL_ACTIVE :
2262 game_panel_controls[GAME_PANEL_SHIELD_NORMAL_TIME].value =
2263 local_player->shield_normal_time_left;
2264 game_panel_controls[GAME_PANEL_SHIELD_DEADLY].value =
2265 (local_player->shield_deadly_time_left > 0 ? EL_SHIELD_DEADLY_ACTIVE :
2267 game_panel_controls[GAME_PANEL_SHIELD_DEADLY_TIME].value =
2268 local_player->shield_deadly_time_left;
2270 game_panel_controls[GAME_PANEL_EXIT].value =
2271 (exit_closed ? EL_EXIT_CLOSED : EL_EXIT_OPEN);
2273 game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL].value =
2274 (game.ball_state ? EL_EMC_MAGIC_BALL_ACTIVE : EL_EMC_MAGIC_BALL);
2275 game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL_SWITCH].value =
2276 (game.ball_state ? EL_EMC_MAGIC_BALL_SWITCH_ACTIVE :
2277 EL_EMC_MAGIC_BALL_SWITCH);
2279 game_panel_controls[GAME_PANEL_LIGHT_SWITCH].value =
2280 (game.light_time_left > 0 ? EL_LIGHT_SWITCH_ACTIVE : EL_LIGHT_SWITCH);
2281 game_panel_controls[GAME_PANEL_LIGHT_SWITCH_TIME].value =
2282 game.light_time_left;
2284 game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH].value =
2285 (game.timegate_time_left > 0 ? EL_TIMEGATE_OPEN : EL_TIMEGATE_CLOSED);
2286 game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH_TIME].value =
2287 game.timegate_time_left;
2289 game_panel_controls[GAME_PANEL_SWITCHGATE_SWITCH].value =
2290 EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
2292 game_panel_controls[GAME_PANEL_EMC_LENSES].value =
2293 (game.lenses_time_left > 0 ? EL_EMC_LENSES : EL_EMPTY);
2294 game_panel_controls[GAME_PANEL_EMC_LENSES_TIME].value =
2295 game.lenses_time_left;
2297 game_panel_controls[GAME_PANEL_EMC_MAGNIFIER].value =
2298 (game.magnify_time_left > 0 ? EL_EMC_MAGNIFIER : EL_EMPTY);
2299 game_panel_controls[GAME_PANEL_EMC_MAGNIFIER_TIME].value =
2300 game.magnify_time_left;
2302 game_panel_controls[GAME_PANEL_BALLOON_SWITCH].value =
2303 (game.wind_direction == MV_LEFT ? EL_BALLOON_SWITCH_LEFT :
2304 game.wind_direction == MV_RIGHT ? EL_BALLOON_SWITCH_RIGHT :
2305 game.wind_direction == MV_UP ? EL_BALLOON_SWITCH_UP :
2306 game.wind_direction == MV_DOWN ? EL_BALLOON_SWITCH_DOWN :
2307 EL_BALLOON_SWITCH_NONE);
2309 game_panel_controls[GAME_PANEL_DYNABOMB_NUMBER].value =
2310 local_player->dynabomb_count;
2311 game_panel_controls[GAME_PANEL_DYNABOMB_SIZE].value =
2312 local_player->dynabomb_size;
2313 game_panel_controls[GAME_PANEL_DYNABOMB_POWER].value =
2314 (local_player->dynabomb_xl ? EL_DYNABOMB_INCREASE_POWER : EL_EMPTY);
2316 game_panel_controls[GAME_PANEL_PENGUINS].value =
2317 local_player->friends_still_needed;
2319 game_panel_controls[GAME_PANEL_SOKOBAN_OBJECTS].value =
2320 local_player->sokobanfields_still_needed;
2321 game_panel_controls[GAME_PANEL_SOKOBAN_FIELDS].value =
2322 local_player->sokobanfields_still_needed;
2324 game_panel_controls[GAME_PANEL_ROBOT_WHEEL].value =
2325 (game.robot_wheel_active ? EL_ROBOT_WHEEL_ACTIVE : EL_ROBOT_WHEEL);
2327 for (i = 0; i < NUM_BELTS; i++)
2329 game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1 + i].value =
2330 (game.belt_dir[i] != MV_NONE ? EL_CONVEYOR_BELT_1_MIDDLE_ACTIVE :
2331 EL_CONVEYOR_BELT_1_MIDDLE) + i;
2332 game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1_SWITCH + i].value =
2333 getBeltSwitchElementFromBeltNrAndBeltDir(i, game.belt_dir[i]);
2336 game_panel_controls[GAME_PANEL_MAGIC_WALL].value =
2337 (game.magic_wall_active ? EL_MAGIC_WALL_ACTIVE : EL_MAGIC_WALL);
2338 game_panel_controls[GAME_PANEL_MAGIC_WALL_TIME].value =
2339 game.magic_wall_time_left;
2341 game_panel_controls[GAME_PANEL_GRAVITY_STATE].value =
2342 local_player->gravity;
2344 for (i = 0; i < NUM_PANEL_GRAPHICS; i++)
2345 game_panel_controls[GAME_PANEL_GRAPHIC_1 + i].value = EL_GRAPHIC_1 + i;
2347 for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2348 game_panel_controls[GAME_PANEL_ELEMENT_1 + i].value =
2349 (IS_DRAWABLE_ELEMENT(game.panel.element[i].id) ?
2350 game.panel.element[i].id : EL_UNDEFINED);
2352 for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2353 game_panel_controls[GAME_PANEL_ELEMENT_COUNT_1 + i].value =
2354 (IS_VALID_ELEMENT(game.panel.element_count[i].id) ?
2355 element_info[game.panel.element_count[i].id].element_count : 0);
2357 for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2358 game_panel_controls[GAME_PANEL_CE_SCORE_1 + i].value =
2359 (IS_CUSTOM_ELEMENT(game.panel.ce_score[i].id) ?
2360 element_info[game.panel.ce_score[i].id].collect_score : 0);
2362 for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2363 game_panel_controls[GAME_PANEL_CE_SCORE_1_ELEMENT + i].value =
2364 (IS_CUSTOM_ELEMENT(game.panel.ce_score_element[i].id) ?
2365 element_info[game.panel.ce_score_element[i].id].collect_score :
2368 game_panel_controls[GAME_PANEL_PLAYER_NAME].value = 0;
2369 game_panel_controls[GAME_PANEL_LEVEL_NAME].value = 0;
2370 game_panel_controls[GAME_PANEL_LEVEL_AUTHOR].value = 0;
2372 /* update game panel control frames */
2374 for (i = 0; game_panel_controls[i].nr != -1; i++)
2376 struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2378 if (gpc->type == TYPE_ELEMENT)
2380 if (gpc->value != EL_UNDEFINED && gpc->value != EL_EMPTY)
2382 int last_anim_random_frame = gfx.anim_random_frame;
2383 int element = gpc->value;
2384 int graphic = el2panelimg(element);
2386 if (gpc->value != gpc->last_value)
2389 gpc->gfx_random = INIT_GFX_RANDOM();
2395 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2396 IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2397 gpc->gfx_random = INIT_GFX_RANDOM();
2400 if (ANIM_MODE(graphic) == ANIM_RANDOM)
2401 gfx.anim_random_frame = gpc->gfx_random;
2403 if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
2404 gpc->gfx_frame = element_info[element].collect_score;
2406 gpc->frame = getGraphicAnimationFrame(el2panelimg(gpc->value),
2409 if (ANIM_MODE(graphic) == ANIM_RANDOM)
2410 gfx.anim_random_frame = last_anim_random_frame;
2416 void DisplayGameControlValues()
2418 boolean redraw_panel = FALSE;
2421 for (i = 0; game_panel_controls[i].nr != -1; i++)
2423 struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2425 if (PANEL_DEACTIVATED(gpc->pos))
2428 if (gpc->value == gpc->last_value &&
2429 gpc->frame == gpc->last_frame)
2432 redraw_panel = TRUE;
2438 /* copy default game door content to main double buffer */
2440 /* !!! CHECK AGAIN !!! */
2441 SetPanelBackground();
2442 // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
2443 DrawBackground(DX, DY, DXSIZE, DYSIZE);
2445 /* redraw game control buttons */
2446 RedrawGameButtons();
2448 game_status = GAME_MODE_PSEUDO_PANEL;
2450 for (i = 0; i < NUM_GAME_PANEL_CONTROLS; i++)
2452 int nr = game_panel_order[i].nr;
2453 struct GamePanelControlInfo *gpc = &game_panel_controls[nr];
2454 struct TextPosInfo *pos = gpc->pos;
2455 int type = gpc->type;
2456 int value = gpc->value;
2457 int frame = gpc->frame;
2458 int size = pos->size;
2459 int font = pos->font;
2460 boolean draw_masked = pos->draw_masked;
2461 int mask_mode = (draw_masked ? BLIT_MASKED : BLIT_OPAQUE);
2463 if (PANEL_DEACTIVATED(pos))
2466 gpc->last_value = value;
2467 gpc->last_frame = frame;
2469 if (type == TYPE_INTEGER)
2471 if (nr == GAME_PANEL_LEVEL_NUMBER ||
2472 nr == GAME_PANEL_TIME)
2474 boolean use_dynamic_size = (size == -1 ? TRUE : FALSE);
2476 if (use_dynamic_size) /* use dynamic number of digits */
2478 int value_change = (nr == GAME_PANEL_LEVEL_NUMBER ? 100 : 1000);
2479 int size1 = (nr == GAME_PANEL_LEVEL_NUMBER ? 2 : 3);
2480 int size2 = size1 + 1;
2481 int font1 = pos->font;
2482 int font2 = pos->font_alt;
2484 size = (value < value_change ? size1 : size2);
2485 font = (value < value_change ? font1 : font2);
2489 /* correct text size if "digits" is zero or less */
2491 size = strlen(int2str(value, size));
2493 /* dynamically correct text alignment */
2494 pos->width = size * getFontWidth(font);
2496 DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2497 int2str(value, size), font, mask_mode);
2499 else if (type == TYPE_ELEMENT)
2501 int element, graphic;
2505 int dst_x = PANEL_XPOS(pos);
2506 int dst_y = PANEL_YPOS(pos);
2508 if (value != EL_UNDEFINED && value != EL_EMPTY)
2511 graphic = el2panelimg(value);
2513 // printf("::: %d, '%s' [%d]\n", element, EL_NAME(element), size);
2515 if (element >= EL_GRAPHIC_1 && element <= EL_GRAPHIC_8 && size == 0)
2518 getSizedGraphicSource(graphic, frame, size, &src_bitmap,
2521 width = graphic_info[graphic].width * size / TILESIZE;
2522 height = graphic_info[graphic].height * size / TILESIZE;
2525 BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2528 BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2532 else if (type == TYPE_STRING)
2534 boolean active = (value != 0);
2535 char *state_normal = "off";
2536 char *state_active = "on";
2537 char *state = (active ? state_active : state_normal);
2538 char *s = (nr == GAME_PANEL_GRAVITY_STATE ? state :
2539 nr == GAME_PANEL_PLAYER_NAME ? setup.player_name :
2540 nr == GAME_PANEL_LEVEL_NAME ? level.name :
2541 nr == GAME_PANEL_LEVEL_AUTHOR ? level.author : NULL);
2543 if (nr == GAME_PANEL_GRAVITY_STATE)
2545 int font1 = pos->font; /* (used for normal state) */
2546 int font2 = pos->font_alt; /* (used for active state) */
2548 font = (active ? font2 : font1);
2557 /* don't truncate output if "chars" is zero or less */
2560 /* dynamically correct text alignment */
2561 pos->width = size * getFontWidth(font);
2564 s_cut = getStringCopyN(s, size);
2566 DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2567 s_cut, font, mask_mode);
2573 redraw_mask |= REDRAW_DOOR_1;
2576 game_status = GAME_MODE_PLAYING;
2579 void UpdateAndDisplayGameControlValues()
2581 if (tape.deactivate_display)
2584 UpdateGameControlValues();
2585 DisplayGameControlValues();
2588 void UpdateGameDoorValues()
2590 UpdateGameControlValues();
2593 void DrawGameDoorValues()
2595 DisplayGameControlValues();
2600 =============================================================================
2602 -----------------------------------------------------------------------------
2603 initialize game engine due to level / tape version number
2604 =============================================================================
2607 static void InitGameEngine()
2609 int i, j, k, l, x, y;
2611 /* set game engine from tape file when re-playing, else from level file */
2612 game.engine_version = (tape.playing ? tape.engine_version :
2613 level.game_version);
2615 /* set single or multi-player game mode (needed for re-playing tapes) */
2616 game.team_mode = setup.team_mode;
2620 int num_players = 0;
2622 for (i = 0; i < MAX_PLAYERS; i++)
2623 if (tape.player_participates[i])
2626 /* multi-player tapes contain input data for more than one player */
2627 game.team_mode = (num_players > 1);
2630 /* ---------------------------------------------------------------------- */
2631 /* set flags for bugs and changes according to active game engine version */
2632 /* ---------------------------------------------------------------------- */
2635 Summary of bugfix/change:
2636 Fixed handling for custom elements that change when pushed by the player.
2638 Fixed/changed in version:
2642 Before 3.1.0, custom elements that "change when pushing" changed directly
2643 after the player started pushing them (until then handled in "DigField()").
2644 Since 3.1.0, these custom elements are not changed until the "pushing"
2645 move of the element is finished (now handled in "ContinueMoving()").
2647 Affected levels/tapes:
2648 The first condition is generally needed for all levels/tapes before version
2649 3.1.0, which might use the old behaviour before it was changed; known tapes
2650 that are affected are some tapes from the level set "Walpurgis Gardens" by
2652 The second condition is an exception from the above case and is needed for
2653 the special case of tapes recorded with game (not engine!) version 3.1.0 or
2654 above (including some development versions of 3.1.0), but before it was
2655 known that this change would break tapes like the above and was fixed in
2656 3.1.1, so that the changed behaviour was active although the engine version
2657 while recording maybe was before 3.1.0. There is at least one tape that is
2658 affected by this exception, which is the tape for the one-level set "Bug
2659 Machine" by Juergen Bonhagen.
2662 game.use_change_when_pushing_bug =
2663 (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2665 tape.game_version >= VERSION_IDENT(3,1,0,0) &&
2666 tape.game_version < VERSION_IDENT(3,1,1,0)));
2669 Summary of bugfix/change:
2670 Fixed handling for blocking the field the player leaves when moving.
2672 Fixed/changed in version:
2676 Before 3.1.1, when "block last field when moving" was enabled, the field
2677 the player is leaving when moving was blocked for the time of the move,
2678 and was directly unblocked afterwards. This resulted in the last field
2679 being blocked for exactly one less than the number of frames of one player
2680 move. Additionally, even when blocking was disabled, the last field was
2681 blocked for exactly one frame.
2682 Since 3.1.1, due to changes in player movement handling, the last field
2683 is not blocked at all when blocking is disabled. When blocking is enabled,
2684 the last field is blocked for exactly the number of frames of one player
2685 move. Additionally, if the player is Murphy, the hero of Supaplex, the
2686 last field is blocked for exactly one more than the number of frames of
2689 Affected levels/tapes:
2690 (!!! yet to be determined -- probably many !!!)
2693 game.use_block_last_field_bug =
2694 (game.engine_version < VERSION_IDENT(3,1,1,0));
2696 /* ---------------------------------------------------------------------- */
2698 /* set maximal allowed number of custom element changes per game frame */
2699 game.max_num_changes_per_frame = 1;
2701 /* default scan direction: scan playfield from top/left to bottom/right */
2702 InitPlayfieldScanMode(CA_ARG_SCAN_MODE_NORMAL);
2704 /* dynamically adjust element properties according to game engine version */
2705 InitElementPropertiesEngine(game.engine_version);
2708 printf("level %d: level version == %06d\n", level_nr, level.game_version);
2709 printf(" tape version == %06d [%s] [file: %06d]\n",
2710 tape.engine_version, (tape.playing ? "PLAYING" : "RECORDING"),
2712 printf(" => game.engine_version == %06d\n", game.engine_version);
2715 /* ---------- initialize player's initial move delay --------------------- */
2717 /* dynamically adjust player properties according to level information */
2718 for (i = 0; i < MAX_PLAYERS; i++)
2719 game.initial_move_delay_value[i] =
2720 get_move_delay_from_stepsize(level.initial_player_stepsize[i]);
2722 /* dynamically adjust player properties according to game engine version */
2723 for (i = 0; i < MAX_PLAYERS; i++)
2724 game.initial_move_delay[i] =
2725 (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
2726 game.initial_move_delay_value[i] : 0);
2728 /* ---------- initialize player's initial push delay --------------------- */
2730 /* dynamically adjust player properties according to game engine version */
2731 game.initial_push_delay_value =
2732 (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
2734 /* ---------- initialize changing elements ------------------------------- */
2736 /* initialize changing elements information */
2737 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2739 struct ElementInfo *ei = &element_info[i];
2741 /* this pointer might have been changed in the level editor */
2742 ei->change = &ei->change_page[0];
2744 if (!IS_CUSTOM_ELEMENT(i))
2746 ei->change->target_element = EL_EMPTY_SPACE;
2747 ei->change->delay_fixed = 0;
2748 ei->change->delay_random = 0;
2749 ei->change->delay_frames = 1;
2752 for (j = 0; j < NUM_CHANGE_EVENTS; j++)
2754 ei->has_change_event[j] = FALSE;
2756 ei->event_page_nr[j] = 0;
2757 ei->event_page[j] = &ei->change_page[0];
2761 /* add changing elements from pre-defined list */
2762 for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
2764 struct ChangingElementInfo *ch_delay = &change_delay_list[i];
2765 struct ElementInfo *ei = &element_info[ch_delay->element];
2767 ei->change->target_element = ch_delay->target_element;
2768 ei->change->delay_fixed = ch_delay->change_delay;
2770 ei->change->pre_change_function = ch_delay->pre_change_function;
2771 ei->change->change_function = ch_delay->change_function;
2772 ei->change->post_change_function = ch_delay->post_change_function;
2774 ei->change->can_change = TRUE;
2775 ei->change->can_change_or_has_action = TRUE;
2777 ei->has_change_event[CE_DELAY] = TRUE;
2779 SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
2780 SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE_OR_HAS_ACTION, TRUE);
2783 /* ---------- initialize internal run-time variables --------------------- */
2785 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2787 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
2789 for (j = 0; j < ei->num_change_pages; j++)
2791 ei->change_page[j].can_change_or_has_action =
2792 (ei->change_page[j].can_change |
2793 ei->change_page[j].has_action);
2797 /* add change events from custom element configuration */
2798 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2800 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
2802 for (j = 0; j < ei->num_change_pages; j++)
2804 if (!ei->change_page[j].can_change_or_has_action)
2807 for (k = 0; k < NUM_CHANGE_EVENTS; k++)
2809 /* only add event page for the first page found with this event */
2810 if (ei->change_page[j].has_event[k] && !(ei->has_change_event[k]))
2812 ei->has_change_event[k] = TRUE;
2814 ei->event_page_nr[k] = j;
2815 ei->event_page[k] = &ei->change_page[j];
2821 /* ---------- initialize reference elements in change conditions --------- */
2823 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2825 int element = EL_CUSTOM_START + i;
2826 struct ElementInfo *ei = &element_info[element];
2828 for (j = 0; j < ei->num_change_pages; j++)
2830 int trigger_element = ei->change_page[j].initial_trigger_element;
2832 if (trigger_element >= EL_PREV_CE_8 &&
2833 trigger_element <= EL_NEXT_CE_8)
2834 trigger_element = RESOLVED_REFERENCE_ELEMENT(element, trigger_element);
2836 ei->change_page[j].trigger_element = trigger_element;
2840 /* ---------- initialize run-time trigger player and element ------------- */
2842 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2844 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
2846 for (j = 0; j < ei->num_change_pages; j++)
2848 ei->change_page[j].actual_trigger_element = EL_EMPTY;
2849 ei->change_page[j].actual_trigger_player = EL_EMPTY;
2850 ei->change_page[j].actual_trigger_player_bits = CH_PLAYER_NONE;
2851 ei->change_page[j].actual_trigger_side = CH_SIDE_NONE;
2852 ei->change_page[j].actual_trigger_ce_value = 0;
2853 ei->change_page[j].actual_trigger_ce_score = 0;
2857 /* ---------- initialize trigger events ---------------------------------- */
2859 /* initialize trigger events information */
2860 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2861 for (j = 0; j < NUM_CHANGE_EVENTS; j++)
2862 trigger_events[i][j] = FALSE;
2864 /* add trigger events from element change event properties */
2865 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2867 struct ElementInfo *ei = &element_info[i];
2869 for (j = 0; j < ei->num_change_pages; j++)
2871 if (!ei->change_page[j].can_change_or_has_action)
2874 if (ei->change_page[j].has_event[CE_BY_OTHER_ACTION])
2876 int trigger_element = ei->change_page[j].trigger_element;
2878 for (k = 0; k < NUM_CHANGE_EVENTS; k++)
2880 if (ei->change_page[j].has_event[k])
2882 if (IS_GROUP_ELEMENT(trigger_element))
2884 struct ElementGroupInfo *group =
2885 element_info[trigger_element].group;
2887 for (l = 0; l < group->num_elements_resolved; l++)
2888 trigger_events[group->element_resolved[l]][k] = TRUE;
2890 else if (trigger_element == EL_ANY_ELEMENT)
2891 for (l = 0; l < MAX_NUM_ELEMENTS; l++)
2892 trigger_events[l][k] = TRUE;
2894 trigger_events[trigger_element][k] = TRUE;
2901 /* ---------- initialize push delay -------------------------------------- */
2903 /* initialize push delay values to default */
2904 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2906 if (!IS_CUSTOM_ELEMENT(i))
2908 /* set default push delay values (corrected since version 3.0.7-1) */
2909 if (game.engine_version < VERSION_IDENT(3,0,7,1))
2911 element_info[i].push_delay_fixed = 2;
2912 element_info[i].push_delay_random = 8;
2916 element_info[i].push_delay_fixed = 8;
2917 element_info[i].push_delay_random = 8;
2922 /* set push delay value for certain elements from pre-defined list */
2923 for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
2925 int e = push_delay_list[i].element;
2927 element_info[e].push_delay_fixed = push_delay_list[i].push_delay_fixed;
2928 element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
2931 /* set push delay value for Supaplex elements for newer engine versions */
2932 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
2934 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2936 if (IS_SP_ELEMENT(i))
2938 /* set SP push delay to just enough to push under a falling zonk */
2939 int delay = (game.engine_version >= VERSION_IDENT(3,1,1,0) ? 8 : 6);
2941 element_info[i].push_delay_fixed = delay;
2942 element_info[i].push_delay_random = 0;
2947 /* ---------- initialize move stepsize ----------------------------------- */
2949 /* initialize move stepsize values to default */
2950 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2951 if (!IS_CUSTOM_ELEMENT(i))
2952 element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
2954 /* set move stepsize value for certain elements from pre-defined list */
2955 for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
2957 int e = move_stepsize_list[i].element;
2959 element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
2962 /* ---------- initialize collect score ----------------------------------- */
2964 /* initialize collect score values for custom elements from initial value */
2965 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2966 if (IS_CUSTOM_ELEMENT(i))
2967 element_info[i].collect_score = element_info[i].collect_score_initial;
2969 /* ---------- initialize collect count ----------------------------------- */
2971 /* initialize collect count values for non-custom elements */
2972 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2973 if (!IS_CUSTOM_ELEMENT(i))
2974 element_info[i].collect_count_initial = 0;
2976 /* add collect count values for all elements from pre-defined list */
2977 for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
2978 element_info[collect_count_list[i].element].collect_count_initial =
2979 collect_count_list[i].count;
2981 /* ---------- initialize access direction -------------------------------- */
2983 /* initialize access direction values to default (access from every side) */
2984 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2985 if (!IS_CUSTOM_ELEMENT(i))
2986 element_info[i].access_direction = MV_ALL_DIRECTIONS;
2988 /* set access direction value for certain elements from pre-defined list */
2989 for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
2990 element_info[access_direction_list[i].element].access_direction =
2991 access_direction_list[i].direction;
2993 /* ---------- initialize explosion content ------------------------------- */
2994 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2996 if (IS_CUSTOM_ELEMENT(i))
2999 for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
3001 /* (content for EL_YAMYAM set at run-time with game.yamyam_content_nr) */
3003 element_info[i].content.e[x][y] =
3004 (i == EL_PLAYER_1 ? EL_EMERALD_YELLOW :
3005 i == EL_PLAYER_2 ? EL_EMERALD_RED :
3006 i == EL_PLAYER_3 ? EL_EMERALD :
3007 i == EL_PLAYER_4 ? EL_EMERALD_PURPLE :
3008 i == EL_MOLE ? EL_EMERALD_RED :
3009 i == EL_PENGUIN ? EL_EMERALD_PURPLE :
3010 i == EL_BUG ? (x == 1 && y == 1 ? EL_DIAMOND : EL_EMERALD) :
3011 i == EL_BD_BUTTERFLY ? EL_BD_DIAMOND :
3012 i == EL_SP_ELECTRON ? EL_SP_INFOTRON :
3013 i == EL_AMOEBA_TO_DIAMOND ? level.amoeba_content :
3014 i == EL_WALL_EMERALD ? EL_EMERALD :
3015 i == EL_WALL_DIAMOND ? EL_DIAMOND :
3016 i == EL_WALL_BD_DIAMOND ? EL_BD_DIAMOND :
3017 i == EL_WALL_EMERALD_YELLOW ? EL_EMERALD_YELLOW :
3018 i == EL_WALL_EMERALD_RED ? EL_EMERALD_RED :
3019 i == EL_WALL_EMERALD_PURPLE ? EL_EMERALD_PURPLE :
3020 i == EL_WALL_PEARL ? EL_PEARL :
3021 i == EL_WALL_CRYSTAL ? EL_CRYSTAL :
3026 /* ---------- initialize recursion detection ------------------------------ */
3027 recursion_loop_depth = 0;
3028 recursion_loop_detected = FALSE;
3029 recursion_loop_element = EL_UNDEFINED;
3031 /* ---------- initialize graphics engine ---------------------------------- */
3032 game.scroll_delay_value =
3033 (game.forced_scroll_delay_value != -1 ? game.forced_scroll_delay_value :
3034 setup.scroll_delay ? setup.scroll_delay_value : 0);
3035 game.scroll_delay_value =
3036 MIN(MAX(MIN_SCROLL_DELAY, game.scroll_delay_value), MAX_SCROLL_DELAY);
3038 FreeEngineSnapshotList();
3041 int get_num_special_action(int element, int action_first, int action_last)
3043 int num_special_action = 0;
3046 for (i = action_first; i <= action_last; i++)
3048 boolean found = FALSE;
3050 for (j = 0; j < NUM_DIRECTIONS; j++)
3051 if (el_act_dir2img(element, i, j) !=
3052 el_act_dir2img(element, ACTION_DEFAULT, j))
3056 num_special_action++;
3061 return num_special_action;
3066 =============================================================================
3068 -----------------------------------------------------------------------------
3069 initialize and start new game
3070 =============================================================================
3075 int full_lev_fieldx = lev_fieldx + (BorderElement != EL_EMPTY ? 2 : 0);
3076 int full_lev_fieldy = lev_fieldy + (BorderElement != EL_EMPTY ? 2 : 0);
3078 boolean emulate_bd = TRUE; /* unless non-BOULDERDASH elements found */
3079 boolean emulate_sb = TRUE; /* unless non-SOKOBAN elements found */
3080 boolean emulate_sp = TRUE; /* unless non-SUPAPLEX elements found */
3081 int initial_move_dir = MV_DOWN;
3084 game_status = GAME_MODE_PLAYING;
3088 if (!game.restart_level)
3089 CloseDoor(DOOR_CLOSE_1);
3091 if (level_editor_test_game)
3092 FadeSkipNextFadeIn();
3094 FadeSetEnterScreen();
3096 FadeOut(REDRAW_FIELD);
3098 /* needed if different viewport properties defined for playing */
3099 ChangeViewportPropertiesIfNeeded();
3101 DrawCompleteVideoDisplay();
3104 InitGameControlValues();
3106 /* don't play tapes over network */
3107 network_playing = (options.network && !tape.playing);
3109 for (i = 0; i < MAX_PLAYERS; i++)
3111 struct PlayerInfo *player = &stored_player[i];
3113 player->index_nr = i;
3114 player->index_bit = (1 << i);
3115 player->element_nr = EL_PLAYER_1 + i;
3117 player->present = FALSE;
3118 player->active = FALSE;
3119 player->mapped = FALSE;
3121 player->killed = FALSE;
3122 player->reanimated = FALSE;
3125 player->effective_action = 0;
3126 player->programmed_action = 0;
3129 player->score_final = 0;
3131 player->gems_still_needed = level.gems_needed;
3132 player->sokobanfields_still_needed = 0;
3133 player->lights_still_needed = 0;
3134 player->friends_still_needed = 0;
3136 for (j = 0; j < MAX_NUM_KEYS; j++)
3137 player->key[j] = FALSE;
3139 player->num_white_keys = 0;
3141 player->dynabomb_count = 0;
3142 player->dynabomb_size = 1;
3143 player->dynabombs_left = 0;
3144 player->dynabomb_xl = FALSE;
3146 player->MovDir = initial_move_dir;
3149 player->GfxDir = initial_move_dir;
3150 player->GfxAction = ACTION_DEFAULT;
3152 player->StepFrame = 0;
3154 player->initial_element = player->element_nr;
3155 player->artwork_element =
3156 (level.use_artwork_element[i] ? level.artwork_element[i] :
3157 player->element_nr);
3158 player->use_murphy = FALSE;
3160 player->block_last_field = FALSE; /* initialized in InitPlayerField() */
3161 player->block_delay_adjustment = 0; /* initialized in InitPlayerField() */
3163 player->gravity = level.initial_player_gravity[i];
3165 player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
3167 player->actual_frame_counter = 0;
3169 player->step_counter = 0;
3171 player->last_move_dir = initial_move_dir;
3173 player->is_active = FALSE;
3175 player->is_waiting = FALSE;
3176 player->is_moving = FALSE;
3177 player->is_auto_moving = FALSE;
3178 player->is_digging = FALSE;
3179 player->is_snapping = FALSE;
3180 player->is_collecting = FALSE;
3181 player->is_pushing = FALSE;
3182 player->is_switching = FALSE;
3183 player->is_dropping = FALSE;
3184 player->is_dropping_pressed = FALSE;
3186 player->is_bored = FALSE;
3187 player->is_sleeping = FALSE;
3189 player->frame_counter_bored = -1;
3190 player->frame_counter_sleeping = -1;
3192 player->anim_delay_counter = 0;
3193 player->post_delay_counter = 0;
3195 player->dir_waiting = initial_move_dir;
3196 player->action_waiting = ACTION_DEFAULT;
3197 player->last_action_waiting = ACTION_DEFAULT;
3198 player->special_action_bored = ACTION_DEFAULT;
3199 player->special_action_sleeping = ACTION_DEFAULT;
3201 player->switch_x = -1;
3202 player->switch_y = -1;
3204 player->drop_x = -1;
3205 player->drop_y = -1;
3207 player->show_envelope = 0;
3209 SetPlayerMoveSpeed(player, level.initial_player_stepsize[i], TRUE);
3211 player->push_delay = -1; /* initialized when pushing starts */
3212 player->push_delay_value = game.initial_push_delay_value;
3214 player->drop_delay = 0;
3215 player->drop_pressed_delay = 0;
3217 player->last_jx = -1;
3218 player->last_jy = -1;
3222 player->shield_normal_time_left = 0;
3223 player->shield_deadly_time_left = 0;
3225 player->inventory_infinite_element = EL_UNDEFINED;
3226 player->inventory_size = 0;
3228 if (level.use_initial_inventory[i])
3230 for (j = 0; j < level.initial_inventory_size[i]; j++)
3232 int element = level.initial_inventory_content[i][j];
3233 int collect_count = element_info[element].collect_count_initial;
3236 if (!IS_CUSTOM_ELEMENT(element))
3239 if (collect_count == 0)
3240 player->inventory_infinite_element = element;
3242 for (k = 0; k < collect_count; k++)
3243 if (player->inventory_size < MAX_INVENTORY_SIZE)
3244 player->inventory_element[player->inventory_size++] = element;
3248 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
3249 SnapField(player, 0, 0);
3251 player->LevelSolved = FALSE;
3252 player->GameOver = FALSE;
3254 player->LevelSolved_GameWon = FALSE;
3255 player->LevelSolved_GameEnd = FALSE;
3256 player->LevelSolved_PanelOff = FALSE;
3257 player->LevelSolved_SaveTape = FALSE;
3258 player->LevelSolved_SaveScore = FALSE;
3259 player->LevelSolved_CountingTime = 0;
3260 player->LevelSolved_CountingScore = 0;
3262 map_player_action[i] = i;
3265 network_player_action_received = FALSE;
3267 #if defined(NETWORK_AVALIABLE)
3268 /* initial null action */
3269 if (network_playing)
3270 SendToServer_MovePlayer(MV_NONE);
3279 TimeLeft = level.time;
3282 ScreenMovDir = MV_NONE;
3286 ScrollStepSize = 0; /* will be correctly initialized by ScrollScreen() */
3288 AllPlayersGone = FALSE;
3290 game.no_time_limit = (level.time == 0);
3292 game.yamyam_content_nr = 0;
3293 game.robot_wheel_active = FALSE;
3294 game.magic_wall_active = FALSE;
3295 game.magic_wall_time_left = 0;
3296 game.light_time_left = 0;
3297 game.timegate_time_left = 0;
3298 game.switchgate_pos = 0;
3299 game.wind_direction = level.wind_direction_initial;
3301 game.lenses_time_left = 0;
3302 game.magnify_time_left = 0;
3304 game.ball_state = level.ball_state_initial;
3305 game.ball_content_nr = 0;
3307 game.envelope_active = FALSE;
3309 /* set focus to local player for network games, else to all players */
3310 game.centered_player_nr = (network_playing ? local_player->index_nr : -1);
3311 game.centered_player_nr_next = game.centered_player_nr;
3312 game.set_centered_player = FALSE;
3314 if (network_playing && tape.recording)
3316 /* store client dependent player focus when recording network games */
3317 tape.centered_player_nr_next = game.centered_player_nr_next;
3318 tape.set_centered_player = TRUE;
3321 for (i = 0; i < NUM_BELTS; i++)
3323 game.belt_dir[i] = MV_NONE;
3324 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
3327 for (i = 0; i < MAX_NUM_AMOEBA; i++)
3328 AmoebaCnt[i] = AmoebaCnt2[i] = 0;
3330 #if DEBUG_INIT_PLAYER
3333 printf("Player status at level initialization:\n");
3337 SCAN_PLAYFIELD(x, y)
3339 Feld[x][y] = level.field[x][y];
3340 MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
3341 ChangeDelay[x][y] = 0;
3342 ChangePage[x][y] = -1;
3343 CustomValue[x][y] = 0; /* initialized in InitField() */
3344 Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
3346 WasJustMoving[x][y] = 0;
3347 WasJustFalling[x][y] = 0;
3348 CheckCollision[x][y] = 0;
3349 CheckImpact[x][y] = 0;
3351 Pushed[x][y] = FALSE;
3353 ChangeCount[x][y] = 0;
3354 ChangeEvent[x][y] = -1;
3356 ExplodePhase[x][y] = 0;
3357 ExplodeDelay[x][y] = 0;
3358 ExplodeField[x][y] = EX_TYPE_NONE;
3360 RunnerVisit[x][y] = 0;
3361 PlayerVisit[x][y] = 0;
3364 GfxRandom[x][y] = INIT_GFX_RANDOM();
3365 GfxElement[x][y] = EL_UNDEFINED;
3366 GfxAction[x][y] = ACTION_DEFAULT;
3367 GfxDir[x][y] = MV_NONE;
3368 GfxRedraw[x][y] = GFX_REDRAW_NONE;
3371 SCAN_PLAYFIELD(x, y)
3373 if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
3375 if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
3377 if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
3380 InitField(x, y, TRUE);
3382 ResetGfxAnimation(x, y);
3387 for (i = 0; i < MAX_PLAYERS; i++)
3389 struct PlayerInfo *player = &stored_player[i];
3391 /* set number of special actions for bored and sleeping animation */
3392 player->num_special_action_bored =
3393 get_num_special_action(player->artwork_element,
3394 ACTION_BORING_1, ACTION_BORING_LAST);
3395 player->num_special_action_sleeping =
3396 get_num_special_action(player->artwork_element,
3397 ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
3400 game.emulation = (emulate_bd ? EMU_BOULDERDASH :
3401 emulate_sb ? EMU_SOKOBAN :
3402 emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
3404 /* initialize type of slippery elements */
3405 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3407 if (!IS_CUSTOM_ELEMENT(i))
3409 /* default: elements slip down either to the left or right randomly */
3410 element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
3412 /* SP style elements prefer to slip down on the left side */
3413 if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
3414 element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3416 /* BD style elements prefer to slip down on the left side */
3417 if (game.emulation == EMU_BOULDERDASH)
3418 element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3422 /* initialize explosion and ignition delay */
3423 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3425 if (!IS_CUSTOM_ELEMENT(i))
3428 int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
3429 game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
3430 game.emulation == EMU_SUPAPLEX ? 3 : 2);
3431 int last_phase = (num_phase + 1) * delay;
3432 int half_phase = (num_phase / 2) * delay;
3434 element_info[i].explosion_delay = last_phase - 1;
3435 element_info[i].ignition_delay = half_phase;
3437 if (i == EL_BLACK_ORB)
3438 element_info[i].ignition_delay = 1;
3442 /* correct non-moving belts to start moving left */
3443 for (i = 0; i < NUM_BELTS; i++)
3444 if (game.belt_dir[i] == MV_NONE)
3445 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
3447 #if USE_NEW_PLAYER_ASSIGNMENTS
3448 /* !!! SAME AS init.c:InitPlayerInfo() -- FIX THIS !!! */
3449 /* choose default local player */
3450 local_player = &stored_player[0];
3452 for (i = 0; i < MAX_PLAYERS; i++)
3453 stored_player[i].connected = FALSE;
3455 local_player->connected = TRUE;
3456 /* !!! SAME AS init.c:InitPlayerInfo() -- FIX THIS !!! */
3460 for (i = 0; i < MAX_PLAYERS; i++)
3461 stored_player[i].connected = tape.player_participates[i];
3463 else if (game.team_mode && !options.network)
3465 /* try to guess locally connected team mode players (needed for correct
3466 assignment of player figures from level to locally playing players) */
3468 for (i = 0; i < MAX_PLAYERS; i++)
3469 if (setup.input[i].use_joystick ||
3470 setup.input[i].key.left != KSYM_UNDEFINED)
3471 stored_player[i].connected = TRUE;
3474 #if DEBUG_INIT_PLAYER
3477 printf("Player status after level initialization:\n");
3479 for (i = 0; i < MAX_PLAYERS; i++)
3481 struct PlayerInfo *player = &stored_player[i];
3483 printf("- player %d: present == %d, connected == %d, active == %d",
3489 if (local_player == player)
3490 printf(" (local player)");
3497 #if DEBUG_INIT_PLAYER
3499 printf("Reassigning players ...\n");
3502 /* check if any connected player was not found in playfield */
3503 for (i = 0; i < MAX_PLAYERS; i++)
3505 struct PlayerInfo *player = &stored_player[i];
3507 if (player->connected && !player->present)
3509 struct PlayerInfo *field_player = NULL;
3511 #if DEBUG_INIT_PLAYER
3513 printf("- looking for field player for player %d ...\n", i + 1);
3516 /* assign first free player found that is present in the playfield */
3518 /* first try: look for unmapped playfield player that is not connected */
3519 for (j = 0; j < MAX_PLAYERS; j++)
3520 if (field_player == NULL &&
3521 stored_player[j].present &&
3522 !stored_player[j].mapped &&
3523 !stored_player[j].connected)
3524 field_player = &stored_player[j];
3526 /* second try: look for *any* unmapped playfield player */
3527 for (j = 0; j < MAX_PLAYERS; j++)
3528 if (field_player == NULL &&
3529 stored_player[j].present &&
3530 !stored_player[j].mapped)
3531 field_player = &stored_player[j];
3533 if (field_player != NULL)
3535 int jx = field_player->jx, jy = field_player->jy;
3537 #if DEBUG_INIT_PLAYER
3539 printf("- found player %d\n", field_player->index_nr + 1);
3542 player->present = FALSE;
3543 player->active = FALSE;
3545 field_player->present = TRUE;
3546 field_player->active = TRUE;
3549 player->initial_element = field_player->initial_element;
3550 player->artwork_element = field_player->artwork_element;
3552 player->block_last_field = field_player->block_last_field;
3553 player->block_delay_adjustment = field_player->block_delay_adjustment;
3556 StorePlayer[jx][jy] = field_player->element_nr;
3558 field_player->jx = field_player->last_jx = jx;
3559 field_player->jy = field_player->last_jy = jy;
3561 if (local_player == player)
3562 local_player = field_player;
3564 map_player_action[field_player->index_nr] = i;
3566 field_player->mapped = TRUE;
3568 #if DEBUG_INIT_PLAYER
3570 printf("- map_player_action[%d] == %d\n",
3571 field_player->index_nr + 1, i + 1);
3576 if (player->connected && player->present)
3577 player->mapped = TRUE;
3580 #if DEBUG_INIT_PLAYER
3583 printf("Player status after player assignment (first stage):\n");
3585 for (i = 0; i < MAX_PLAYERS; i++)
3587 struct PlayerInfo *player = &stored_player[i];
3589 printf("- player %d: present == %d, connected == %d, active == %d",
3595 if (local_player == player)
3596 printf(" (local player)");
3605 /* check if any connected player was not found in playfield */
3606 for (i = 0; i < MAX_PLAYERS; i++)
3608 struct PlayerInfo *player = &stored_player[i];
3610 if (player->connected && !player->present)
3612 for (j = 0; j < MAX_PLAYERS; j++)
3614 struct PlayerInfo *field_player = &stored_player[j];
3615 int jx = field_player->jx, jy = field_player->jy;
3617 /* assign first free player found that is present in the playfield */
3618 if (field_player->present && !field_player->connected)
3620 player->present = TRUE;
3621 player->active = TRUE;
3623 field_player->present = FALSE;
3624 field_player->active = FALSE;
3626 player->initial_element = field_player->initial_element;
3627 player->artwork_element = field_player->artwork_element;
3629 player->block_last_field = field_player->block_last_field;
3630 player->block_delay_adjustment = field_player->block_delay_adjustment;
3632 StorePlayer[jx][jy] = player->element_nr;
3634 player->jx = player->last_jx = jx;
3635 player->jy = player->last_jy = jy;
3645 printf("::: local_player->present == %d\n", local_player->present);
3650 /* when playing a tape, eliminate all players who do not participate */
3652 #if USE_NEW_PLAYER_ASSIGNMENTS
3654 if (!game.team_mode)
3656 for (i = 0; i < MAX_PLAYERS; i++)
3658 if (stored_player[i].active &&
3659 !tape.player_participates[map_player_action[i]])
3661 struct PlayerInfo *player = &stored_player[i];
3662 int jx = player->jx, jy = player->jy;
3664 #if DEBUG_INIT_PLAYER
3666 printf("Removing player %d at (%d, %d)\n", i + 1, jx, jy);
3669 player->active = FALSE;
3670 StorePlayer[jx][jy] = 0;
3671 Feld[jx][jy] = EL_EMPTY;
3678 for (i = 0; i < MAX_PLAYERS; i++)
3680 if (stored_player[i].active &&
3681 !tape.player_participates[i])
3683 struct PlayerInfo *player = &stored_player[i];
3684 int jx = player->jx, jy = player->jy;
3686 player->active = FALSE;
3687 StorePlayer[jx][jy] = 0;
3688 Feld[jx][jy] = EL_EMPTY;
3693 else if (!options.network && !game.team_mode) /* && !tape.playing */
3695 /* when in single player mode, eliminate all but the first active player */
3697 for (i = 0; i < MAX_PLAYERS; i++)
3699 if (stored_player[i].active)
3701 for (j = i + 1; j < MAX_PLAYERS; j++)
3703 if (stored_player[j].active)
3705 struct PlayerInfo *player = &stored_player[j];
3706 int jx = player->jx, jy = player->jy;
3708 player->active = FALSE;
3709 player->present = FALSE;
3711 StorePlayer[jx][jy] = 0;
3712 Feld[jx][jy] = EL_EMPTY;
3719 /* when recording the game, store which players take part in the game */
3722 #if USE_NEW_PLAYER_ASSIGNMENTS
3723 for (i = 0; i < MAX_PLAYERS; i++)
3724 if (stored_player[i].connected)
3725 tape.player_participates[i] = TRUE;
3727 for (i = 0; i < MAX_PLAYERS; i++)
3728 if (stored_player[i].active)
3729 tape.player_participates[i] = TRUE;
3733 #if DEBUG_INIT_PLAYER
3736 printf("Player status after player assignment (final stage):\n");
3738 for (i = 0; i < MAX_PLAYERS; i++)
3740 struct PlayerInfo *player = &stored_player[i];
3742 printf("- player %d: present == %d, connected == %d, active == %d",
3748 if (local_player == player)
3749 printf(" (local player)");
3756 if (BorderElement == EL_EMPTY)
3759 SBX_Right = lev_fieldx - SCR_FIELDX;
3761 SBY_Lower = lev_fieldy - SCR_FIELDY;
3766 SBX_Right = lev_fieldx - SCR_FIELDX + 1;
3768 SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
3771 if (full_lev_fieldx <= SCR_FIELDX)
3772 SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
3773 if (full_lev_fieldy <= SCR_FIELDY)
3774 SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
3776 if (EVEN(SCR_FIELDX) && full_lev_fieldx > SCR_FIELDX)
3778 if (EVEN(SCR_FIELDY) && full_lev_fieldy > SCR_FIELDY)
3781 /* if local player not found, look for custom element that might create
3782 the player (make some assumptions about the right custom element) */
3783 if (!local_player->present)
3785 int start_x = 0, start_y = 0;
3786 int found_rating = 0;
3787 int found_element = EL_UNDEFINED;
3788 int player_nr = local_player->index_nr;
3790 SCAN_PLAYFIELD(x, y)
3792 int element = Feld[x][y];
3797 if (level.use_start_element[player_nr] &&
3798 level.start_element[player_nr] == element &&
3805 found_element = element;
3808 if (!IS_CUSTOM_ELEMENT(element))
3811 if (CAN_CHANGE(element))
3813 for (i = 0; i < element_info[element].num_change_pages; i++)
3815 /* check for player created from custom element as single target */
3816 content = element_info[element].change_page[i].target_element;
3817 is_player = ELEM_IS_PLAYER(content);
3819 if (is_player && (found_rating < 3 ||
3820 (found_rating == 3 && element < found_element)))
3826 found_element = element;
3831 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
3833 /* check for player created from custom element as explosion content */
3834 content = element_info[element].content.e[xx][yy];
3835 is_player = ELEM_IS_PLAYER(content);
3837 if (is_player && (found_rating < 2 ||
3838 (found_rating == 2 && element < found_element)))
3840 start_x = x + xx - 1;
3841 start_y = y + yy - 1;
3844 found_element = element;
3847 if (!CAN_CHANGE(element))
3850 for (i = 0; i < element_info[element].num_change_pages; i++)
3852 /* check for player created from custom element as extended target */
3854 element_info[element].change_page[i].target_content.e[xx][yy];
3856 is_player = ELEM_IS_PLAYER(content);
3858 if (is_player && (found_rating < 1 ||
3859 (found_rating == 1 && element < found_element)))
3861 start_x = x + xx - 1;
3862 start_y = y + yy - 1;
3865 found_element = element;
3871 scroll_x = (start_x < SBX_Left + MIDPOSX ? SBX_Left :
3872 start_x > SBX_Right + MIDPOSX ? SBX_Right :
3875 scroll_y = (start_y < SBY_Upper + MIDPOSY ? SBY_Upper :
3876 start_y > SBY_Lower + MIDPOSY ? SBY_Lower :
3881 scroll_x = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
3882 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
3883 local_player->jx - MIDPOSX);
3885 scroll_y = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
3886 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
3887 local_player->jy - MIDPOSY);
3890 /* !!! FIX THIS (START) !!! */
3891 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
3893 InitGameEngine_EM();
3895 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
3897 InitGameEngine_SP();
3901 DrawLevel(REDRAW_FIELD);
3904 /* after drawing the level, correct some elements */
3905 if (game.timegate_time_left == 0)
3906 CloseAllOpenTimegates();
3909 /* blit playfield from scroll buffer to normal back buffer for fading in */
3910 BlitScreenToBitmap(backbuffer);
3912 redraw_mask |= REDRAW_FROM_BACKBUFFER;
3913 /* !!! FIX THIS (END) !!! */
3915 FadeIn(REDRAW_FIELD);
3918 // full screen redraw is required at this point in the following cases:
3919 // - special editor door undrawn when game was started from level editor
3920 // - drawing area (playfield) was changed and has to be removed completely
3921 redraw_mask = REDRAW_ALL;
3925 if (!game.restart_level)
3927 /* copy default game door content to main double buffer */
3929 /* !!! CHECK AGAIN !!! */
3930 SetPanelBackground();
3931 // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
3932 DrawBackground(DX, DY, DXSIZE, DYSIZE);
3935 SetPanelBackground();
3936 SetDrawBackgroundMask(REDRAW_DOOR_1);
3938 UpdateAndDisplayGameControlValues();
3940 if (!game.restart_level)
3944 game_gadget[SOUND_CTRL_ID_MUSIC]->checked = setup.sound_music;
3945 game_gadget[SOUND_CTRL_ID_LOOPS]->checked = setup.sound_loops;
3946 game_gadget[SOUND_CTRL_ID_SIMPLE]->checked = setup.sound_simple;
3950 /* copy actual game door content to door double buffer for OpenDoor() */
3951 BlitBitmap(drawto, bitmap_db_door_1, DX, DY, DXSIZE, DYSIZE, 0, 0);
3953 OpenDoor(DOOR_OPEN_ALL);
3955 PlaySound(SND_GAME_STARTING);
3957 if (setup.sound_music)
3960 KeyboardAutoRepeatOffUnlessAutoplay();
3962 #if DEBUG_INIT_PLAYER
3965 printf("Player status (final):\n");
3967 for (i = 0; i < MAX_PLAYERS; i++)
3969 struct PlayerInfo *player = &stored_player[i];
3971 printf("- player %d: present == %d, connected == %d, active == %d",
3977 if (local_player == player)
3978 printf(" (local player)");
3991 if (!game.restart_level && !tape.playing)
3993 LevelStats_incPlayed(level_nr);
3995 SaveLevelSetup_SeriesInfo();
3998 game.restart_level = FALSE;
4000 SaveEngineSnapshotToList();
4003 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y)
4005 /* this is used for non-R'n'D game engines to update certain engine values */
4007 /* needed to determine if sounds are played within the visible screen area */
4008 scroll_x = actual_scroll_x;
4009 scroll_y = actual_scroll_y;
4012 void InitMovDir(int x, int y)
4014 int i, element = Feld[x][y];
4015 static int xy[4][2] =
4022 static int direction[3][4] =
4024 { MV_RIGHT, MV_UP, MV_LEFT, MV_DOWN },
4025 { MV_LEFT, MV_DOWN, MV_RIGHT, MV_UP },
4026 { MV_LEFT, MV_RIGHT, MV_UP, MV_DOWN }
4035 Feld[x][y] = EL_BUG;
4036 MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
4039 case EL_SPACESHIP_RIGHT:
4040 case EL_SPACESHIP_UP:
4041 case EL_SPACESHIP_LEFT:
4042 case EL_SPACESHIP_DOWN:
4043 Feld[x][y] = EL_SPACESHIP;
4044 MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
4047 case EL_BD_BUTTERFLY_RIGHT:
4048 case EL_BD_BUTTERFLY_UP:
4049 case EL_BD_BUTTERFLY_LEFT:
4050 case EL_BD_BUTTERFLY_DOWN:
4051 Feld[x][y] = EL_BD_BUTTERFLY;
4052 MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
4055 case EL_BD_FIREFLY_RIGHT:
4056 case EL_BD_FIREFLY_UP:
4057 case EL_BD_FIREFLY_LEFT:
4058 case EL_BD_FIREFLY_DOWN:
4059 Feld[x][y] = EL_BD_FIREFLY;
4060 MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
4063 case EL_PACMAN_RIGHT:
4065 case EL_PACMAN_LEFT:
4066 case EL_PACMAN_DOWN:
4067 Feld[x][y] = EL_PACMAN;
4068 MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
4071 case EL_YAMYAM_LEFT:
4072 case EL_YAMYAM_RIGHT:
4074 case EL_YAMYAM_DOWN:
4075 Feld[x][y] = EL_YAMYAM;
4076 MovDir[x][y] = direction[2][element - EL_YAMYAM_LEFT];
4079 case EL_SP_SNIKSNAK:
4080 MovDir[x][y] = MV_UP;
4083 case EL_SP_ELECTRON:
4084 MovDir[x][y] = MV_LEFT;
4091 Feld[x][y] = EL_MOLE;
4092 MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
4096 if (IS_CUSTOM_ELEMENT(element))
4098 struct ElementInfo *ei = &element_info[element];
4099 int move_direction_initial = ei->move_direction_initial;
4100 int move_pattern = ei->move_pattern;
4102 if (move_direction_initial == MV_START_PREVIOUS)
4104 if (MovDir[x][y] != MV_NONE)
4107 move_direction_initial = MV_START_AUTOMATIC;
4110 if (move_direction_initial == MV_START_RANDOM)
4111 MovDir[x][y] = 1 << RND(4);
4112 else if (move_direction_initial & MV_ANY_DIRECTION)
4113 MovDir[x][y] = move_direction_initial;
4114 else if (move_pattern == MV_ALL_DIRECTIONS ||
4115 move_pattern == MV_TURNING_LEFT ||
4116 move_pattern == MV_TURNING_RIGHT ||
4117 move_pattern == MV_TURNING_LEFT_RIGHT ||
4118 move_pattern == MV_TURNING_RIGHT_LEFT ||
4119 move_pattern == MV_TURNING_RANDOM)
4120 MovDir[x][y] = 1 << RND(4);
4121 else if (move_pattern == MV_HORIZONTAL)
4122 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
4123 else if (move_pattern == MV_VERTICAL)
4124 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
4125 else if (move_pattern & MV_ANY_DIRECTION)
4126 MovDir[x][y] = element_info[element].move_pattern;
4127 else if (move_pattern == MV_ALONG_LEFT_SIDE ||
4128 move_pattern == MV_ALONG_RIGHT_SIDE)
4130 /* use random direction as default start direction */
4131 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
4132 MovDir[x][y] = 1 << RND(4);
4134 for (i = 0; i < NUM_DIRECTIONS; i++)
4136 int x1 = x + xy[i][0];
4137 int y1 = y + xy[i][1];
4139 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4141 if (move_pattern == MV_ALONG_RIGHT_SIDE)
4142 MovDir[x][y] = direction[0][i];
4144 MovDir[x][y] = direction[1][i];
4153 MovDir[x][y] = 1 << RND(4);
4155 if (element != EL_BUG &&
4156 element != EL_SPACESHIP &&
4157 element != EL_BD_BUTTERFLY &&
4158 element != EL_BD_FIREFLY)
4161 for (i = 0; i < NUM_DIRECTIONS; i++)
4163 int x1 = x + xy[i][0];
4164 int y1 = y + xy[i][1];
4166 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4168 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
4170 MovDir[x][y] = direction[0][i];
4173 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
4174 element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4176 MovDir[x][y] = direction[1][i];
4185 GfxDir[x][y] = MovDir[x][y];
4188 void InitAmoebaNr(int x, int y)
4191 int group_nr = AmoebeNachbarNr(x, y);
4195 for (i = 1; i < MAX_NUM_AMOEBA; i++)
4197 if (AmoebaCnt[i] == 0)
4205 AmoebaNr[x][y] = group_nr;
4206 AmoebaCnt[group_nr]++;
4207 AmoebaCnt2[group_nr]++;
4210 static void PlayerWins(struct PlayerInfo *player)
4212 player->LevelSolved = TRUE;
4213 player->GameOver = TRUE;
4215 player->score_final = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
4216 level.native_em_level->lev->score : player->score);
4218 player->LevelSolved_CountingTime = (game.no_time_limit ? TimePlayed :
4220 player->LevelSolved_CountingScore = player->score_final;
4225 static int time, time_final;
4226 static int score, score_final;
4227 static int game_over_delay_1 = 0;
4228 static int game_over_delay_2 = 0;
4229 int game_over_delay_value_1 = 50;
4230 int game_over_delay_value_2 = 50;
4232 if (!local_player->LevelSolved_GameWon)
4236 /* do not start end game actions before the player stops moving (to exit) */
4237 if (local_player->MovPos)
4240 local_player->LevelSolved_GameWon = TRUE;
4241 local_player->LevelSolved_SaveTape = tape.recording;
4242 local_player->LevelSolved_SaveScore = !tape.playing;
4246 LevelStats_incSolved(level_nr);
4248 SaveLevelSetup_SeriesInfo();
4251 if (tape.auto_play) /* tape might already be stopped here */
4252 tape.auto_play_level_solved = TRUE;
4256 game_over_delay_1 = game_over_delay_value_1;
4257 game_over_delay_2 = game_over_delay_value_2;
4259 time = time_final = (game.no_time_limit ? TimePlayed : TimeLeft);
4260 score = score_final = local_player->score_final;
4265 score_final += TimeLeft * level.score[SC_TIME_BONUS];
4267 else if (game.no_time_limit && TimePlayed < 999)
4270 score_final += (999 - TimePlayed) * level.score[SC_TIME_BONUS];
4273 local_player->score_final = score_final;
4275 if (level_editor_test_game)
4278 score = score_final;
4280 local_player->LevelSolved_CountingTime = time;
4281 local_player->LevelSolved_CountingScore = score;
4283 game_panel_controls[GAME_PANEL_TIME].value = time;
4284 game_panel_controls[GAME_PANEL_SCORE].value = score;
4286 DisplayGameControlValues();
4289 if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
4291 if (ExitX >= 0 && ExitY >= 0) /* local player has left the level */
4293 /* close exit door after last player */
4294 if ((AllPlayersGone &&
4295 (Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
4296 Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN ||
4297 Feld[ExitX][ExitY] == EL_STEEL_EXIT_OPEN)) ||
4298 Feld[ExitX][ExitY] == EL_EM_EXIT_OPEN ||
4299 Feld[ExitX][ExitY] == EL_EM_STEEL_EXIT_OPEN)
4301 int element = Feld[ExitX][ExitY];
4303 Feld[ExitX][ExitY] =
4304 (element == EL_EXIT_OPEN ? EL_EXIT_CLOSING :
4305 element == EL_EM_EXIT_OPEN ? EL_EM_EXIT_CLOSING :
4306 element == EL_SP_EXIT_OPEN ? EL_SP_EXIT_CLOSING:
4307 element == EL_STEEL_EXIT_OPEN ? EL_STEEL_EXIT_CLOSING:
4308 EL_EM_STEEL_EXIT_CLOSING);
4310 PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING);
4313 /* player disappears */
4314 DrawLevelField(ExitX, ExitY);
4317 for (i = 0; i < MAX_PLAYERS; i++)
4319 struct PlayerInfo *player = &stored_player[i];
4321 if (player->present)
4323 RemovePlayer(player);
4325 /* player disappears */
4326 DrawLevelField(player->jx, player->jy);
4331 PlaySound(SND_GAME_WINNING);
4334 if (game_over_delay_1 > 0)
4336 game_over_delay_1--;
4341 if (time != time_final)
4343 int time_to_go = ABS(time_final - time);
4344 int time_count_dir = (time < time_final ? +1 : -1);
4345 int time_count_steps = (time_to_go > 100 && time_to_go % 10 == 0 ? 10 : 1);
4347 time += time_count_steps * time_count_dir;
4348 score += time_count_steps * level.score[SC_TIME_BONUS];
4350 local_player->LevelSolved_CountingTime = time;
4351 local_player->LevelSolved_CountingScore = score;
4353 game_panel_controls[GAME_PANEL_TIME].value = time;
4354 game_panel_controls[GAME_PANEL_SCORE].value = score;
4356 DisplayGameControlValues();
4358 if (time == time_final)
4359 StopSound(SND_GAME_LEVELTIME_BONUS);
4360 else if (setup.sound_loops)
4361 PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4363 PlaySound(SND_GAME_LEVELTIME_BONUS);
4368 local_player->LevelSolved_PanelOff = TRUE;
4370 if (game_over_delay_2 > 0)
4372 game_over_delay_2--;
4383 boolean raise_level = FALSE;
4385 local_player->LevelSolved_GameEnd = TRUE;
4387 CloseDoor(DOOR_CLOSE_1);
4389 if (local_player->LevelSolved_SaveTape)
4391 SaveTapeChecked(tape.level_nr); /* ask to save tape */
4394 if (level_editor_test_game)
4396 game_status = GAME_MODE_MAIN;
4398 DrawAndFadeInMainMenu(REDRAW_FIELD);
4403 if (!local_player->LevelSolved_SaveScore)
4405 FadeOut(REDRAW_FIELD);
4407 game_status = GAME_MODE_MAIN;
4409 DrawAndFadeInMainMenu(REDRAW_FIELD);
4414 if (level_nr == leveldir_current->handicap_level)
4416 leveldir_current->handicap_level++;
4418 SaveLevelSetup_SeriesInfo();
4421 if (level_nr < leveldir_current->last_level)
4422 raise_level = TRUE; /* advance to next level */
4424 if ((hi_pos = NewHiScore()) >= 0)
4426 game_status = GAME_MODE_SCORES;
4428 DrawHallOfFame(hi_pos);
4438 FadeOut(REDRAW_FIELD);
4440 game_status = GAME_MODE_MAIN;
4448 DrawAndFadeInMainMenu(REDRAW_FIELD);
4457 LoadScore(level_nr);
4459 if (strEqual(setup.player_name, EMPTY_PLAYER_NAME) ||
4460 local_player->score_final < highscore[MAX_SCORE_ENTRIES - 1].Score)
4463 for (k = 0; k < MAX_SCORE_ENTRIES; k++)
4465 if (local_player->score_final > highscore[k].Score)
4467 /* player has made it to the hall of fame */
4469 if (k < MAX_SCORE_ENTRIES - 1)
4471 int m = MAX_SCORE_ENTRIES - 1;
4474 for (l = k; l < MAX_SCORE_ENTRIES; l++)
4475 if (strEqual(setup.player_name, highscore[l].Name))
4477 if (m == k) /* player's new highscore overwrites his old one */
4481 for (l = m; l > k; l--)
4483 strcpy(highscore[l].Name, highscore[l - 1].Name);
4484 highscore[l].Score = highscore[l - 1].Score;
4491 strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
4492 highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
4493 highscore[k].Score = local_player->score_final;
4499 else if (!strncmp(setup.player_name, highscore[k].Name,
4500 MAX_PLAYER_NAME_LEN))
4501 break; /* player already there with a higher score */
4507 SaveScore(level_nr);
4512 inline static int getElementMoveStepsizeExt(int x, int y, int direction)
4514 int element = Feld[x][y];
4515 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
4516 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
4517 int horiz_move = (dx != 0);
4518 int sign = (horiz_move ? dx : dy);
4519 int step = sign * element_info[element].move_stepsize;
4521 /* special values for move stepsize for spring and things on conveyor belt */
4524 if (CAN_FALL(element) &&
4525 y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
4526 step = sign * MOVE_STEPSIZE_NORMAL / 2;
4527 else if (element == EL_SPRING)
4528 step = sign * MOVE_STEPSIZE_NORMAL * 2;
4534 inline static int getElementMoveStepsize(int x, int y)
4536 return getElementMoveStepsizeExt(x, y, MovDir[x][y]);
4539 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
4541 if (player->GfxAction != action || player->GfxDir != dir)
4543 player->GfxAction = action;
4544 player->GfxDir = dir;
4546 player->StepFrame = 0;
4550 static void ResetGfxFrame(int x, int y, boolean redraw)
4552 int element = Feld[x][y];
4553 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
4554 int last_gfx_frame = GfxFrame[x][y];
4556 if (graphic_info[graphic].anim_global_sync)
4557 GfxFrame[x][y] = FrameCounter;
4558 else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
4559 GfxFrame[x][y] = CustomValue[x][y];
4560 else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
4561 GfxFrame[x][y] = element_info[element].collect_score;
4562 else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
4563 GfxFrame[x][y] = ChangeDelay[x][y];
4565 if (redraw && GfxFrame[x][y] != last_gfx_frame)
4566 DrawLevelGraphicAnimation(x, y, graphic);
4569 static void ResetGfxAnimation(int x, int y)
4571 GfxAction[x][y] = ACTION_DEFAULT;
4572 GfxDir[x][y] = MovDir[x][y];
4575 ResetGfxFrame(x, y, FALSE);
4578 static void ResetRandomAnimationValue(int x, int y)
4580 GfxRandom[x][y] = INIT_GFX_RANDOM();
4583 void InitMovingField(int x, int y, int direction)
4585 int element = Feld[x][y];
4586 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
4587 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
4590 boolean is_moving_before, is_moving_after;
4592 /* check if element was/is moving or being moved before/after mode change */
4593 is_moving_before = (WasJustMoving[x][y] != 0);
4594 is_moving_after = (getElementMoveStepsizeExt(x, y, direction) != 0);
4596 /* reset animation only for moving elements which change direction of moving
4597 or which just started or stopped moving
4598 (else CEs with property "can move" / "not moving" are reset each frame) */
4599 if (is_moving_before != is_moving_after ||
4600 direction != MovDir[x][y])
4601 ResetGfxAnimation(x, y);
4603 MovDir[x][y] = direction;
4604 GfxDir[x][y] = direction;
4606 GfxAction[x][y] = (!is_moving_after ? ACTION_WAITING :
4607 direction == MV_DOWN && CAN_FALL(element) ?
4608 ACTION_FALLING : ACTION_MOVING);
4610 /* this is needed for CEs with property "can move" / "not moving" */
4612 if (is_moving_after)
4614 if (Feld[newx][newy] == EL_EMPTY)
4615 Feld[newx][newy] = EL_BLOCKED;
4617 MovDir[newx][newy] = MovDir[x][y];
4619 CustomValue[newx][newy] = CustomValue[x][y];
4621 GfxFrame[newx][newy] = GfxFrame[x][y];
4622 GfxRandom[newx][newy] = GfxRandom[x][y];
4623 GfxAction[newx][newy] = GfxAction[x][y];
4624 GfxDir[newx][newy] = GfxDir[x][y];
4628 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
4630 int direction = MovDir[x][y];
4631 int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0);
4632 int newy = y + (direction & MV_UP ? -1 : direction & MV_DOWN ? +1 : 0);
4638 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
4640 int oldx = x, oldy = y;
4641 int direction = MovDir[x][y];
4643 if (direction == MV_LEFT)
4645 else if (direction == MV_RIGHT)
4647 else if (direction == MV_UP)
4649 else if (direction == MV_DOWN)
4652 *comes_from_x = oldx;
4653 *comes_from_y = oldy;
4656 int MovingOrBlocked2Element(int x, int y)
4658 int element = Feld[x][y];
4660 if (element == EL_BLOCKED)
4664 Blocked2Moving(x, y, &oldx, &oldy);
4665 return Feld[oldx][oldy];
4671 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
4673 /* like MovingOrBlocked2Element(), but if element is moving
4674 and (x,y) is the field the moving element is just leaving,
4675 return EL_BLOCKED instead of the element value */
4676 int element = Feld[x][y];
4678 if (IS_MOVING(x, y))
4680 if (element == EL_BLOCKED)
4684 Blocked2Moving(x, y, &oldx, &oldy);
4685 return Feld[oldx][oldy];
4694 static void RemoveField(int x, int y)
4696 Feld[x][y] = EL_EMPTY;
4702 CustomValue[x][y] = 0;
4705 ChangeDelay[x][y] = 0;
4706 ChangePage[x][y] = -1;
4707 Pushed[x][y] = FALSE;
4709 GfxElement[x][y] = EL_UNDEFINED;
4710 GfxAction[x][y] = ACTION_DEFAULT;
4711 GfxDir[x][y] = MV_NONE;
4714 void RemoveMovingField(int x, int y)
4716 int oldx = x, oldy = y, newx = x, newy = y;
4717 int element = Feld[x][y];
4718 int next_element = EL_UNDEFINED;
4720 if (element != EL_BLOCKED && !IS_MOVING(x, y))
4723 if (IS_MOVING(x, y))
4725 Moving2Blocked(x, y, &newx, &newy);
4727 if (Feld[newx][newy] != EL_BLOCKED)
4729 /* element is moving, but target field is not free (blocked), but
4730 already occupied by something different (example: acid pool);
4731 in this case, only remove the moving field, but not the target */
4733 RemoveField(oldx, oldy);
4735 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
4737 TEST_DrawLevelField(oldx, oldy);
4742 else if (element == EL_BLOCKED)
4744 Blocked2Moving(x, y, &oldx, &oldy);
4745 if (!IS_MOVING(oldx, oldy))
4749 if (element == EL_BLOCKED &&
4750 (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
4751 Feld[oldx][oldy] == EL_QUICKSAND_FAST_EMPTYING ||
4752 Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
4753 Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
4754 Feld[oldx][oldy] == EL_DC_MAGIC_WALL_EMPTYING ||
4755 Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
4756 next_element = get_next_element(Feld[oldx][oldy]);
4758 RemoveField(oldx, oldy);
4759 RemoveField(newx, newy);
4761 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
4763 if (next_element != EL_UNDEFINED)
4764 Feld[oldx][oldy] = next_element;
4766 TEST_DrawLevelField(oldx, oldy);
4767 TEST_DrawLevelField(newx, newy);
4770 void DrawDynamite(int x, int y)
4772 int sx = SCREENX(x), sy = SCREENY(y);
4773 int graphic = el2img(Feld[x][y]);
4776 if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
4779 if (IS_WALKABLE_INSIDE(Back[x][y]))
4783 DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
4784 else if (Store[x][y])
4785 DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
4787 frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
4789 if (Back[x][y] || Store[x][y])
4790 DrawGraphicThruMask(sx, sy, graphic, frame);
4792 DrawGraphic(sx, sy, graphic, frame);
4795 void CheckDynamite(int x, int y)
4797 if (MovDelay[x][y] != 0) /* dynamite is still waiting to explode */
4801 if (MovDelay[x][y] != 0)
4804 PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
4810 StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
4815 static void setMinimalPlayerBoundaries(int *sx1, int *sy1, int *sx2, int *sy2)
4817 boolean num_checked_players = 0;
4820 for (i = 0; i < MAX_PLAYERS; i++)
4822 if (stored_player[i].active)
4824 int sx = stored_player[i].jx;
4825 int sy = stored_player[i].jy;
4827 if (num_checked_players == 0)
4834 *sx1 = MIN(*sx1, sx);
4835 *sy1 = MIN(*sy1, sy);
4836 *sx2 = MAX(*sx2, sx);
4837 *sy2 = MAX(*sy2, sy);
4840 num_checked_players++;
4845 static boolean checkIfAllPlayersFitToScreen_RND()
4847 int sx1 = 0, sy1 = 0, sx2 = 0, sy2 = 0;
4849 setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
4851 return (sx2 - sx1 < SCR_FIELDX &&
4852 sy2 - sy1 < SCR_FIELDY);
4855 static void setScreenCenteredToAllPlayers(int *sx, int *sy)
4857 int sx1 = scroll_x, sy1 = scroll_y, sx2 = scroll_x, sy2 = scroll_y;
4859 setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
4861 *sx = (sx1 + sx2) / 2;
4862 *sy = (sy1 + sy2) / 2;
4865 void DrawRelocateScreen(int old_x, int old_y, int x, int y, int move_dir,
4866 boolean center_screen, boolean quick_relocation)
4868 boolean ffwd_delay = (tape.playing && tape.fast_forward);
4869 boolean no_delay = (tape.warp_forward);
4870 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
4871 int wait_delay_value = (no_delay ? 0 : frame_delay_value);
4873 if (quick_relocation)
4875 if (!IN_VIS_FIELD(SCREENX(x), SCREENY(y)) || center_screen)
4877 if (!level.shifted_relocation || center_screen)
4879 /* quick relocation (without scrolling), with centering of screen */
4881 scroll_x = (x < SBX_Left + MIDPOSX ? SBX_Left :
4882 x > SBX_Right + MIDPOSX ? SBX_Right :
4885 scroll_y = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
4886 y > SBY_Lower + MIDPOSY ? SBY_Lower :
4891 /* quick relocation (without scrolling), but do not center screen */
4893 int center_scroll_x = (old_x < SBX_Left + MIDPOSX ? SBX_Left :
4894 old_x > SBX_Right + MIDPOSX ? SBX_Right :
4897 int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
4898 old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
4901 int offset_x = x + (scroll_x - center_scroll_x);
4902 int offset_y = y + (scroll_y - center_scroll_y);
4904 scroll_x = (offset_x < SBX_Left + MIDPOSX ? SBX_Left :
4905 offset_x > SBX_Right + MIDPOSX ? SBX_Right :
4906 offset_x - MIDPOSX);
4908 scroll_y = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
4909 offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
4910 offset_y - MIDPOSY);
4915 if (!level.shifted_relocation || center_screen)
4917 /* quick relocation (without scrolling), with centering of screen */
4919 scroll_x = (x < SBX_Left + MIDPOSX ? SBX_Left :
4920 x > SBX_Right + MIDPOSX ? SBX_Right :
4923 scroll_y = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
4924 y > SBY_Lower + MIDPOSY ? SBY_Lower :
4929 /* quick relocation (without scrolling), but do not center screen */
4931 int center_scroll_x = (old_x < SBX_Left + MIDPOSX ? SBX_Left :
4932 old_x > SBX_Right + MIDPOSX ? SBX_Right :
4935 int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
4936 old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
4939 int offset_x = x + (scroll_x - center_scroll_x);
4940 int offset_y = y + (scroll_y - center_scroll_y);
4942 scroll_x = (offset_x < SBX_Left + MIDPOSX ? SBX_Left :
4943 offset_x > SBX_Right + MIDPOSX ? SBX_Right :
4944 offset_x - MIDPOSX);
4946 scroll_y = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
4947 offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
4948 offset_y - MIDPOSY);
4952 RedrawPlayfield(TRUE, 0,0,0,0);
4956 int scroll_xx, scroll_yy;
4958 if (!level.shifted_relocation || center_screen)
4960 /* visible relocation (with scrolling), with centering of screen */
4962 scroll_xx = (x < SBX_Left + MIDPOSX ? SBX_Left :
4963 x > SBX_Right + MIDPOSX ? SBX_Right :
4966 scroll_yy = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
4967 y > SBY_Lower + MIDPOSY ? SBY_Lower :
4972 /* visible relocation (with scrolling), but do not center screen */
4974 int center_scroll_x = (old_x < SBX_Left + MIDPOSX ? SBX_Left :
4975 old_x > SBX_Right + MIDPOSX ? SBX_Right :
4978 int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
4979 old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
4982 int offset_x = x + (scroll_x - center_scroll_x);
4983 int offset_y = y + (scroll_y - center_scroll_y);
4985 scroll_xx = (offset_x < SBX_Left + MIDPOSX ? SBX_Left :
4986 offset_x > SBX_Right + MIDPOSX ? SBX_Right :
4987 offset_x - MIDPOSX);
4989 scroll_yy = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
4990 offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
4991 offset_y - MIDPOSY);
4995 ScrollScreen(NULL, SCROLL_GO_ON); /* scroll last frame to full tile */
4997 while (scroll_x != scroll_xx || scroll_y != scroll_yy)
5000 int fx = FX, fy = FY;
5002 dx = (scroll_xx < scroll_x ? +1 : scroll_xx > scroll_x ? -1 : 0);
5003 dy = (scroll_yy < scroll_y ? +1 : scroll_yy > scroll_y ? -1 : 0);
5005 if (dx == 0 && dy == 0) /* no scrolling needed at all */
5011 fx += dx * TILEX / 2;
5012 fy += dy * TILEY / 2;
5014 ScrollLevel(dx, dy);
5017 /* scroll in two steps of half tile size to make things smoother */
5018 BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
5019 Delay(wait_delay_value);
5021 /* scroll second step to align at full tile size */
5023 Delay(wait_delay_value);
5028 Delay(wait_delay_value);
5032 void RelocatePlayer(int jx, int jy, int el_player_raw)
5034 int el_player = GET_PLAYER_ELEMENT(el_player_raw);
5035 int player_nr = GET_PLAYER_NR(el_player);
5036 struct PlayerInfo *player = &stored_player[player_nr];
5037 boolean ffwd_delay = (tape.playing && tape.fast_forward);
5038 boolean no_delay = (tape.warp_forward);
5039 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5040 int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5041 int old_jx = player->jx;
5042 int old_jy = player->jy;
5043 int old_element = Feld[old_jx][old_jy];
5044 int element = Feld[jx][jy];
5045 boolean player_relocated = (old_jx != jx || old_jy != jy);
5047 int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
5048 int move_dir_vert = (jy < old_jy ? MV_UP : jy > old_jy ? MV_DOWN : 0);
5049 int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
5050 int enter_side_vert = MV_DIR_OPPOSITE(move_dir_vert);
5051 int leave_side_horiz = move_dir_horiz;
5052 int leave_side_vert = move_dir_vert;
5053 int enter_side = enter_side_horiz | enter_side_vert;
5054 int leave_side = leave_side_horiz | leave_side_vert;
5056 if (player->GameOver) /* do not reanimate dead player */
5059 if (!player_relocated) /* no need to relocate the player */
5062 if (IS_PLAYER(jx, jy)) /* player already placed at new position */
5064 RemoveField(jx, jy); /* temporarily remove newly placed player */
5065 DrawLevelField(jx, jy);
5068 if (player->present)
5070 while (player->MovPos)
5072 ScrollPlayer(player, SCROLL_GO_ON);
5073 ScrollScreen(NULL, SCROLL_GO_ON);
5075 AdvanceFrameAndPlayerCounters(player->index_nr);
5080 Delay(wait_delay_value);
5083 DrawPlayer(player); /* needed here only to cleanup last field */
5084 DrawLevelField(player->jx, player->jy); /* remove player graphic */
5086 player->is_moving = FALSE;
5089 if (IS_CUSTOM_ELEMENT(old_element))
5090 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
5092 player->index_bit, leave_side);
5094 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
5096 player->index_bit, leave_side);
5098 Feld[jx][jy] = el_player;
5099 InitPlayerField(jx, jy, el_player, TRUE);
5101 /* "InitPlayerField()" above sets Feld[jx][jy] to EL_EMPTY, but it may be
5102 possible that the relocation target field did not contain a player element,
5103 but a walkable element, to which the new player was relocated -- in this
5104 case, restore that (already initialized!) element on the player field */
5105 if (!ELEM_IS_PLAYER(element)) /* player may be set on walkable element */
5107 Feld[jx][jy] = element; /* restore previously existing element */
5110 /* only visually relocate centered player */
5111 DrawRelocateScreen(old_jx, old_jy, player->jx, player->jy, player->MovDir,
5112 FALSE, level.instant_relocation);
5114 TestIfPlayerTouchesBadThing(jx, jy);
5115 TestIfPlayerTouchesCustomElement(jx, jy);
5117 if (IS_CUSTOM_ELEMENT(element))
5118 CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
5119 player->index_bit, enter_side);
5121 CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
5122 player->index_bit, enter_side);
5124 if (player->is_switching)
5126 /* ensure that relocation while still switching an element does not cause
5127 a new element to be treated as also switched directly after relocation
5128 (this is important for teleporter switches that teleport the player to
5129 a place where another teleporter switch is in the same direction, which
5130 would then incorrectly be treated as immediately switched before the
5131 direction key that caused the switch was released) */
5133 player->switch_x += jx - old_jx;
5134 player->switch_y += jy - old_jy;
5138 void Explode(int ex, int ey, int phase, int mode)
5144 /* !!! eliminate this variable !!! */
5145 int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
5147 if (game.explosions_delayed)
5149 ExplodeField[ex][ey] = mode;
5153 if (phase == EX_PHASE_START) /* initialize 'Store[][]' field */
5155 int center_element = Feld[ex][ey];
5156 int artwork_element, explosion_element; /* set these values later */
5158 /* remove things displayed in background while burning dynamite */
5159 if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
5162 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
5164 /* put moving element to center field (and let it explode there) */
5165 center_element = MovingOrBlocked2Element(ex, ey);
5166 RemoveMovingField(ex, ey);
5167 Feld[ex][ey] = center_element;
5170 /* now "center_element" is finally determined -- set related values now */
5171 artwork_element = center_element; /* for custom player artwork */
5172 explosion_element = center_element; /* for custom player artwork */
5174 if (IS_PLAYER(ex, ey))
5176 int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
5178 artwork_element = stored_player[player_nr].artwork_element;
5180 if (level.use_explosion_element[player_nr])
5182 explosion_element = level.explosion_element[player_nr];
5183 artwork_element = explosion_element;
5187 if (mode == EX_TYPE_NORMAL ||
5188 mode == EX_TYPE_CENTER ||
5189 mode == EX_TYPE_CROSS)
5190 PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
5192 last_phase = element_info[explosion_element].explosion_delay + 1;
5194 for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
5196 int xx = x - ex + 1;
5197 int yy = y - ey + 1;
5200 if (!IN_LEV_FIELD(x, y) ||
5201 (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
5202 (mode == EX_TYPE_CROSS && (x != ex && y != ey)))
5205 element = Feld[x][y];
5207 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
5209 element = MovingOrBlocked2Element(x, y);
5211 if (!IS_EXPLOSION_PROOF(element))
5212 RemoveMovingField(x, y);
5215 /* indestructible elements can only explode in center (but not flames) */
5216 if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
5217 mode == EX_TYPE_BORDER)) ||
5218 element == EL_FLAMES)
5221 /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
5222 behaviour, for example when touching a yamyam that explodes to rocks
5223 with active deadly shield, a rock is created under the player !!! */
5224 /* (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8) */
5226 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
5227 (game.engine_version < VERSION_IDENT(3,1,0,0) ||
5228 (x == ex && y == ey && mode != EX_TYPE_BORDER)))
5230 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
5233 if (IS_ACTIVE_BOMB(element))
5235 /* re-activate things under the bomb like gate or penguin */
5236 Feld[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
5243 /* save walkable background elements while explosion on same tile */
5244 if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
5245 (x != ex || y != ey || mode == EX_TYPE_BORDER))
5246 Back[x][y] = element;
5248 /* ignite explodable elements reached by other explosion */
5249 if (element == EL_EXPLOSION)
5250 element = Store2[x][y];
5252 if (AmoebaNr[x][y] &&
5253 (element == EL_AMOEBA_FULL ||
5254 element == EL_BD_AMOEBA ||
5255 element == EL_AMOEBA_GROWING))
5257 AmoebaCnt[AmoebaNr[x][y]]--;
5258 AmoebaCnt2[AmoebaNr[x][y]]--;
5263 if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
5265 int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
5267 Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
5269 if (PLAYERINFO(ex, ey)->use_murphy)
5270 Store[x][y] = EL_EMPTY;
5273 /* !!! check this case -- currently needed for rnd_rado_negundo_v,
5274 !!! levels 015 018 019 020 021 022 023 026 027 028 !!! */
5275 else if (ELEM_IS_PLAYER(center_element))
5276 Store[x][y] = EL_EMPTY;
5277 else if (center_element == EL_YAMYAM)
5278 Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
5279 else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
5280 Store[x][y] = element_info[center_element].content.e[xx][yy];
5282 /* needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
5283 (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
5284 otherwise) -- FIX THIS !!! */
5285 else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
5286 Store[x][y] = element_info[element].content.e[1][1];
5288 else if (!CAN_EXPLODE(element))
5289 Store[x][y] = element_info[element].content.e[1][1];
5292 Store[x][y] = EL_EMPTY;
5294 if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
5295 center_element == EL_AMOEBA_TO_DIAMOND)
5296 Store2[x][y] = element;
5298 Feld[x][y] = EL_EXPLOSION;
5299 GfxElement[x][y] = artwork_element;
5301 ExplodePhase[x][y] = 1;
5302 ExplodeDelay[x][y] = last_phase;
5307 if (center_element == EL_YAMYAM)
5308 game.yamyam_content_nr =
5309 (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
5321 GfxFrame[x][y] = 0; /* restart explosion animation */
5323 last_phase = ExplodeDelay[x][y];
5325 ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
5327 /* this can happen if the player leaves an explosion just in time */
5328 if (GfxElement[x][y] == EL_UNDEFINED)
5329 GfxElement[x][y] = EL_EMPTY;
5331 border_element = Store2[x][y];
5332 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5333 border_element = StorePlayer[x][y];
5335 if (phase == element_info[border_element].ignition_delay ||
5336 phase == last_phase)
5338 boolean border_explosion = FALSE;
5340 if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
5341 !PLAYER_EXPLOSION_PROTECTED(x, y))
5343 KillPlayerUnlessExplosionProtected(x, y);
5344 border_explosion = TRUE;
5346 else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
5348 Feld[x][y] = Store2[x][y];
5351 border_explosion = TRUE;
5353 else if (border_element == EL_AMOEBA_TO_DIAMOND)
5355 AmoebeUmwandeln(x, y);
5357 border_explosion = TRUE;
5360 /* if an element just explodes due to another explosion (chain-reaction),
5361 do not immediately end the new explosion when it was the last frame of
5362 the explosion (as it would be done in the following "if"-statement!) */
5363 if (border_explosion && phase == last_phase)
5367 if (phase == last_phase)
5371 element = Feld[x][y] = Store[x][y];
5372 Store[x][y] = Store2[x][y] = 0;
5373 GfxElement[x][y] = EL_UNDEFINED;
5375 /* player can escape from explosions and might therefore be still alive */
5376 if (element >= EL_PLAYER_IS_EXPLODING_1 &&
5377 element <= EL_PLAYER_IS_EXPLODING_4)
5379 int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
5380 int explosion_element = EL_PLAYER_1 + player_nr;
5381 int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
5382 int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
5384 if (level.use_explosion_element[player_nr])
5385 explosion_element = level.explosion_element[player_nr];
5387 Feld[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
5388 element_info[explosion_element].content.e[xx][yy]);
5391 /* restore probably existing indestructible background element */
5392 if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
5393 element = Feld[x][y] = Back[x][y];
5396 MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
5397 GfxDir[x][y] = MV_NONE;
5398 ChangeDelay[x][y] = 0;
5399 ChangePage[x][y] = -1;
5401 CustomValue[x][y] = 0;
5403 InitField_WithBug2(x, y, FALSE);
5405 TEST_DrawLevelField(x, y);
5407 TestIfElementTouchesCustomElement(x, y);
5409 if (GFX_CRUMBLED(element))
5410 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
5412 if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
5413 StorePlayer[x][y] = 0;
5415 if (ELEM_IS_PLAYER(element))
5416 RelocatePlayer(x, y, element);
5418 else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
5420 int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
5421 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5424 TEST_DrawLevelFieldCrumbled(x, y);
5426 if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
5428 DrawLevelElement(x, y, Back[x][y]);
5429 DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
5431 else if (IS_WALKABLE_UNDER(Back[x][y]))
5433 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5434 DrawLevelElementThruMask(x, y, Back[x][y]);
5436 else if (!IS_WALKABLE_INSIDE(Back[x][y]))
5437 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5441 void DynaExplode(int ex, int ey)
5444 int dynabomb_element = Feld[ex][ey];
5445 int dynabomb_size = 1;
5446 boolean dynabomb_xl = FALSE;
5447 struct PlayerInfo *player;
5448 static int xy[4][2] =
5456 if (IS_ACTIVE_BOMB(dynabomb_element))
5458 player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
5459 dynabomb_size = player->dynabomb_size;
5460 dynabomb_xl = player->dynabomb_xl;
5461 player->dynabombs_left++;
5464 Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
5466 for (i = 0; i < NUM_DIRECTIONS; i++)
5468 for (j = 1; j <= dynabomb_size; j++)
5470 int x = ex + j * xy[i][0];
5471 int y = ey + j * xy[i][1];
5474 if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
5477 element = Feld[x][y];
5479 /* do not restart explosions of fields with active bombs */
5480 if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
5483 Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
5485 if (element != EL_EMPTY && element != EL_EXPLOSION &&
5486 !IS_DIGGABLE(element) && !dynabomb_xl)
5492 void Bang(int x, int y)
5494 int element = MovingOrBlocked2Element(x, y);
5495 int explosion_type = EX_TYPE_NORMAL;
5497 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5499 struct PlayerInfo *player = PLAYERINFO(x, y);
5501 element = Feld[x][y] = player->initial_element;
5503 if (level.use_explosion_element[player->index_nr])
5505 int explosion_element = level.explosion_element[player->index_nr];
5507 if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
5508 explosion_type = EX_TYPE_CROSS;
5509 else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
5510 explosion_type = EX_TYPE_CENTER;
5518 case EL_BD_BUTTERFLY:
5521 case EL_DARK_YAMYAM:
5525 RaiseScoreElement(element);
5528 case EL_DYNABOMB_PLAYER_1_ACTIVE:
5529 case EL_DYNABOMB_PLAYER_2_ACTIVE:
5530 case EL_DYNABOMB_PLAYER_3_ACTIVE:
5531 case EL_DYNABOMB_PLAYER_4_ACTIVE:
5532 case EL_DYNABOMB_INCREASE_NUMBER:
5533 case EL_DYNABOMB_INCREASE_SIZE:
5534 case EL_DYNABOMB_INCREASE_POWER:
5535 explosion_type = EX_TYPE_DYNA;
5538 case EL_DC_LANDMINE:
5539 explosion_type = EX_TYPE_CENTER;
5544 case EL_LAMP_ACTIVE:
5545 case EL_AMOEBA_TO_DIAMOND:
5546 if (!IS_PLAYER(x, y)) /* penguin and player may be at same field */
5547 explosion_type = EX_TYPE_CENTER;
5551 if (element_info[element].explosion_type == EXPLODES_CROSS)
5552 explosion_type = EX_TYPE_CROSS;
5553 else if (element_info[element].explosion_type == EXPLODES_1X1)
5554 explosion_type = EX_TYPE_CENTER;
5558 if (explosion_type == EX_TYPE_DYNA)
5561 Explode(x, y, EX_PHASE_START, explosion_type);
5563 CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
5566 void SplashAcid(int x, int y)
5568 if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
5569 (!IN_LEV_FIELD(x - 1, y - 2) ||
5570 !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
5571 Feld[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
5573 if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
5574 (!IN_LEV_FIELD(x + 1, y - 2) ||
5575 !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
5576 Feld[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
5578 PlayLevelSound(x, y, SND_ACID_SPLASHING);
5581 static void InitBeltMovement()
5583 static int belt_base_element[4] =
5585 EL_CONVEYOR_BELT_1_LEFT,
5586 EL_CONVEYOR_BELT_2_LEFT,
5587 EL_CONVEYOR_BELT_3_LEFT,
5588 EL_CONVEYOR_BELT_4_LEFT
5590 static int belt_base_active_element[4] =
5592 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
5593 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
5594 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
5595 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
5600 /* set frame order for belt animation graphic according to belt direction */
5601 for (i = 0; i < NUM_BELTS; i++)
5605 for (j = 0; j < NUM_BELT_PARTS; j++)
5607 int element = belt_base_active_element[belt_nr] + j;
5608 int graphic_1 = el2img(element);
5609 int graphic_2 = el2panelimg(element);
5611 if (game.belt_dir[i] == MV_LEFT)
5613 graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
5614 graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
5618 graphic_info[graphic_1].anim_mode |= ANIM_REVERSE;
5619 graphic_info[graphic_2].anim_mode |= ANIM_REVERSE;
5624 SCAN_PLAYFIELD(x, y)
5626 int element = Feld[x][y];
5628 for (i = 0; i < NUM_BELTS; i++)
5630 if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
5632 int e_belt_nr = getBeltNrFromBeltElement(element);
5635 if (e_belt_nr == belt_nr)
5637 int belt_part = Feld[x][y] - belt_base_element[belt_nr];
5639 Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
5646 static void ToggleBeltSwitch(int x, int y)
5648 static int belt_base_element[4] =
5650 EL_CONVEYOR_BELT_1_LEFT,
5651 EL_CONVEYOR_BELT_2_LEFT,
5652 EL_CONVEYOR_BELT_3_LEFT,
5653 EL_CONVEYOR_BELT_4_LEFT
5655 static int belt_base_active_element[4] =
5657 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
5658 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
5659 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
5660 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
5662 static int belt_base_switch_element[4] =
5664 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
5665 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
5666 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
5667 EL_CONVEYOR_BELT_4_SWITCH_LEFT
5669 static int belt_move_dir[4] =
5677 int element = Feld[x][y];
5678 int belt_nr = getBeltNrFromBeltSwitchElement(element);
5679 int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
5680 int belt_dir = belt_move_dir[belt_dir_nr];
5683 if (!IS_BELT_SWITCH(element))
5686 game.belt_dir_nr[belt_nr] = belt_dir_nr;
5687 game.belt_dir[belt_nr] = belt_dir;
5689 if (belt_dir_nr == 3)
5692 /* set frame order for belt animation graphic according to belt direction */
5693 for (i = 0; i < NUM_BELT_PARTS; i++)
5695 int element = belt_base_active_element[belt_nr] + i;
5696 int graphic_1 = el2img(element);
5697 int graphic_2 = el2panelimg(element);
5699 if (belt_dir == MV_LEFT)
5701 graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
5702 graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
5706 graphic_info[graphic_1].anim_mode |= ANIM_REVERSE;
5707 graphic_info[graphic_2].anim_mode |= ANIM_REVERSE;
5711 SCAN_PLAYFIELD(xx, yy)
5713 int element = Feld[xx][yy];
5715 if (IS_BELT_SWITCH(element))
5717 int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
5719 if (e_belt_nr == belt_nr)
5721 Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
5722 TEST_DrawLevelField(xx, yy);
5725 else if (IS_BELT(element) && belt_dir != MV_NONE)
5727 int e_belt_nr = getBeltNrFromBeltElement(element);
5729 if (e_belt_nr == belt_nr)
5731 int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
5733 Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
5734 TEST_DrawLevelField(xx, yy);
5737 else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
5739 int e_belt_nr = getBeltNrFromBeltActiveElement(element);
5741 if (e_belt_nr == belt_nr)
5743 int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
5745 Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
5746 TEST_DrawLevelField(xx, yy);
5752 static void ToggleSwitchgateSwitch(int x, int y)
5756 game.switchgate_pos = !game.switchgate_pos;
5758 SCAN_PLAYFIELD(xx, yy)
5760 int element = Feld[xx][yy];
5762 if (element == EL_SWITCHGATE_SWITCH_UP)
5764 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_DOWN;
5765 TEST_DrawLevelField(xx, yy);
5767 else if (element == EL_SWITCHGATE_SWITCH_DOWN)
5769 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP;
5770 TEST_DrawLevelField(xx, yy);
5772 else if (element == EL_DC_SWITCHGATE_SWITCH_UP)
5774 Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_DOWN;
5775 TEST_DrawLevelField(xx, yy);
5777 else if (element == EL_DC_SWITCHGATE_SWITCH_DOWN)
5779 Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP;
5780 TEST_DrawLevelField(xx, yy);
5782 else if (element == EL_SWITCHGATE_OPEN ||
5783 element == EL_SWITCHGATE_OPENING)
5785 Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
5787 PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
5789 else if (element == EL_SWITCHGATE_CLOSED ||
5790 element == EL_SWITCHGATE_CLOSING)
5792 Feld[xx][yy] = EL_SWITCHGATE_OPENING;
5794 PlayLevelSoundAction(xx, yy, ACTION_OPENING);
5799 static int getInvisibleActiveFromInvisibleElement(int element)
5801 return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
5802 element == EL_INVISIBLE_WALL ? EL_INVISIBLE_WALL_ACTIVE :
5803 element == EL_INVISIBLE_SAND ? EL_INVISIBLE_SAND_ACTIVE :
5807 static int getInvisibleFromInvisibleActiveElement(int element)
5809 return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
5810 element == EL_INVISIBLE_WALL_ACTIVE ? EL_INVISIBLE_WALL :
5811 element == EL_INVISIBLE_SAND_ACTIVE ? EL_INVISIBLE_SAND :
5815 static void RedrawAllLightSwitchesAndInvisibleElements()
5819 SCAN_PLAYFIELD(x, y)
5821 int element = Feld[x][y];
5823 if (element == EL_LIGHT_SWITCH &&
5824 game.light_time_left > 0)
5826 Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
5827 TEST_DrawLevelField(x, y);
5829 else if (element == EL_LIGHT_SWITCH_ACTIVE &&
5830 game.light_time_left == 0)
5832 Feld[x][y] = EL_LIGHT_SWITCH;
5833 TEST_DrawLevelField(x, y);
5835 else if (element == EL_EMC_DRIPPER &&
5836 game.light_time_left > 0)
5838 Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
5839 TEST_DrawLevelField(x, y);
5841 else if (element == EL_EMC_DRIPPER_ACTIVE &&
5842 game.light_time_left == 0)
5844 Feld[x][y] = EL_EMC_DRIPPER;
5845 TEST_DrawLevelField(x, y);
5847 else if (element == EL_INVISIBLE_STEELWALL ||
5848 element == EL_INVISIBLE_WALL ||
5849 element == EL_INVISIBLE_SAND)
5851 if (game.light_time_left > 0)
5852 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
5854 TEST_DrawLevelField(x, y);
5856 /* uncrumble neighbour fields, if needed */
5857 if (element == EL_INVISIBLE_SAND)
5858 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
5860 else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
5861 element == EL_INVISIBLE_WALL_ACTIVE ||
5862 element == EL_INVISIBLE_SAND_ACTIVE)
5864 if (game.light_time_left == 0)
5865 Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
5867 TEST_DrawLevelField(x, y);
5869 /* re-crumble neighbour fields, if needed */
5870 if (element == EL_INVISIBLE_SAND)
5871 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
5876 static void RedrawAllInvisibleElementsForLenses()
5880 SCAN_PLAYFIELD(x, y)
5882 int element = Feld[x][y];
5884 if (element == EL_EMC_DRIPPER &&
5885 game.lenses_time_left > 0)
5887 Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
5888 TEST_DrawLevelField(x, y);
5890 else if (element == EL_EMC_DRIPPER_ACTIVE &&
5891 game.lenses_time_left == 0)
5893 Feld[x][y] = EL_EMC_DRIPPER;
5894 TEST_DrawLevelField(x, y);
5896 else if (element == EL_INVISIBLE_STEELWALL ||
5897 element == EL_INVISIBLE_WALL ||
5898 element == EL_INVISIBLE_SAND)
5900 if (game.lenses_time_left > 0)
5901 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
5903 TEST_DrawLevelField(x, y);
5905 /* uncrumble neighbour fields, if needed */
5906 if (element == EL_INVISIBLE_SAND)
5907 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
5909 else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
5910 element == EL_INVISIBLE_WALL_ACTIVE ||
5911 element == EL_INVISIBLE_SAND_ACTIVE)
5913 if (game.lenses_time_left == 0)
5914 Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
5916 TEST_DrawLevelField(x, y);
5918 /* re-crumble neighbour fields, if needed */
5919 if (element == EL_INVISIBLE_SAND)
5920 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
5925 static void RedrawAllInvisibleElementsForMagnifier()
5929 SCAN_PLAYFIELD(x, y)
5931 int element = Feld[x][y];
5933 if (element == EL_EMC_FAKE_GRASS &&
5934 game.magnify_time_left > 0)
5936 Feld[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
5937 TEST_DrawLevelField(x, y);
5939 else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
5940 game.magnify_time_left == 0)
5942 Feld[x][y] = EL_EMC_FAKE_GRASS;
5943 TEST_DrawLevelField(x, y);
5945 else if (IS_GATE_GRAY(element) &&
5946 game.magnify_time_left > 0)
5948 Feld[x][y] = (IS_RND_GATE_GRAY(element) ?
5949 element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
5950 IS_EM_GATE_GRAY(element) ?
5951 element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
5952 IS_EMC_GATE_GRAY(element) ?
5953 element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
5954 IS_DC_GATE_GRAY(element) ?
5955 EL_DC_GATE_WHITE_GRAY_ACTIVE :
5957 TEST_DrawLevelField(x, y);
5959 else if (IS_GATE_GRAY_ACTIVE(element) &&
5960 game.magnify_time_left == 0)
5962 Feld[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
5963 element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
5964 IS_EM_GATE_GRAY_ACTIVE(element) ?
5965 element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
5966 IS_EMC_GATE_GRAY_ACTIVE(element) ?
5967 element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
5968 IS_DC_GATE_GRAY_ACTIVE(element) ?
5969 EL_DC_GATE_WHITE_GRAY :
5971 TEST_DrawLevelField(x, y);
5976 static void ToggleLightSwitch(int x, int y)
5978 int element = Feld[x][y];
5980 game.light_time_left =
5981 (element == EL_LIGHT_SWITCH ?
5982 level.time_light * FRAMES_PER_SECOND : 0);
5984 RedrawAllLightSwitchesAndInvisibleElements();
5987 static void ActivateTimegateSwitch(int x, int y)
5991 game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
5993 SCAN_PLAYFIELD(xx, yy)
5995 int element = Feld[xx][yy];
5997 if (element == EL_TIMEGATE_CLOSED ||
5998 element == EL_TIMEGATE_CLOSING)
6000 Feld[xx][yy] = EL_TIMEGATE_OPENING;
6001 PlayLevelSound(xx, yy, SND_CLASS_TIMEGATE_OPENING);
6005 else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
6007 Feld[xx][yy] = EL_TIMEGATE_SWITCH;
6008 TEST_DrawLevelField(xx, yy);
6014 Feld[x][y] = (Feld[x][y] == EL_TIMEGATE_SWITCH ? EL_TIMEGATE_SWITCH_ACTIVE :
6015 EL_DC_TIMEGATE_SWITCH_ACTIVE);
6018 void Impact(int x, int y)
6020 boolean last_line = (y == lev_fieldy - 1);
6021 boolean object_hit = FALSE;
6022 boolean impact = (last_line || object_hit);
6023 int element = Feld[x][y];
6024 int smashed = EL_STEELWALL;
6026 if (!last_line) /* check if element below was hit */
6028 if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
6031 object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
6032 MovDir[x][y + 1] != MV_DOWN ||
6033 MovPos[x][y + 1] <= TILEY / 2));
6035 /* do not smash moving elements that left the smashed field in time */
6036 if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
6037 ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
6040 #if USE_QUICKSAND_IMPACT_BUGFIX
6041 if (Feld[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
6043 RemoveMovingField(x, y + 1);
6044 Feld[x][y + 1] = EL_QUICKSAND_EMPTY;
6045 Feld[x][y + 2] = EL_ROCK;
6046 TEST_DrawLevelField(x, y + 2);
6051 if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTYING && object_hit == FALSE)
6053 RemoveMovingField(x, y + 1);
6054 Feld[x][y + 1] = EL_QUICKSAND_FAST_EMPTY;
6055 Feld[x][y + 2] = EL_ROCK;
6056 TEST_DrawLevelField(x, y + 2);
6063 smashed = MovingOrBlocked2Element(x, y + 1);
6065 impact = (last_line || object_hit);
6068 if (!last_line && smashed == EL_ACID) /* element falls into acid */
6070 SplashAcid(x, y + 1);
6074 /* !!! not sufficient for all cases -- see EL_PEARL below !!! */
6075 /* only reset graphic animation if graphic really changes after impact */
6077 el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
6079 ResetGfxAnimation(x, y);
6080 TEST_DrawLevelField(x, y);
6083 if (impact && CAN_EXPLODE_IMPACT(element))
6088 else if (impact && element == EL_PEARL &&
6089 smashed != EL_DC_MAGIC_WALL && smashed != EL_DC_MAGIC_WALL_ACTIVE)
6091 ResetGfxAnimation(x, y);
6093 Feld[x][y] = EL_PEARL_BREAKING;
6094 PlayLevelSound(x, y, SND_PEARL_BREAKING);
6097 else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
6099 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6104 if (impact && element == EL_AMOEBA_DROP)
6106 if (object_hit && IS_PLAYER(x, y + 1))
6107 KillPlayerUnlessEnemyProtected(x, y + 1);
6108 else if (object_hit && smashed == EL_PENGUIN)
6112 Feld[x][y] = EL_AMOEBA_GROWING;
6113 Store[x][y] = EL_AMOEBA_WET;
6115 ResetRandomAnimationValue(x, y);
6120 if (object_hit) /* check which object was hit */
6122 if ((CAN_PASS_MAGIC_WALL(element) &&
6123 (smashed == EL_MAGIC_WALL ||
6124 smashed == EL_BD_MAGIC_WALL)) ||
6125 (CAN_PASS_DC_MAGIC_WALL(element) &&
6126 smashed == EL_DC_MAGIC_WALL))
6129 int activated_magic_wall =
6130 (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
6131 smashed == EL_BD_MAGIC_WALL ? EL_BD_MAGIC_WALL_ACTIVE :
6132 EL_DC_MAGIC_WALL_ACTIVE);
6134 /* activate magic wall / mill */
6135 SCAN_PLAYFIELD(xx, yy)
6137 if (Feld[xx][yy] == smashed)
6138 Feld[xx][yy] = activated_magic_wall;
6141 game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
6142 game.magic_wall_active = TRUE;
6144 PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
6145 SND_MAGIC_WALL_ACTIVATING :
6146 smashed == EL_BD_MAGIC_WALL ?
6147 SND_BD_MAGIC_WALL_ACTIVATING :
6148 SND_DC_MAGIC_WALL_ACTIVATING));
6151 if (IS_PLAYER(x, y + 1))
6153 if (CAN_SMASH_PLAYER(element))
6155 KillPlayerUnlessEnemyProtected(x, y + 1);
6159 else if (smashed == EL_PENGUIN)
6161 if (CAN_SMASH_PLAYER(element))
6167 else if (element == EL_BD_DIAMOND)
6169 if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
6175 else if (((element == EL_SP_INFOTRON ||
6176 element == EL_SP_ZONK) &&
6177 (smashed == EL_SP_SNIKSNAK ||
6178 smashed == EL_SP_ELECTRON ||
6179 smashed == EL_SP_DISK_ORANGE)) ||
6180 (element == EL_SP_INFOTRON &&
6181 smashed == EL_SP_DISK_YELLOW))
6186 else if (CAN_SMASH_EVERYTHING(element))
6188 if (IS_CLASSIC_ENEMY(smashed) ||
6189 CAN_EXPLODE_SMASHED(smashed))
6194 else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
6196 if (smashed == EL_LAMP ||
6197 smashed == EL_LAMP_ACTIVE)
6202 else if (smashed == EL_NUT)
6204 Feld[x][y + 1] = EL_NUT_BREAKING;
6205 PlayLevelSound(x, y, SND_NUT_BREAKING);
6206 RaiseScoreElement(EL_NUT);
6209 else if (smashed == EL_PEARL)
6211 ResetGfxAnimation(x, y);
6213 Feld[x][y + 1] = EL_PEARL_BREAKING;
6214 PlayLevelSound(x, y, SND_PEARL_BREAKING);
6217 else if (smashed == EL_DIAMOND)
6219 Feld[x][y + 1] = EL_DIAMOND_BREAKING;
6220 PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
6223 else if (IS_BELT_SWITCH(smashed))
6225 ToggleBeltSwitch(x, y + 1);
6227 else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
6228 smashed == EL_SWITCHGATE_SWITCH_DOWN ||
6229 smashed == EL_DC_SWITCHGATE_SWITCH_UP ||
6230 smashed == EL_DC_SWITCHGATE_SWITCH_DOWN)
6232 ToggleSwitchgateSwitch(x, y + 1);
6234 else if (smashed == EL_LIGHT_SWITCH ||
6235 smashed == EL_LIGHT_SWITCH_ACTIVE)
6237 ToggleLightSwitch(x, y + 1);
6241 CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6243 CheckElementChangeBySide(x, y + 1, smashed, element,
6244 CE_SWITCHED, CH_SIDE_TOP);
6245 CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
6251 CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6256 /* play sound of magic wall / mill */
6258 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
6259 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ||
6260 Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE))
6262 if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
6263 PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
6264 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
6265 PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
6266 else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
6267 PlayLevelSound(x, y, SND_DC_MAGIC_WALL_FILLING);
6272 /* play sound of object that hits the ground */
6273 if (last_line || object_hit)
6274 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6277 inline static void TurnRoundExt(int x, int y)
6289 { 0, 0 }, { 0, 0 }, { 0, 0 },
6294 int left, right, back;
6298 { MV_DOWN, MV_UP, MV_RIGHT },
6299 { MV_UP, MV_DOWN, MV_LEFT },
6301 { MV_LEFT, MV_RIGHT, MV_DOWN },
6305 { MV_RIGHT, MV_LEFT, MV_UP }
6308 int element = Feld[x][y];
6309 int move_pattern = element_info[element].move_pattern;
6311 int old_move_dir = MovDir[x][y];
6312 int left_dir = turn[old_move_dir].left;
6313 int right_dir = turn[old_move_dir].right;
6314 int back_dir = turn[old_move_dir].back;
6316 int left_dx = move_xy[left_dir].dx, left_dy = move_xy[left_dir].dy;
6317 int right_dx = move_xy[right_dir].dx, right_dy = move_xy[right_dir].dy;
6318 int move_dx = move_xy[old_move_dir].dx, move_dy = move_xy[old_move_dir].dy;
6319 int back_dx = move_xy[back_dir].dx, back_dy = move_xy[back_dir].dy;
6321 int left_x = x + left_dx, left_y = y + left_dy;
6322 int right_x = x + right_dx, right_y = y + right_dy;
6323 int move_x = x + move_dx, move_y = y + move_dy;
6327 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
6329 TestIfBadThingTouchesOtherBadThing(x, y);
6331 if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
6332 MovDir[x][y] = right_dir;
6333 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6334 MovDir[x][y] = left_dir;
6336 if (element == EL_BUG && MovDir[x][y] != old_move_dir)
6338 else if (element == EL_BD_BUTTERFLY) /* && MovDir[x][y] == left_dir) */
6341 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
6343 TestIfBadThingTouchesOtherBadThing(x, y);
6345 if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
6346 MovDir[x][y] = left_dir;
6347 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6348 MovDir[x][y] = right_dir;
6350 if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
6352 else if (element == EL_BD_FIREFLY) /* && MovDir[x][y] == right_dir) */
6355 else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
6357 TestIfBadThingTouchesOtherBadThing(x, y);
6359 if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
6360 MovDir[x][y] = left_dir;
6361 else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
6362 MovDir[x][y] = right_dir;
6364 if (MovDir[x][y] != old_move_dir)
6367 else if (element == EL_YAMYAM)
6369 boolean can_turn_left = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
6370 boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
6372 if (can_turn_left && can_turn_right)
6373 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6374 else if (can_turn_left)
6375 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6376 else if (can_turn_right)
6377 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6379 MovDir[x][y] = back_dir;
6381 MovDelay[x][y] = 16 + 16 * RND(3);
6383 else if (element == EL_DARK_YAMYAM)
6385 boolean can_turn_left = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6387 boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6390 if (can_turn_left && can_turn_right)
6391 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6392 else if (can_turn_left)
6393 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6394 else if (can_turn_right)
6395 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6397 MovDir[x][y] = back_dir;
6399 MovDelay[x][y] = 16 + 16 * RND(3);
6401 else if (element == EL_PACMAN)
6403 boolean can_turn_left = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
6404 boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
6406 if (can_turn_left && can_turn_right)
6407 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6408 else if (can_turn_left)
6409 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6410 else if (can_turn_right)
6411 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6413 MovDir[x][y] = back_dir;
6415 MovDelay[x][y] = 6 + RND(40);
6417 else if (element == EL_PIG)
6419 boolean can_turn_left = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
6420 boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
6421 boolean can_move_on = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
6422 boolean should_turn_left, should_turn_right, should_move_on;
6424 int rnd = RND(rnd_value);
6426 should_turn_left = (can_turn_left &&
6428 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
6429 y + back_dy + left_dy)));
6430 should_turn_right = (can_turn_right &&
6432 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
6433 y + back_dy + right_dy)));
6434 should_move_on = (can_move_on &&
6437 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
6438 y + move_dy + left_dy) ||
6439 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
6440 y + move_dy + right_dy)));
6442 if (should_turn_left || should_turn_right || should_move_on)
6444 if (should_turn_left && should_turn_right && should_move_on)
6445 MovDir[x][y] = (rnd < rnd_value / 3 ? left_dir :
6446 rnd < 2 * rnd_value / 3 ? right_dir :
6448 else if (should_turn_left && should_turn_right)
6449 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6450 else if (should_turn_left && should_move_on)
6451 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
6452 else if (should_turn_right && should_move_on)
6453 MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
6454 else if (should_turn_left)
6455 MovDir[x][y] = left_dir;
6456 else if (should_turn_right)
6457 MovDir[x][y] = right_dir;
6458 else if (should_move_on)
6459 MovDir[x][y] = old_move_dir;
6461 else if (can_move_on && rnd > rnd_value / 8)
6462 MovDir[x][y] = old_move_dir;
6463 else if (can_turn_left && can_turn_right)
6464 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6465 else if (can_turn_left && rnd > rnd_value / 8)
6466 MovDir[x][y] = left_dir;
6467 else if (can_turn_right && rnd > rnd_value/8)
6468 MovDir[x][y] = right_dir;
6470 MovDir[x][y] = back_dir;
6472 xx = x + move_xy[MovDir[x][y]].dx;
6473 yy = y + move_xy[MovDir[x][y]].dy;
6475 if (!IN_LEV_FIELD(xx, yy) ||
6476 (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy])))
6477 MovDir[x][y] = old_move_dir;
6481 else if (element == EL_DRAGON)
6483 boolean can_turn_left = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
6484 boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
6485 boolean can_move_on = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
6487 int rnd = RND(rnd_value);
6489 if (can_move_on && rnd > rnd_value / 8)
6490 MovDir[x][y] = old_move_dir;
6491 else if (can_turn_left && can_turn_right)
6492 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6493 else if (can_turn_left && rnd > rnd_value / 8)
6494 MovDir[x][y] = left_dir;
6495 else if (can_turn_right && rnd > rnd_value / 8)
6496 MovDir[x][y] = right_dir;
6498 MovDir[x][y] = back_dir;
6500 xx = x + move_xy[MovDir[x][y]].dx;
6501 yy = y + move_xy[MovDir[x][y]].dy;
6503 if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
6504 MovDir[x][y] = old_move_dir;
6508 else if (element == EL_MOLE)
6510 boolean can_move_on =
6511 (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
6512 IS_AMOEBOID(Feld[move_x][move_y]) ||
6513 Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
6516 boolean can_turn_left =
6517 (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
6518 IS_AMOEBOID(Feld[left_x][left_y])));
6520 boolean can_turn_right =
6521 (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
6522 IS_AMOEBOID(Feld[right_x][right_y])));
6524 if (can_turn_left && can_turn_right)
6525 MovDir[x][y] = (RND(2) ? left_dir : right_dir);
6526 else if (can_turn_left)
6527 MovDir[x][y] = left_dir;
6529 MovDir[x][y] = right_dir;
6532 if (MovDir[x][y] != old_move_dir)
6535 else if (element == EL_BALLOON)
6537 MovDir[x][y] = game.wind_direction;
6540 else if (element == EL_SPRING)
6542 if (MovDir[x][y] & MV_HORIZONTAL)
6544 if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
6545 !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
6547 Feld[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
6548 ResetGfxAnimation(move_x, move_y);
6549 TEST_DrawLevelField(move_x, move_y);
6551 MovDir[x][y] = back_dir;
6553 else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
6554 SPRING_CAN_ENTER_FIELD(element, x, y + 1))
6555 MovDir[x][y] = MV_NONE;
6560 else if (element == EL_ROBOT ||
6561 element == EL_SATELLITE ||
6562 element == EL_PENGUIN ||
6563 element == EL_EMC_ANDROID)
6565 int attr_x = -1, attr_y = -1;
6576 for (i = 0; i < MAX_PLAYERS; i++)
6578 struct PlayerInfo *player = &stored_player[i];
6579 int jx = player->jx, jy = player->jy;
6581 if (!player->active)
6585 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
6593 if (element == EL_ROBOT && ZX >= 0 && ZY >= 0 &&
6594 (Feld[ZX][ZY] == EL_ROBOT_WHEEL_ACTIVE ||
6595 game.engine_version < VERSION_IDENT(3,1,0,0)))
6601 if (element == EL_PENGUIN)
6604 static int xy[4][2] =
6612 for (i = 0; i < NUM_DIRECTIONS; i++)
6614 int ex = x + xy[i][0];
6615 int ey = y + xy[i][1];
6617 if (IN_LEV_FIELD(ex, ey) && (Feld[ex][ey] == EL_EXIT_OPEN ||
6618 Feld[ex][ey] == EL_EM_EXIT_OPEN ||
6619 Feld[ex][ey] == EL_STEEL_EXIT_OPEN ||
6620 Feld[ex][ey] == EL_EM_STEEL_EXIT_OPEN))
6629 MovDir[x][y] = MV_NONE;
6631 MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
6632 else if (attr_x > x)
6633 MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
6635 MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
6636 else if (attr_y > y)
6637 MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
6639 if (element == EL_ROBOT)
6643 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6644 MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
6645 Moving2Blocked(x, y, &newx, &newy);
6647 if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
6648 MovDelay[x][y] = 8 + 8 * !RND(3);
6650 MovDelay[x][y] = 16;
6652 else if (element == EL_PENGUIN)
6658 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6660 boolean first_horiz = RND(2);
6661 int new_move_dir = MovDir[x][y];
6664 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6665 Moving2Blocked(x, y, &newx, &newy);
6667 if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
6671 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6672 Moving2Blocked(x, y, &newx, &newy);
6674 if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
6677 MovDir[x][y] = old_move_dir;
6681 else if (element == EL_SATELLITE)
6687 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6689 boolean first_horiz = RND(2);
6690 int new_move_dir = MovDir[x][y];
6693 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6694 Moving2Blocked(x, y, &newx, &newy);
6696 if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
6700 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6701 Moving2Blocked(x, y, &newx, &newy);
6703 if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
6706 MovDir[x][y] = old_move_dir;
6710 else if (element == EL_EMC_ANDROID)
6712 static int check_pos[16] =
6714 -1, /* 0 => (invalid) */
6715 7, /* 1 => MV_LEFT */
6716 3, /* 2 => MV_RIGHT */
6717 -1, /* 3 => (invalid) */
6719 0, /* 5 => MV_LEFT | MV_UP */
6720 2, /* 6 => MV_RIGHT | MV_UP */
6721 -1, /* 7 => (invalid) */
6722 5, /* 8 => MV_DOWN */
6723 6, /* 9 => MV_LEFT | MV_DOWN */
6724 4, /* 10 => MV_RIGHT | MV_DOWN */
6725 -1, /* 11 => (invalid) */
6726 -1, /* 12 => (invalid) */
6727 -1, /* 13 => (invalid) */
6728 -1, /* 14 => (invalid) */
6729 -1, /* 15 => (invalid) */
6737 { -1, -1, MV_LEFT | MV_UP },
6739 { +1, -1, MV_RIGHT | MV_UP },
6740 { +1, 0, MV_RIGHT },
6741 { +1, +1, MV_RIGHT | MV_DOWN },
6743 { -1, +1, MV_LEFT | MV_DOWN },
6746 int start_pos, check_order;
6747 boolean can_clone = FALSE;
6750 /* check if there is any free field around current position */
6751 for (i = 0; i < 8; i++)
6753 int newx = x + check_xy[i].dx;
6754 int newy = y + check_xy[i].dy;
6756 if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
6764 if (can_clone) /* randomly find an element to clone */
6768 start_pos = check_pos[RND(8)];
6769 check_order = (RND(2) ? -1 : +1);
6771 for (i = 0; i < 8; i++)
6773 int pos_raw = start_pos + i * check_order;
6774 int pos = (pos_raw + 8) % 8;
6775 int newx = x + check_xy[pos].dx;
6776 int newy = y + check_xy[pos].dy;
6778 if (ANDROID_CAN_CLONE_FIELD(newx, newy))
6780 element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
6781 element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
6783 Store[x][y] = Feld[newx][newy];
6792 if (can_clone) /* randomly find a direction to move */
6796 start_pos = check_pos[RND(8)];
6797 check_order = (RND(2) ? -1 : +1);
6799 for (i = 0; i < 8; i++)
6801 int pos_raw = start_pos + i * check_order;
6802 int pos = (pos_raw + 8) % 8;
6803 int newx = x + check_xy[pos].dx;
6804 int newy = y + check_xy[pos].dy;
6805 int new_move_dir = check_xy[pos].dir;
6807 if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
6809 MovDir[x][y] = new_move_dir;
6810 MovDelay[x][y] = level.android_clone_time * 8 + 1;
6819 if (can_clone) /* cloning and moving successful */
6822 /* cannot clone -- try to move towards player */
6824 start_pos = check_pos[MovDir[x][y] & 0x0f];
6825 check_order = (RND(2) ? -1 : +1);
6827 for (i = 0; i < 3; i++)
6829 /* first check start_pos, then previous/next or (next/previous) pos */
6830 int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
6831 int pos = (pos_raw + 8) % 8;
6832 int newx = x + check_xy[pos].dx;
6833 int newy = y + check_xy[pos].dy;
6834 int new_move_dir = check_xy[pos].dir;
6836 if (IS_PLAYER(newx, newy))
6839 if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
6841 MovDir[x][y] = new_move_dir;
6842 MovDelay[x][y] = level.android_move_time * 8 + 1;
6849 else if (move_pattern == MV_TURNING_LEFT ||
6850 move_pattern == MV_TURNING_RIGHT ||
6851 move_pattern == MV_TURNING_LEFT_RIGHT ||
6852 move_pattern == MV_TURNING_RIGHT_LEFT ||
6853 move_pattern == MV_TURNING_RANDOM ||
6854 move_pattern == MV_ALL_DIRECTIONS)
6856 boolean can_turn_left =
6857 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
6858 boolean can_turn_right =
6859 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
6861 if (element_info[element].move_stepsize == 0) /* "not moving" */
6864 if (move_pattern == MV_TURNING_LEFT)
6865 MovDir[x][y] = left_dir;
6866 else if (move_pattern == MV_TURNING_RIGHT)
6867 MovDir[x][y] = right_dir;
6868 else if (move_pattern == MV_TURNING_LEFT_RIGHT)
6869 MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
6870 else if (move_pattern == MV_TURNING_RIGHT_LEFT)
6871 MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
6872 else if (move_pattern == MV_TURNING_RANDOM)
6873 MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
6874 can_turn_right && !can_turn_left ? right_dir :
6875 RND(2) ? left_dir : right_dir);
6876 else if (can_turn_left && can_turn_right)
6877 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6878 else if (can_turn_left)
6879 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6880 else if (can_turn_right)
6881 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6883 MovDir[x][y] = back_dir;
6885 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6887 else if (move_pattern == MV_HORIZONTAL ||
6888 move_pattern == MV_VERTICAL)
6890 if (move_pattern & old_move_dir)
6891 MovDir[x][y] = back_dir;
6892 else if (move_pattern == MV_HORIZONTAL)
6893 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
6894 else if (move_pattern == MV_VERTICAL)
6895 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
6897 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6899 else if (move_pattern & MV_ANY_DIRECTION)
6901 MovDir[x][y] = move_pattern;
6902 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6904 else if (move_pattern & MV_WIND_DIRECTION)
6906 MovDir[x][y] = game.wind_direction;
6907 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6909 else if (move_pattern == MV_ALONG_LEFT_SIDE)
6911 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
6912 MovDir[x][y] = left_dir;
6913 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
6914 MovDir[x][y] = right_dir;
6916 if (MovDir[x][y] != old_move_dir)
6917 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6919 else if (move_pattern == MV_ALONG_RIGHT_SIDE)
6921 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
6922 MovDir[x][y] = right_dir;
6923 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
6924 MovDir[x][y] = left_dir;
6926 if (MovDir[x][y] != old_move_dir)
6927 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6929 else if (move_pattern == MV_TOWARDS_PLAYER ||
6930 move_pattern == MV_AWAY_FROM_PLAYER)
6932 int attr_x = -1, attr_y = -1;
6934 boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
6945 for (i = 0; i < MAX_PLAYERS; i++)
6947 struct PlayerInfo *player = &stored_player[i];
6948 int jx = player->jx, jy = player->jy;
6950 if (!player->active)
6954 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
6962 MovDir[x][y] = MV_NONE;
6964 MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
6965 else if (attr_x > x)
6966 MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
6968 MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
6969 else if (attr_y > y)
6970 MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
6972 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6974 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6976 boolean first_horiz = RND(2);
6977 int new_move_dir = MovDir[x][y];
6979 if (element_info[element].move_stepsize == 0) /* "not moving" */
6981 first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
6982 MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6988 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6989 Moving2Blocked(x, y, &newx, &newy);
6991 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
6995 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6996 Moving2Blocked(x, y, &newx, &newy);
6998 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7001 MovDir[x][y] = old_move_dir;
7004 else if (move_pattern == MV_WHEN_PUSHED ||
7005 move_pattern == MV_WHEN_DROPPED)
7007 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7008 MovDir[x][y] = MV_NONE;
7012 else if (move_pattern & MV_MAZE_RUNNER_STYLE)
7014 static int test_xy[7][2] =
7024 static int test_dir[7] =
7034 boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
7035 int move_preference = -1000000; /* start with very low preference */
7036 int new_move_dir = MV_NONE;
7037 int start_test = RND(4);
7040 for (i = 0; i < NUM_DIRECTIONS; i++)
7042 int move_dir = test_dir[start_test + i];
7043 int move_dir_preference;
7045 xx = x + test_xy[start_test + i][0];
7046 yy = y + test_xy[start_test + i][1];
7048 if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
7049 (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
7051 new_move_dir = move_dir;
7056 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
7059 move_dir_preference = -1 * RunnerVisit[xx][yy];
7060 if (hunter_mode && PlayerVisit[xx][yy] > 0)
7061 move_dir_preference = PlayerVisit[xx][yy];
7063 if (move_dir_preference > move_preference)
7065 /* prefer field that has not been visited for the longest time */
7066 move_preference = move_dir_preference;
7067 new_move_dir = move_dir;
7069 else if (move_dir_preference == move_preference &&
7070 move_dir == old_move_dir)
7072 /* prefer last direction when all directions are preferred equally */
7073 move_preference = move_dir_preference;
7074 new_move_dir = move_dir;
7078 MovDir[x][y] = new_move_dir;
7079 if (old_move_dir != new_move_dir)
7080 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7084 static void TurnRound(int x, int y)
7086 int direction = MovDir[x][y];
7090 GfxDir[x][y] = MovDir[x][y];
7092 if (direction != MovDir[x][y])
7096 GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
7098 ResetGfxFrame(x, y, FALSE);
7101 static boolean JustBeingPushed(int x, int y)
7105 for (i = 0; i < MAX_PLAYERS; i++)
7107 struct PlayerInfo *player = &stored_player[i];
7109 if (player->active && player->is_pushing && player->MovPos)
7111 int next_jx = player->jx + (player->jx - player->last_jx);
7112 int next_jy = player->jy + (player->jy - player->last_jy);
7114 if (x == next_jx && y == next_jy)
7122 void StartMoving(int x, int y)
7124 boolean started_moving = FALSE; /* some elements can fall _and_ move */
7125 int element = Feld[x][y];
7130 if (MovDelay[x][y] == 0)
7131 GfxAction[x][y] = ACTION_DEFAULT;
7133 if (CAN_FALL(element) && y < lev_fieldy - 1)
7135 if ((x > 0 && IS_PLAYER(x - 1, y)) ||
7136 (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
7137 if (JustBeingPushed(x, y))
7140 if (element == EL_QUICKSAND_FULL)
7142 if (IS_FREE(x, y + 1))
7144 InitMovingField(x, y, MV_DOWN);
7145 started_moving = TRUE;
7147 Feld[x][y] = EL_QUICKSAND_EMPTYING;
7148 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7149 if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7150 Store[x][y] = EL_ROCK;
7152 Store[x][y] = EL_ROCK;
7155 PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7157 else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7159 if (!MovDelay[x][y])
7161 MovDelay[x][y] = TILEY + 1;
7163 ResetGfxAnimation(x, y);
7164 ResetGfxAnimation(x, y + 1);
7169 DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7170 DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7177 Feld[x][y] = EL_QUICKSAND_EMPTY;
7178 Feld[x][y + 1] = EL_QUICKSAND_FULL;
7179 Store[x][y + 1] = Store[x][y];
7182 PlayLevelSoundAction(x, y, ACTION_FILLING);
7184 else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7186 if (!MovDelay[x][y])
7188 MovDelay[x][y] = TILEY + 1;
7190 ResetGfxAnimation(x, y);
7191 ResetGfxAnimation(x, y + 1);
7196 DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7197 DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7204 Feld[x][y] = EL_QUICKSAND_EMPTY;
7205 Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7206 Store[x][y + 1] = Store[x][y];
7209 PlayLevelSoundAction(x, y, ACTION_FILLING);
7212 else if (element == EL_QUICKSAND_FAST_FULL)
7214 if (IS_FREE(x, y + 1))
7216 InitMovingField(x, y, MV_DOWN);
7217 started_moving = TRUE;
7219 Feld[x][y] = EL_QUICKSAND_FAST_EMPTYING;
7220 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7221 if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7222 Store[x][y] = EL_ROCK;
7224 Store[x][y] = EL_ROCK;
7227 PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7229 else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7231 if (!MovDelay[x][y])
7233 MovDelay[x][y] = TILEY + 1;
7235 ResetGfxAnimation(x, y);
7236 ResetGfxAnimation(x, y + 1);
7241 DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7242 DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7249 Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
7250 Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7251 Store[x][y + 1] = Store[x][y];
7254 PlayLevelSoundAction(x, y, ACTION_FILLING);
7256 else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7258 if (!MovDelay[x][y])
7260 MovDelay[x][y] = TILEY + 1;
7262 ResetGfxAnimation(x, y);
7263 ResetGfxAnimation(x, y + 1);
7268 DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7269 DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7276 Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
7277 Feld[x][y + 1] = EL_QUICKSAND_FULL;
7278 Store[x][y + 1] = Store[x][y];
7281 PlayLevelSoundAction(x, y, ACTION_FILLING);
7284 else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7285 Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7287 InitMovingField(x, y, MV_DOWN);
7288 started_moving = TRUE;
7290 Feld[x][y] = EL_QUICKSAND_FILLING;
7291 Store[x][y] = element;
7293 PlayLevelSoundAction(x, y, ACTION_FILLING);
7295 else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7296 Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7298 InitMovingField(x, y, MV_DOWN);
7299 started_moving = TRUE;
7301 Feld[x][y] = EL_QUICKSAND_FAST_FILLING;
7302 Store[x][y] = element;
7304 PlayLevelSoundAction(x, y, ACTION_FILLING);
7306 else if (element == EL_MAGIC_WALL_FULL)
7308 if (IS_FREE(x, y + 1))
7310 InitMovingField(x, y, MV_DOWN);
7311 started_moving = TRUE;
7313 Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
7314 Store[x][y] = EL_CHANGED(Store[x][y]);
7316 else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
7318 if (!MovDelay[x][y])
7319 MovDelay[x][y] = TILEY / 4 + 1;
7328 Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
7329 Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
7330 Store[x][y + 1] = EL_CHANGED(Store[x][y]);
7334 else if (element == EL_BD_MAGIC_WALL_FULL)
7336 if (IS_FREE(x, y + 1))
7338 InitMovingField(x, y, MV_DOWN);
7339 started_moving = TRUE;
7341 Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
7342 Store[x][y] = EL_CHANGED_BD(Store[x][y]);
7344 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
7346 if (!MovDelay[x][y])
7347 MovDelay[x][y] = TILEY / 4 + 1;
7356 Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
7357 Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
7358 Store[x][y + 1] = EL_CHANGED_BD(Store[x][y]);
7362 else if (element == EL_DC_MAGIC_WALL_FULL)
7364 if (IS_FREE(x, y + 1))
7366 InitMovingField(x, y, MV_DOWN);
7367 started_moving = TRUE;
7369 Feld[x][y] = EL_DC_MAGIC_WALL_EMPTYING;
7370 Store[x][y] = EL_CHANGED_DC(Store[x][y]);
7372 else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
7374 if (!MovDelay[x][y])
7375 MovDelay[x][y] = TILEY / 4 + 1;
7384 Feld[x][y] = EL_DC_MAGIC_WALL_ACTIVE;
7385 Feld[x][y + 1] = EL_DC_MAGIC_WALL_FULL;
7386 Store[x][y + 1] = EL_CHANGED_DC(Store[x][y]);
7390 else if ((CAN_PASS_MAGIC_WALL(element) &&
7391 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
7392 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)) ||
7393 (CAN_PASS_DC_MAGIC_WALL(element) &&
7394 (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)))
7397 InitMovingField(x, y, MV_DOWN);
7398 started_moving = TRUE;
7401 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
7402 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ? EL_BD_MAGIC_WALL_FILLING :
7403 EL_DC_MAGIC_WALL_FILLING);
7404 Store[x][y] = element;
7406 else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
7408 SplashAcid(x, y + 1);
7410 InitMovingField(x, y, MV_DOWN);
7411 started_moving = TRUE;
7413 Store[x][y] = EL_ACID;
7416 (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7417 CheckImpact[x][y] && !IS_FREE(x, y + 1)) ||
7418 (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
7419 CAN_FALL(element) && WasJustFalling[x][y] &&
7420 (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
7422 (game.engine_version < VERSION_IDENT(2,2,0,7) &&
7423 CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
7424 (Feld[x][y + 1] == EL_BLOCKED)))
7426 /* this is needed for a special case not covered by calling "Impact()"
7427 from "ContinueMoving()": if an element moves to a tile directly below
7428 another element which was just falling on that tile (which was empty
7429 in the previous frame), the falling element above would just stop
7430 instead of smashing the element below (in previous version, the above
7431 element was just checked for "moving" instead of "falling", resulting
7432 in incorrect smashes caused by horizontal movement of the above
7433 element; also, the case of the player being the element to smash was
7434 simply not covered here... :-/ ) */
7436 CheckCollision[x][y] = 0;
7437 CheckImpact[x][y] = 0;
7441 else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
7443 if (MovDir[x][y] == MV_NONE)
7445 InitMovingField(x, y, MV_DOWN);
7446 started_moving = TRUE;
7449 else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
7451 if (WasJustFalling[x][y]) /* prevent animation from being restarted */
7452 MovDir[x][y] = MV_DOWN;
7454 InitMovingField(x, y, MV_DOWN);
7455 started_moving = TRUE;
7457 else if (element == EL_AMOEBA_DROP)
7459 Feld[x][y] = EL_AMOEBA_GROWING;
7460 Store[x][y] = EL_AMOEBA_WET;
7462 else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
7463 (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
7464 !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
7465 element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
7467 boolean can_fall_left = (x > 0 && IS_FREE(x - 1, y) &&
7468 (IS_FREE(x - 1, y + 1) ||
7469 Feld[x - 1][y + 1] == EL_ACID));
7470 boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
7471 (IS_FREE(x + 1, y + 1) ||
7472 Feld[x + 1][y + 1] == EL_ACID));
7473 boolean can_fall_any = (can_fall_left || can_fall_right);
7474 boolean can_fall_both = (can_fall_left && can_fall_right);
7475 int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
7477 if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
7479 if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
7480 can_fall_right = FALSE;
7481 else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
7482 can_fall_left = FALSE;
7483 else if (slippery_type == SLIPPERY_ONLY_LEFT)
7484 can_fall_right = FALSE;
7485 else if (slippery_type == SLIPPERY_ONLY_RIGHT)
7486 can_fall_left = FALSE;
7488 can_fall_any = (can_fall_left || can_fall_right);
7489 can_fall_both = FALSE;
7494 if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
7495 can_fall_right = FALSE; /* slip down on left side */
7497 can_fall_left = !(can_fall_right = RND(2));
7499 can_fall_both = FALSE;
7504 /* if not determined otherwise, prefer left side for slipping down */
7505 InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
7506 started_moving = TRUE;
7509 else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
7511 boolean left_is_free = (x > 0 && IS_FREE(x - 1, y));
7512 boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
7513 int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
7514 int belt_dir = game.belt_dir[belt_nr];
7516 if ((belt_dir == MV_LEFT && left_is_free) ||
7517 (belt_dir == MV_RIGHT && right_is_free))
7519 int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
7521 InitMovingField(x, y, belt_dir);
7522 started_moving = TRUE;
7524 Pushed[x][y] = TRUE;
7525 Pushed[nextx][y] = TRUE;
7527 GfxAction[x][y] = ACTION_DEFAULT;
7531 MovDir[x][y] = 0; /* if element was moving, stop it */
7536 /* not "else if" because of elements that can fall and move (EL_SPRING) */
7537 if (CAN_MOVE(element) && !started_moving)
7539 int move_pattern = element_info[element].move_pattern;
7542 Moving2Blocked(x, y, &newx, &newy);
7544 if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
7547 if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7548 CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7550 WasJustMoving[x][y] = 0;
7551 CheckCollision[x][y] = 0;
7553 TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
7555 if (Feld[x][y] != element) /* element has changed */
7559 if (!MovDelay[x][y]) /* start new movement phase */
7561 /* all objects that can change their move direction after each step
7562 (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
7564 if (element != EL_YAMYAM &&
7565 element != EL_DARK_YAMYAM &&
7566 element != EL_PACMAN &&
7567 !(move_pattern & MV_ANY_DIRECTION) &&
7568 move_pattern != MV_TURNING_LEFT &&
7569 move_pattern != MV_TURNING_RIGHT &&
7570 move_pattern != MV_TURNING_LEFT_RIGHT &&
7571 move_pattern != MV_TURNING_RIGHT_LEFT &&
7572 move_pattern != MV_TURNING_RANDOM)
7576 if (MovDelay[x][y] && (element == EL_BUG ||
7577 element == EL_SPACESHIP ||
7578 element == EL_SP_SNIKSNAK ||
7579 element == EL_SP_ELECTRON ||
7580 element == EL_MOLE))
7581 TEST_DrawLevelField(x, y);
7585 if (MovDelay[x][y]) /* wait some time before next movement */
7589 if (element == EL_ROBOT ||
7590 element == EL_YAMYAM ||
7591 element == EL_DARK_YAMYAM)
7593 DrawLevelElementAnimationIfNeeded(x, y, element);
7594 PlayLevelSoundAction(x, y, ACTION_WAITING);
7596 else if (element == EL_SP_ELECTRON)
7597 DrawLevelElementAnimationIfNeeded(x, y, element);
7598 else if (element == EL_DRAGON)
7601 int dir = MovDir[x][y];
7602 int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
7603 int dy = (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
7604 int graphic = (dir == MV_LEFT ? IMG_FLAMES_1_LEFT :
7605 dir == MV_RIGHT ? IMG_FLAMES_1_RIGHT :
7606 dir == MV_UP ? IMG_FLAMES_1_UP :
7607 dir == MV_DOWN ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
7608 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
7610 GfxAction[x][y] = ACTION_ATTACKING;
7612 if (IS_PLAYER(x, y))
7613 DrawPlayerField(x, y);
7615 TEST_DrawLevelField(x, y);
7617 PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
7619 for (i = 1; i <= 3; i++)
7621 int xx = x + i * dx;
7622 int yy = y + i * dy;
7623 int sx = SCREENX(xx);
7624 int sy = SCREENY(yy);
7625 int flame_graphic = graphic + (i - 1);
7627 if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
7632 int flamed = MovingOrBlocked2Element(xx, yy);
7634 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
7637 RemoveMovingField(xx, yy);
7639 ChangeDelay[xx][yy] = 0;
7641 Feld[xx][yy] = EL_FLAMES;
7643 if (IN_SCR_FIELD(sx, sy))
7645 TEST_DrawLevelFieldCrumbled(xx, yy);
7646 DrawGraphic(sx, sy, flame_graphic, frame);
7651 if (Feld[xx][yy] == EL_FLAMES)
7652 Feld[xx][yy] = EL_EMPTY;
7653 TEST_DrawLevelField(xx, yy);
7658 if (MovDelay[x][y]) /* element still has to wait some time */
7660 PlayLevelSoundAction(x, y, ACTION_WAITING);
7666 /* now make next step */
7668 Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
7670 if (DONT_COLLIDE_WITH(element) &&
7671 IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
7672 !PLAYER_ENEMY_PROTECTED(newx, newy))
7674 TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
7679 else if (CAN_MOVE_INTO_ACID(element) &&
7680 IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID &&
7681 !IS_MV_DIAGONAL(MovDir[x][y]) &&
7682 (MovDir[x][y] == MV_DOWN ||
7683 game.engine_version >= VERSION_IDENT(3,1,0,0)))
7685 SplashAcid(newx, newy);
7686 Store[x][y] = EL_ACID;
7688 else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
7690 if (Feld[newx][newy] == EL_EXIT_OPEN ||
7691 Feld[newx][newy] == EL_EM_EXIT_OPEN ||
7692 Feld[newx][newy] == EL_STEEL_EXIT_OPEN ||
7693 Feld[newx][newy] == EL_EM_STEEL_EXIT_OPEN)
7696 TEST_DrawLevelField(x, y);
7698 PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
7699 if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
7700 DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
7702 local_player->friends_still_needed--;
7703 if (!local_player->friends_still_needed &&
7704 !local_player->GameOver && AllPlayersGone)
7705 PlayerWins(local_player);
7709 else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
7711 if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MP_MOVING)
7712 TEST_DrawLevelField(newx, newy);
7714 GfxDir[x][y] = MovDir[x][y] = MV_NONE;
7716 else if (!IS_FREE(newx, newy))
7718 GfxAction[x][y] = ACTION_WAITING;
7720 if (IS_PLAYER(x, y))
7721 DrawPlayerField(x, y);
7723 TEST_DrawLevelField(x, y);
7728 else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
7730 if (IS_FOOD_PIG(Feld[newx][newy]))
7732 if (IS_MOVING(newx, newy))
7733 RemoveMovingField(newx, newy);
7736 Feld[newx][newy] = EL_EMPTY;
7737 TEST_DrawLevelField(newx, newy);
7740 PlayLevelSound(x, y, SND_PIG_DIGGING);
7742 else if (!IS_FREE(newx, newy))
7744 if (IS_PLAYER(x, y))
7745 DrawPlayerField(x, y);
7747 TEST_DrawLevelField(x, y);
7752 else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
7754 if (Store[x][y] != EL_EMPTY)
7756 boolean can_clone = FALSE;
7759 /* check if element to clone is still there */
7760 for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
7762 if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == Store[x][y])
7770 /* cannot clone or target field not free anymore -- do not clone */
7771 if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7772 Store[x][y] = EL_EMPTY;
7775 if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7777 if (IS_MV_DIAGONAL(MovDir[x][y]))
7779 int diagonal_move_dir = MovDir[x][y];
7780 int stored = Store[x][y];
7781 int change_delay = 8;
7784 /* android is moving diagonally */
7786 CreateField(x, y, EL_DIAGONAL_SHRINKING);
7788 Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
7789 GfxElement[x][y] = EL_EMC_ANDROID;
7790 GfxAction[x][y] = ACTION_SHRINKING;
7791 GfxDir[x][y] = diagonal_move_dir;
7792 ChangeDelay[x][y] = change_delay;
7794 graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
7797 DrawLevelGraphicAnimation(x, y, graphic);
7798 PlayLevelSoundAction(x, y, ACTION_SHRINKING);
7800 if (Feld[newx][newy] == EL_ACID)
7802 SplashAcid(newx, newy);
7807 CreateField(newx, newy, EL_DIAGONAL_GROWING);
7809 Store[newx][newy] = EL_EMC_ANDROID;
7810 GfxElement[newx][newy] = EL_EMC_ANDROID;
7811 GfxAction[newx][newy] = ACTION_GROWING;
7812 GfxDir[newx][newy] = diagonal_move_dir;
7813 ChangeDelay[newx][newy] = change_delay;
7815 graphic = el_act_dir2img(GfxElement[newx][newy],
7816 GfxAction[newx][newy], GfxDir[newx][newy]);
7818 DrawLevelGraphicAnimation(newx, newy, graphic);
7819 PlayLevelSoundAction(newx, newy, ACTION_GROWING);
7825 Feld[newx][newy] = EL_EMPTY;
7826 TEST_DrawLevelField(newx, newy);
7828 PlayLevelSoundAction(x, y, ACTION_DIGGING);
7831 else if (!IS_FREE(newx, newy))
7836 else if (IS_CUSTOM_ELEMENT(element) &&
7837 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7839 if (!DigFieldByCE(newx, newy, element))
7842 if (move_pattern & MV_MAZE_RUNNER_STYLE)
7844 RunnerVisit[x][y] = FrameCounter;
7845 PlayerVisit[x][y] /= 8; /* expire player visit path */
7848 else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
7850 if (!IS_FREE(newx, newy))
7852 if (IS_PLAYER(x, y))
7853 DrawPlayerField(x, y);
7855 TEST_DrawLevelField(x, y);
7861 boolean wanna_flame = !RND(10);
7862 int dx = newx - x, dy = newy - y;
7863 int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
7864 int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
7865 int element1 = (IN_LEV_FIELD(newx1, newy1) ?
7866 MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
7867 int element2 = (IN_LEV_FIELD(newx2, newy2) ?
7868 MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
7871 IS_CLASSIC_ENEMY(element1) ||
7872 IS_CLASSIC_ENEMY(element2)) &&
7873 element1 != EL_DRAGON && element2 != EL_DRAGON &&
7874 element1 != EL_FLAMES && element2 != EL_FLAMES)
7876 ResetGfxAnimation(x, y);
7877 GfxAction[x][y] = ACTION_ATTACKING;
7879 if (IS_PLAYER(x, y))
7880 DrawPlayerField(x, y);
7882 TEST_DrawLevelField(x, y);
7884 PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
7886 MovDelay[x][y] = 50;
7888 Feld[newx][newy] = EL_FLAMES;
7889 if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
7890 Feld[newx1][newy1] = EL_FLAMES;
7891 if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
7892 Feld[newx2][newy2] = EL_FLAMES;
7898 else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
7899 Feld[newx][newy] == EL_DIAMOND)
7901 if (IS_MOVING(newx, newy))
7902 RemoveMovingField(newx, newy);
7905 Feld[newx][newy] = EL_EMPTY;
7906 TEST_DrawLevelField(newx, newy);
7909 PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
7911 else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
7912 IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
7914 if (AmoebaNr[newx][newy])
7916 AmoebaCnt2[AmoebaNr[newx][newy]]--;
7917 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
7918 Feld[newx][newy] == EL_BD_AMOEBA)
7919 AmoebaCnt[AmoebaNr[newx][newy]]--;
7922 if (IS_MOVING(newx, newy))
7924 RemoveMovingField(newx, newy);
7928 Feld[newx][newy] = EL_EMPTY;
7929 TEST_DrawLevelField(newx, newy);
7932 PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
7934 else if ((element == EL_PACMAN || element == EL_MOLE)
7935 && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
7937 if (AmoebaNr[newx][newy])
7939 AmoebaCnt2[AmoebaNr[newx][newy]]--;
7940 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
7941 Feld[newx][newy] == EL_BD_AMOEBA)
7942 AmoebaCnt[AmoebaNr[newx][newy]]--;
7945 if (element == EL_MOLE)
7947 Feld[newx][newy] = EL_AMOEBA_SHRINKING;
7948 PlayLevelSound(x, y, SND_MOLE_DIGGING);
7950 ResetGfxAnimation(x, y);
7951 GfxAction[x][y] = ACTION_DIGGING;
7952 TEST_DrawLevelField(x, y);
7954 MovDelay[newx][newy] = 0; /* start amoeba shrinking delay */
7956 return; /* wait for shrinking amoeba */
7958 else /* element == EL_PACMAN */
7960 Feld[newx][newy] = EL_EMPTY;
7961 TEST_DrawLevelField(newx, newy);
7962 PlayLevelSound(x, y, SND_PACMAN_DIGGING);
7965 else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
7966 (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
7967 (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
7969 /* wait for shrinking amoeba to completely disappear */
7972 else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
7974 /* object was running against a wall */
7978 if (GFX_ELEMENT(element) != EL_SAND) /* !!! FIX THIS (crumble) !!! */
7979 DrawLevelElementAnimation(x, y, element);
7981 if (DONT_TOUCH(element))
7982 TestIfBadThingTouchesPlayer(x, y);
7987 InitMovingField(x, y, MovDir[x][y]);
7989 PlayLevelSoundAction(x, y, ACTION_MOVING);
7993 ContinueMoving(x, y);
7996 void ContinueMoving(int x, int y)
7998 int element = Feld[x][y];
7999 struct ElementInfo *ei = &element_info[element];
8000 int direction = MovDir[x][y];
8001 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
8002 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
8003 int newx = x + dx, newy = y + dy;
8004 int stored = Store[x][y];
8005 int stored_new = Store[newx][newy];
8006 boolean pushed_by_player = (Pushed[x][y] && IS_PLAYER(x, y));
8007 boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
8008 boolean last_line = (newy == lev_fieldy - 1);
8010 MovPos[x][y] += getElementMoveStepsize(x, y);
8012 if (pushed_by_player) /* special case: moving object pushed by player */
8013 MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
8015 if (ABS(MovPos[x][y]) < TILEX)
8017 TEST_DrawLevelField(x, y);
8019 return; /* element is still moving */
8022 /* element reached destination field */
8024 Feld[x][y] = EL_EMPTY;
8025 Feld[newx][newy] = element;
8026 MovPos[x][y] = 0; /* force "not moving" for "crumbled sand" */
8028 if (Store[x][y] == EL_ACID) /* element is moving into acid pool */
8030 element = Feld[newx][newy] = EL_ACID;
8032 else if (element == EL_MOLE)
8034 Feld[x][y] = EL_SAND;
8036 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8038 else if (element == EL_QUICKSAND_FILLING)
8040 element = Feld[newx][newy] = get_next_element(element);
8041 Store[newx][newy] = Store[x][y];
8043 else if (element == EL_QUICKSAND_EMPTYING)
8045 Feld[x][y] = get_next_element(element);
8046 element = Feld[newx][newy] = Store[x][y];
8048 else if (element == EL_QUICKSAND_FAST_FILLING)
8050 element = Feld[newx][newy] = get_next_element(element);
8051 Store[newx][newy] = Store[x][y];
8053 else if (element == EL_QUICKSAND_FAST_EMPTYING)
8055 Feld[x][y] = get_next_element(element);
8056 element = Feld[newx][newy] = Store[x][y];
8058 else if (element == EL_MAGIC_WALL_FILLING)
8060 element = Feld[newx][newy] = get_next_element(element);
8061 if (!game.magic_wall_active)
8062 element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
8063 Store[newx][newy] = Store[x][y];
8065 else if (element == EL_MAGIC_WALL_EMPTYING)
8067 Feld[x][y] = get_next_element(element);
8068 if (!game.magic_wall_active)
8069 Feld[x][y] = EL_MAGIC_WALL_DEAD;
8070 element = Feld[newx][newy] = Store[x][y];
8072 InitField(newx, newy, FALSE);
8074 else if (element == EL_BD_MAGIC_WALL_FILLING)
8076 element = Feld[newx][newy] = get_next_element(element);
8077 if (!game.magic_wall_active)
8078 element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
8079 Store[newx][newy] = Store[x][y];
8081 else if (element == EL_BD_MAGIC_WALL_EMPTYING)
8083 Feld[x][y] = get_next_element(element);
8084 if (!game.magic_wall_active)
8085 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
8086 element = Feld[newx][newy] = Store[x][y];
8088 InitField(newx, newy, FALSE);
8090 else if (element == EL_DC_MAGIC_WALL_FILLING)
8092 element = Feld[newx][newy] = get_next_element(element);
8093 if (!game.magic_wall_active)
8094 element = Feld[newx][newy] = EL_DC_MAGIC_WALL_DEAD;
8095 Store[newx][newy] = Store[x][y];
8097 else if (element == EL_DC_MAGIC_WALL_EMPTYING)
8099 Feld[x][y] = get_next_element(element);
8100 if (!game.magic_wall_active)
8101 Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
8102 element = Feld[newx][newy] = Store[x][y];
8104 InitField(newx, newy, FALSE);
8106 else if (element == EL_AMOEBA_DROPPING)
8108 Feld[x][y] = get_next_element(element);
8109 element = Feld[newx][newy] = Store[x][y];
8111 else if (element == EL_SOKOBAN_OBJECT)
8114 Feld[x][y] = Back[x][y];
8116 if (Back[newx][newy])
8117 Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
8119 Back[x][y] = Back[newx][newy] = 0;
8122 Store[x][y] = EL_EMPTY;
8127 MovDelay[newx][newy] = 0;
8129 if (CAN_CHANGE_OR_HAS_ACTION(element))
8131 /* copy element change control values to new field */
8132 ChangeDelay[newx][newy] = ChangeDelay[x][y];
8133 ChangePage[newx][newy] = ChangePage[x][y];
8134 ChangeCount[newx][newy] = ChangeCount[x][y];
8135 ChangeEvent[newx][newy] = ChangeEvent[x][y];
8138 CustomValue[newx][newy] = CustomValue[x][y];
8140 ChangeDelay[x][y] = 0;
8141 ChangePage[x][y] = -1;
8142 ChangeCount[x][y] = 0;
8143 ChangeEvent[x][y] = -1;
8145 CustomValue[x][y] = 0;
8147 /* copy animation control values to new field */
8148 GfxFrame[newx][newy] = GfxFrame[x][y];
8149 GfxRandom[newx][newy] = GfxRandom[x][y]; /* keep same random value */
8150 GfxAction[newx][newy] = GfxAction[x][y]; /* keep action one frame */
8151 GfxDir[newx][newy] = GfxDir[x][y]; /* keep element direction */
8153 Pushed[x][y] = Pushed[newx][newy] = FALSE;
8155 /* some elements can leave other elements behind after moving */
8156 if (ei->move_leave_element != EL_EMPTY &&
8157 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
8158 (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
8160 int move_leave_element = ei->move_leave_element;
8162 /* this makes it possible to leave the removed element again */
8163 if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
8164 move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
8166 Feld[x][y] = move_leave_element;
8168 if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
8169 MovDir[x][y] = direction;
8171 InitField(x, y, FALSE);
8173 if (GFX_CRUMBLED(Feld[x][y]))
8174 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8176 if (ELEM_IS_PLAYER(move_leave_element))
8177 RelocatePlayer(x, y, move_leave_element);
8180 /* do this after checking for left-behind element */
8181 ResetGfxAnimation(x, y); /* reset animation values for old field */
8183 if (!CAN_MOVE(element) ||
8184 (CAN_FALL(element) && direction == MV_DOWN &&
8185 (element == EL_SPRING ||
8186 element_info[element].move_pattern == MV_WHEN_PUSHED ||
8187 element_info[element].move_pattern == MV_WHEN_DROPPED)))
8188 GfxDir[x][y] = MovDir[newx][newy] = 0;
8190 TEST_DrawLevelField(x, y);
8191 TEST_DrawLevelField(newx, newy);
8193 Stop[newx][newy] = TRUE; /* ignore this element until the next frame */
8195 /* prevent pushed element from moving on in pushed direction */
8196 if (pushed_by_player && CAN_MOVE(element) &&
8197 element_info[element].move_pattern & MV_ANY_DIRECTION &&
8198 !(element_info[element].move_pattern & direction))
8199 TurnRound(newx, newy);
8201 /* prevent elements on conveyor belt from moving on in last direction */
8202 if (pushed_by_conveyor && CAN_FALL(element) &&
8203 direction & MV_HORIZONTAL)
8204 MovDir[newx][newy] = 0;
8206 if (!pushed_by_player)
8208 int nextx = newx + dx, nexty = newy + dy;
8209 boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
8211 WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
8213 if (CAN_FALL(element) && direction == MV_DOWN)
8214 WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
8216 if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
8217 CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
8219 if (CAN_FALL(element) && direction == MV_DOWN && check_collision_again)
8220 CheckImpact[newx][newy] = CHECK_DELAY_IMPACT;
8223 if (DONT_TOUCH(element)) /* object may be nasty to player or others */
8225 TestIfBadThingTouchesPlayer(newx, newy);
8226 TestIfBadThingTouchesFriend(newx, newy);
8228 if (!IS_CUSTOM_ELEMENT(element))
8229 TestIfBadThingTouchesOtherBadThing(newx, newy);
8231 else if (element == EL_PENGUIN)
8232 TestIfFriendTouchesBadThing(newx, newy);
8234 if (DONT_GET_HIT_BY(element))
8236 TestIfGoodThingGetsHitByBadThing(newx, newy, direction);
8239 /* give the player one last chance (one more frame) to move away */
8240 if (CAN_FALL(element) && direction == MV_DOWN &&
8241 (last_line || (!IS_FREE(x, newy + 1) &&
8242 (!IS_PLAYER(x, newy + 1) ||
8243 game.engine_version < VERSION_IDENT(3,1,1,0)))))
8246 if (pushed_by_player && !game.use_change_when_pushing_bug)
8248 int push_side = MV_DIR_OPPOSITE(direction);
8249 struct PlayerInfo *player = PLAYERINFO(x, y);
8251 CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
8252 player->index_bit, push_side);
8253 CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
8254 player->index_bit, push_side);
8257 if (element == EL_EMC_ANDROID && pushed_by_player) /* make another move */
8258 MovDelay[newx][newy] = 1;
8260 CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
8262 TestIfElementTouchesCustomElement(x, y); /* empty or new element */
8263 TestIfElementHitsCustomElement(newx, newy, direction);
8264 TestIfPlayerTouchesCustomElement(newx, newy);
8265 TestIfElementTouchesCustomElement(newx, newy);
8267 if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
8268 IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
8269 CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
8270 MV_DIR_OPPOSITE(direction));
8273 int AmoebeNachbarNr(int ax, int ay)
8276 int element = Feld[ax][ay];
8278 static int xy[4][2] =
8286 for (i = 0; i < NUM_DIRECTIONS; i++)
8288 int x = ax + xy[i][0];
8289 int y = ay + xy[i][1];
8291 if (!IN_LEV_FIELD(x, y))
8294 if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
8295 group_nr = AmoebaNr[x][y];
8301 void AmoebenVereinigen(int ax, int ay)
8303 int i, x, y, xx, yy;
8304 int new_group_nr = AmoebaNr[ax][ay];
8305 static int xy[4][2] =
8313 if (new_group_nr == 0)
8316 for (i = 0; i < NUM_DIRECTIONS; i++)
8321 if (!IN_LEV_FIELD(x, y))
8324 if ((Feld[x][y] == EL_AMOEBA_FULL ||
8325 Feld[x][y] == EL_BD_AMOEBA ||
8326 Feld[x][y] == EL_AMOEBA_DEAD) &&
8327 AmoebaNr[x][y] != new_group_nr)
8329 int old_group_nr = AmoebaNr[x][y];
8331 if (old_group_nr == 0)
8334 AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
8335 AmoebaCnt[old_group_nr] = 0;
8336 AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
8337 AmoebaCnt2[old_group_nr] = 0;
8339 SCAN_PLAYFIELD(xx, yy)
8341 if (AmoebaNr[xx][yy] == old_group_nr)
8342 AmoebaNr[xx][yy] = new_group_nr;
8348 void AmoebeUmwandeln(int ax, int ay)
8352 if (Feld[ax][ay] == EL_AMOEBA_DEAD)
8354 int group_nr = AmoebaNr[ax][ay];
8359 printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
8360 printf("AmoebeUmwandeln(): This should never happen!\n");
8365 SCAN_PLAYFIELD(x, y)
8367 if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
8370 Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
8374 PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
8375 SND_AMOEBA_TURNING_TO_GEM :
8376 SND_AMOEBA_TURNING_TO_ROCK));
8381 static int xy[4][2] =
8389 for (i = 0; i < NUM_DIRECTIONS; i++)
8394 if (!IN_LEV_FIELD(x, y))
8397 if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
8399 PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
8400 SND_AMOEBA_TURNING_TO_GEM :
8401 SND_AMOEBA_TURNING_TO_ROCK));
8408 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
8411 int group_nr = AmoebaNr[ax][ay];
8412 boolean done = FALSE;
8417 printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
8418 printf("AmoebeUmwandelnBD(): This should never happen!\n");
8423 SCAN_PLAYFIELD(x, y)
8425 if (AmoebaNr[x][y] == group_nr &&
8426 (Feld[x][y] == EL_AMOEBA_DEAD ||
8427 Feld[x][y] == EL_BD_AMOEBA ||
8428 Feld[x][y] == EL_AMOEBA_GROWING))
8431 Feld[x][y] = new_element;
8432 InitField(x, y, FALSE);
8433 TEST_DrawLevelField(x, y);
8439 PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
8440 SND_BD_AMOEBA_TURNING_TO_ROCK :
8441 SND_BD_AMOEBA_TURNING_TO_GEM));
8444 void AmoebeWaechst(int x, int y)
8446 static unsigned int sound_delay = 0;
8447 static unsigned int sound_delay_value = 0;
8449 if (!MovDelay[x][y]) /* start new growing cycle */
8453 if (DelayReached(&sound_delay, sound_delay_value))
8455 PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
8456 sound_delay_value = 30;
8460 if (MovDelay[x][y]) /* wait some time before growing bigger */
8463 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8465 int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
8466 6 - MovDelay[x][y]);
8468 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
8471 if (!MovDelay[x][y])
8473 Feld[x][y] = Store[x][y];
8475 TEST_DrawLevelField(x, y);
8480 void AmoebaDisappearing(int x, int y)
8482 static unsigned int sound_delay = 0;
8483 static unsigned int sound_delay_value = 0;
8485 if (!MovDelay[x][y]) /* start new shrinking cycle */
8489 if (DelayReached(&sound_delay, sound_delay_value))
8490 sound_delay_value = 30;
8493 if (MovDelay[x][y]) /* wait some time before shrinking */
8496 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8498 int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
8499 6 - MovDelay[x][y]);
8501 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
8504 if (!MovDelay[x][y])
8506 Feld[x][y] = EL_EMPTY;
8507 TEST_DrawLevelField(x, y);
8509 /* don't let mole enter this field in this cycle;
8510 (give priority to objects falling to this field from above) */
8516 void AmoebeAbleger(int ax, int ay)
8519 int element = Feld[ax][ay];
8520 int graphic = el2img(element);
8521 int newax = ax, neway = ay;
8522 boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
8523 static int xy[4][2] =
8531 if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
8533 Feld[ax][ay] = EL_AMOEBA_DEAD;
8534 TEST_DrawLevelField(ax, ay);
8538 if (IS_ANIMATED(graphic))
8539 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
8541 if (!MovDelay[ax][ay]) /* start making new amoeba field */
8542 MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
8544 if (MovDelay[ax][ay]) /* wait some time before making new amoeba */
8547 if (MovDelay[ax][ay])
8551 if (can_drop) /* EL_AMOEBA_WET or EL_EMC_DRIPPER */
8554 int x = ax + xy[start][0];
8555 int y = ay + xy[start][1];
8557 if (!IN_LEV_FIELD(x, y))
8560 if (IS_FREE(x, y) ||
8561 CAN_GROW_INTO(Feld[x][y]) ||
8562 Feld[x][y] == EL_QUICKSAND_EMPTY ||
8563 Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
8569 if (newax == ax && neway == ay)
8572 else /* normal or "filled" (BD style) amoeba */
8575 boolean waiting_for_player = FALSE;
8577 for (i = 0; i < NUM_DIRECTIONS; i++)
8579 int j = (start + i) % 4;
8580 int x = ax + xy[j][0];
8581 int y = ay + xy[j][1];
8583 if (!IN_LEV_FIELD(x, y))
8586 if (IS_FREE(x, y) ||
8587 CAN_GROW_INTO(Feld[x][y]) ||
8588 Feld[x][y] == EL_QUICKSAND_EMPTY ||
8589 Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
8595 else if (IS_PLAYER(x, y))
8596 waiting_for_player = TRUE;
8599 if (newax == ax && neway == ay) /* amoeba cannot grow */
8601 if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
8603 Feld[ax][ay] = EL_AMOEBA_DEAD;
8604 TEST_DrawLevelField(ax, ay);
8605 AmoebaCnt[AmoebaNr[ax][ay]]--;
8607 if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0) /* amoeba is completely dead */
8609 if (element == EL_AMOEBA_FULL)
8610 AmoebeUmwandeln(ax, ay);
8611 else if (element == EL_BD_AMOEBA)
8612 AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
8617 else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
8619 /* amoeba gets larger by growing in some direction */
8621 int new_group_nr = AmoebaNr[ax][ay];
8624 if (new_group_nr == 0)
8626 printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
8627 printf("AmoebeAbleger(): This should never happen!\n");
8632 AmoebaNr[newax][neway] = new_group_nr;
8633 AmoebaCnt[new_group_nr]++;
8634 AmoebaCnt2[new_group_nr]++;
8636 /* if amoeba touches other amoeba(s) after growing, unify them */
8637 AmoebenVereinigen(newax, neway);
8639 if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
8641 AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
8647 if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
8648 (neway == lev_fieldy - 1 && newax != ax))
8650 Feld[newax][neway] = EL_AMOEBA_GROWING; /* creation of new amoeba */
8651 Store[newax][neway] = element;
8653 else if (neway == ay || element == EL_EMC_DRIPPER)
8655 Feld[newax][neway] = EL_AMOEBA_DROP; /* drop left/right of amoeba */
8657 PlayLevelSoundAction(newax, neway, ACTION_GROWING);
8661 InitMovingField(ax, ay, MV_DOWN); /* drop dripping from amoeba */
8662 Feld[ax][ay] = EL_AMOEBA_DROPPING;
8663 Store[ax][ay] = EL_AMOEBA_DROP;
8664 ContinueMoving(ax, ay);
8668 TEST_DrawLevelField(newax, neway);
8671 void Life(int ax, int ay)
8675 int element = Feld[ax][ay];
8676 int graphic = el2img(element);
8677 int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
8679 boolean changed = FALSE;
8681 if (IS_ANIMATED(graphic))
8682 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
8687 if (!MovDelay[ax][ay]) /* start new "game of life" cycle */
8688 MovDelay[ax][ay] = life_time;
8690 if (MovDelay[ax][ay]) /* wait some time before next cycle */
8693 if (MovDelay[ax][ay])
8697 for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
8699 int xx = ax+x1, yy = ay+y1;
8702 if (!IN_LEV_FIELD(xx, yy))
8705 for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
8707 int x = xx+x2, y = yy+y2;
8709 if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
8712 if (((Feld[x][y] == element ||
8713 (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
8715 (IS_FREE(x, y) && Stop[x][y]))
8719 if (xx == ax && yy == ay) /* field in the middle */
8721 if (nachbarn < life_parameter[0] ||
8722 nachbarn > life_parameter[1])
8724 Feld[xx][yy] = EL_EMPTY;
8726 TEST_DrawLevelField(xx, yy);
8727 Stop[xx][yy] = TRUE;
8731 else if (IS_FREE(xx, yy) || CAN_GROW_INTO(Feld[xx][yy]))
8732 { /* free border field */
8733 if (nachbarn >= life_parameter[2] &&
8734 nachbarn <= life_parameter[3])
8736 Feld[xx][yy] = element;
8737 MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
8739 TEST_DrawLevelField(xx, yy);
8740 Stop[xx][yy] = TRUE;
8747 PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
8748 SND_GAME_OF_LIFE_GROWING);
8751 static void InitRobotWheel(int x, int y)
8753 ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
8756 static void RunRobotWheel(int x, int y)
8758 PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
8761 static void StopRobotWheel(int x, int y)
8763 if (ZX == x && ZY == y)
8767 game.robot_wheel_active = FALSE;
8771 static void InitTimegateWheel(int x, int y)
8773 ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
8776 static void RunTimegateWheel(int x, int y)
8778 PlayLevelSound(x, y, SND_CLASS_TIMEGATE_SWITCH_ACTIVE);
8781 static void InitMagicBallDelay(int x, int y)
8783 ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
8786 static void ActivateMagicBall(int bx, int by)
8790 if (level.ball_random)
8792 int pos_border = RND(8); /* select one of the eight border elements */
8793 int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
8794 int xx = pos_content % 3;
8795 int yy = pos_content / 3;
8800 if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
8801 CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
8805 for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
8807 int xx = x - bx + 1;
8808 int yy = y - by + 1;
8810 if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
8811 CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
8815 game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
8818 void CheckExit(int x, int y)
8820 if (local_player->gems_still_needed > 0 ||
8821 local_player->sokobanfields_still_needed > 0 ||
8822 local_player->lights_still_needed > 0)
8824 int element = Feld[x][y];
8825 int graphic = el2img(element);
8827 if (IS_ANIMATED(graphic))
8828 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8833 if (AllPlayersGone) /* do not re-open exit door closed after last player */
8836 Feld[x][y] = EL_EXIT_OPENING;
8838 PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
8841 void CheckExitEM(int x, int y)
8843 if (local_player->gems_still_needed > 0 ||
8844 local_player->sokobanfields_still_needed > 0 ||
8845 local_player->lights_still_needed > 0)
8847 int element = Feld[x][y];
8848 int graphic = el2img(element);
8850 if (IS_ANIMATED(graphic))
8851 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8856 if (AllPlayersGone) /* do not re-open exit door closed after last player */
8859 Feld[x][y] = EL_EM_EXIT_OPENING;
8861 PlayLevelSoundNearest(x, y, SND_CLASS_EM_EXIT_OPENING);
8864 void CheckExitSteel(int x, int y)
8866 if (local_player->gems_still_needed > 0 ||
8867 local_player->sokobanfields_still_needed > 0 ||
8868 local_player->lights_still_needed > 0)
8870 int element = Feld[x][y];
8871 int graphic = el2img(element);
8873 if (IS_ANIMATED(graphic))
8874 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8879 if (AllPlayersGone) /* do not re-open exit door closed after last player */
8882 Feld[x][y] = EL_STEEL_EXIT_OPENING;
8884 PlayLevelSoundNearest(x, y, SND_CLASS_STEEL_EXIT_OPENING);
8887 void CheckExitSteelEM(int x, int y)
8889 if (local_player->gems_still_needed > 0 ||
8890 local_player->sokobanfields_still_needed > 0 ||
8891 local_player->lights_still_needed > 0)
8893 int element = Feld[x][y];
8894 int graphic = el2img(element);
8896 if (IS_ANIMATED(graphic))
8897 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8902 if (AllPlayersGone) /* do not re-open exit door closed after last player */
8905 Feld[x][y] = EL_EM_STEEL_EXIT_OPENING;
8907 PlayLevelSoundNearest(x, y, SND_CLASS_EM_STEEL_EXIT_OPENING);
8910 void CheckExitSP(int x, int y)
8912 if (local_player->gems_still_needed > 0)
8914 int element = Feld[x][y];
8915 int graphic = el2img(element);
8917 if (IS_ANIMATED(graphic))
8918 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8923 if (AllPlayersGone) /* do not re-open exit door closed after last player */
8926 Feld[x][y] = EL_SP_EXIT_OPENING;
8928 PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
8931 static void CloseAllOpenTimegates()
8935 SCAN_PLAYFIELD(x, y)
8937 int element = Feld[x][y];
8939 if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
8941 Feld[x][y] = EL_TIMEGATE_CLOSING;
8943 PlayLevelSoundAction(x, y, ACTION_CLOSING);
8948 void DrawTwinkleOnField(int x, int y)
8950 if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
8953 if (Feld[x][y] == EL_BD_DIAMOND)
8956 if (MovDelay[x][y] == 0) /* next animation frame */
8957 MovDelay[x][y] = 11 * !GetSimpleRandom(500);
8959 if (MovDelay[x][y] != 0) /* wait some time before next frame */
8963 DrawLevelElementAnimation(x, y, Feld[x][y]);
8965 if (MovDelay[x][y] != 0)
8967 int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
8968 10 - MovDelay[x][y]);
8970 DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
8975 void MauerWaechst(int x, int y)
8979 if (!MovDelay[x][y]) /* next animation frame */
8980 MovDelay[x][y] = 3 * delay;
8982 if (MovDelay[x][y]) /* wait some time before next frame */
8986 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8988 int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
8989 int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
8991 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
8994 if (!MovDelay[x][y])
8996 if (MovDir[x][y] == MV_LEFT)
8998 if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
8999 TEST_DrawLevelField(x - 1, y);
9001 else if (MovDir[x][y] == MV_RIGHT)
9003 if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
9004 TEST_DrawLevelField(x + 1, y);
9006 else if (MovDir[x][y] == MV_UP)
9008 if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
9009 TEST_DrawLevelField(x, y - 1);
9013 if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
9014 TEST_DrawLevelField(x, y + 1);
9017 Feld[x][y] = Store[x][y];
9019 GfxDir[x][y] = MovDir[x][y] = MV_NONE;
9020 TEST_DrawLevelField(x, y);
9025 void MauerAbleger(int ax, int ay)
9027 int element = Feld[ax][ay];
9028 int graphic = el2img(element);
9029 boolean oben_frei = FALSE, unten_frei = FALSE;
9030 boolean links_frei = FALSE, rechts_frei = FALSE;
9031 boolean oben_massiv = FALSE, unten_massiv = FALSE;
9032 boolean links_massiv = FALSE, rechts_massiv = FALSE;
9033 boolean new_wall = FALSE;
9035 if (IS_ANIMATED(graphic))
9036 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9038 if (!MovDelay[ax][ay]) /* start building new wall */
9039 MovDelay[ax][ay] = 6;
9041 if (MovDelay[ax][ay]) /* wait some time before building new wall */
9044 if (MovDelay[ax][ay])
9048 if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9050 if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9052 if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9054 if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9057 if (element == EL_EXPANDABLE_WALL_VERTICAL ||
9058 element == EL_EXPANDABLE_WALL_ANY)
9062 Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
9063 Store[ax][ay-1] = element;
9064 GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9065 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9066 DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9067 IMG_EXPANDABLE_WALL_GROWING_UP, 0);
9072 Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
9073 Store[ax][ay+1] = element;
9074 GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9075 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9076 DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9077 IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
9082 if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9083 element == EL_EXPANDABLE_WALL_ANY ||
9084 element == EL_EXPANDABLE_WALL ||
9085 element == EL_BD_EXPANDABLE_WALL)
9089 Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
9090 Store[ax-1][ay] = element;
9091 GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9092 if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9093 DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9094 IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
9100 Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
9101 Store[ax+1][ay] = element;
9102 GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9103 if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9104 DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9105 IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
9110 if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
9111 TEST_DrawLevelField(ax, ay);
9113 if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
9115 if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
9116 unten_massiv = TRUE;
9117 if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
9118 links_massiv = TRUE;
9119 if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
9120 rechts_massiv = TRUE;
9122 if (((oben_massiv && unten_massiv) ||
9123 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9124 element == EL_EXPANDABLE_WALL) &&
9125 ((links_massiv && rechts_massiv) ||
9126 element == EL_EXPANDABLE_WALL_VERTICAL))
9127 Feld[ax][ay] = EL_WALL;
9130 PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9133 void MauerAblegerStahl(int ax, int ay)
9135 int element = Feld[ax][ay];
9136 int graphic = el2img(element);
9137 boolean oben_frei = FALSE, unten_frei = FALSE;
9138 boolean links_frei = FALSE, rechts_frei = FALSE;
9139 boolean oben_massiv = FALSE, unten_massiv = FALSE;
9140 boolean links_massiv = FALSE, rechts_massiv = FALSE;
9141 boolean new_wall = FALSE;
9143 if (IS_ANIMATED(graphic))
9144 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9146 if (!MovDelay[ax][ay]) /* start building new wall */
9147 MovDelay[ax][ay] = 6;
9149 if (MovDelay[ax][ay]) /* wait some time before building new wall */
9152 if (MovDelay[ax][ay])
9156 if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9158 if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9160 if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9162 if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9165 if (element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
9166 element == EL_EXPANDABLE_STEELWALL_ANY)
9170 Feld[ax][ay-1] = EL_EXPANDABLE_STEELWALL_GROWING;
9171 Store[ax][ay-1] = element;
9172 GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9173 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9174 DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9175 IMG_EXPANDABLE_STEELWALL_GROWING_UP, 0);
9180 Feld[ax][ay+1] = EL_EXPANDABLE_STEELWALL_GROWING;
9181 Store[ax][ay+1] = element;
9182 GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9183 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9184 DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9185 IMG_EXPANDABLE_STEELWALL_GROWING_DOWN, 0);
9190 if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
9191 element == EL_EXPANDABLE_STEELWALL_ANY)
9195 Feld[ax-1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9196 Store[ax-1][ay] = element;
9197 GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9198 if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9199 DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9200 IMG_EXPANDABLE_STEELWALL_GROWING_LEFT, 0);
9206 Feld[ax+1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9207 Store[ax+1][ay] = element;
9208 GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9209 if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9210 DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9211 IMG_EXPANDABLE_STEELWALL_GROWING_RIGHT, 0);
9216 if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
9218 if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
9219 unten_massiv = TRUE;
9220 if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
9221 links_massiv = TRUE;
9222 if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
9223 rechts_massiv = TRUE;
9225 if (((oben_massiv && unten_massiv) ||
9226 element == EL_EXPANDABLE_STEELWALL_HORIZONTAL) &&
9227 ((links_massiv && rechts_massiv) ||
9228 element == EL_EXPANDABLE_STEELWALL_VERTICAL))
9229 Feld[ax][ay] = EL_STEELWALL;
9232 PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9235 void CheckForDragon(int x, int y)
9238 boolean dragon_found = FALSE;
9239 static int xy[4][2] =
9247 for (i = 0; i < NUM_DIRECTIONS; i++)
9249 for (j = 0; j < 4; j++)
9251 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9253 if (IN_LEV_FIELD(xx, yy) &&
9254 (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
9256 if (Feld[xx][yy] == EL_DRAGON)
9257 dragon_found = TRUE;
9266 for (i = 0; i < NUM_DIRECTIONS; i++)
9268 for (j = 0; j < 3; j++)
9270 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9272 if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
9274 Feld[xx][yy] = EL_EMPTY;
9275 TEST_DrawLevelField(xx, yy);
9284 static void InitBuggyBase(int x, int y)
9286 int element = Feld[x][y];
9287 int activating_delay = FRAMES_PER_SECOND / 4;
9290 (element == EL_SP_BUGGY_BASE ?
9291 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
9292 element == EL_SP_BUGGY_BASE_ACTIVATING ?
9294 element == EL_SP_BUGGY_BASE_ACTIVE ?
9295 1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
9298 static void WarnBuggyBase(int x, int y)
9301 static int xy[4][2] =
9309 for (i = 0; i < NUM_DIRECTIONS; i++)
9311 int xx = x + xy[i][0];
9312 int yy = y + xy[i][1];
9314 if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
9316 PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
9323 static void InitTrap(int x, int y)
9325 ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
9328 static void ActivateTrap(int x, int y)
9330 PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
9333 static void ChangeActiveTrap(int x, int y)
9335 int graphic = IMG_TRAP_ACTIVE;
9337 /* if new animation frame was drawn, correct crumbled sand border */
9338 if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
9339 TEST_DrawLevelFieldCrumbled(x, y);
9342 static int getSpecialActionElement(int element, int number, int base_element)
9344 return (element != EL_EMPTY ? element :
9345 number != -1 ? base_element + number - 1 :
9349 static int getModifiedActionNumber(int value_old, int operator, int operand,
9350 int value_min, int value_max)
9352 int value_new = (operator == CA_MODE_SET ? operand :
9353 operator == CA_MODE_ADD ? value_old + operand :
9354 operator == CA_MODE_SUBTRACT ? value_old - operand :
9355 operator == CA_MODE_MULTIPLY ? value_old * operand :
9356 operator == CA_MODE_DIVIDE ? value_old / MAX(1, operand) :
9357 operator == CA_MODE_MODULO ? value_old % MAX(1, operand) :
9360 return (value_new < value_min ? value_min :
9361 value_new > value_max ? value_max :
9365 static void ExecuteCustomElementAction(int x, int y, int element, int page)
9367 struct ElementInfo *ei = &element_info[element];
9368 struct ElementChangeInfo *change = &ei->change_page[page];
9369 int target_element = change->target_element;
9370 int action_type = change->action_type;
9371 int action_mode = change->action_mode;
9372 int action_arg = change->action_arg;
9373 int action_element = change->action_element;
9376 if (!change->has_action)
9379 /* ---------- determine action paramater values -------------------------- */
9381 int level_time_value =
9382 (level.time > 0 ? TimeLeft :
9385 int action_arg_element_raw =
9386 (action_arg == CA_ARG_PLAYER_TRIGGER ? change->actual_trigger_player :
9387 action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
9388 action_arg == CA_ARG_ELEMENT_TARGET ? change->target_element :
9389 action_arg == CA_ARG_ELEMENT_ACTION ? change->action_element :
9390 action_arg == CA_ARG_INVENTORY_RM_TRIGGER ? change->actual_trigger_element:
9391 action_arg == CA_ARG_INVENTORY_RM_TARGET ? change->target_element :
9392 action_arg == CA_ARG_INVENTORY_RM_ACTION ? change->action_element :
9394 int action_arg_element = GetElementFromGroupElement(action_arg_element_raw);
9396 int action_arg_direction =
9397 (action_arg >= CA_ARG_DIRECTION_LEFT &&
9398 action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
9399 action_arg == CA_ARG_DIRECTION_TRIGGER ?
9400 change->actual_trigger_side :
9401 action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
9402 MV_DIR_OPPOSITE(change->actual_trigger_side) :
9405 int action_arg_number_min =
9406 (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
9409 int action_arg_number_max =
9410 (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
9411 action_type == CA_SET_LEVEL_GEMS ? 999 :
9412 action_type == CA_SET_LEVEL_TIME ? 9999 :
9413 action_type == CA_SET_LEVEL_SCORE ? 99999 :
9414 action_type == CA_SET_CE_VALUE ? 9999 :
9415 action_type == CA_SET_CE_SCORE ? 9999 :
9418 int action_arg_number_reset =
9419 (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize[0] :
9420 action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
9421 action_type == CA_SET_LEVEL_TIME ? level.time :
9422 action_type == CA_SET_LEVEL_SCORE ? 0 :
9423 action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
9424 action_type == CA_SET_CE_SCORE ? 0 :
9427 int action_arg_number =
9428 (action_arg <= CA_ARG_MAX ? action_arg :
9429 action_arg >= CA_ARG_SPEED_NOT_MOVING &&
9430 action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
9431 action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
9432 action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
9433 action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
9434 action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
9435 action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
9436 action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
9437 action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
9438 action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
9439 action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? local_player->gems_still_needed :
9440 action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? local_player->score :
9441 action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
9442 action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
9443 action_arg == CA_ARG_ELEMENT_CV_ACTION ? GET_NEW_CE_VALUE(action_element):
9444 action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
9445 action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
9446 action_arg == CA_ARG_ELEMENT_CS_ACTION ? GET_CE_SCORE(action_element) :
9447 action_arg == CA_ARG_ELEMENT_NR_TARGET ? change->target_element :
9448 action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
9449 action_arg == CA_ARG_ELEMENT_NR_ACTION ? change->action_element :
9452 int action_arg_number_old =
9453 (action_type == CA_SET_LEVEL_GEMS ? local_player->gems_still_needed :
9454 action_type == CA_SET_LEVEL_TIME ? TimeLeft :
9455 action_type == CA_SET_LEVEL_SCORE ? local_player->score :
9456 action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
9457 action_type == CA_SET_CE_SCORE ? ei->collect_score :
9460 int action_arg_number_new =
9461 getModifiedActionNumber(action_arg_number_old,
9462 action_mode, action_arg_number,
9463 action_arg_number_min, action_arg_number_max);
9465 int trigger_player_bits =
9466 (change->actual_trigger_player_bits != CH_PLAYER_NONE ?
9467 change->actual_trigger_player_bits : change->trigger_player);
9469 int action_arg_player_bits =
9470 (action_arg >= CA_ARG_PLAYER_1 &&
9471 action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
9472 action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
9473 action_arg == CA_ARG_PLAYER_ACTION ? 1 << GET_PLAYER_NR(action_element) :
9476 /* ---------- execute action -------------------------------------------- */
9478 switch (action_type)
9485 /* ---------- level actions ------------------------------------------- */
9487 case CA_RESTART_LEVEL:
9489 game.restart_level = TRUE;
9494 case CA_SHOW_ENVELOPE:
9496 int element = getSpecialActionElement(action_arg_element,
9497 action_arg_number, EL_ENVELOPE_1);
9499 if (IS_ENVELOPE(element))
9500 local_player->show_envelope = element;
9505 case CA_SET_LEVEL_TIME:
9507 if (level.time > 0) /* only modify limited time value */
9509 TimeLeft = action_arg_number_new;
9511 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
9513 DisplayGameControlValues();
9515 if (!TimeLeft && setup.time_limit)
9516 for (i = 0; i < MAX_PLAYERS; i++)
9517 KillPlayer(&stored_player[i]);
9523 case CA_SET_LEVEL_SCORE:
9525 local_player->score = action_arg_number_new;
9527 game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
9529 DisplayGameControlValues();
9534 case CA_SET_LEVEL_GEMS:
9536 local_player->gems_still_needed = action_arg_number_new;
9538 game_panel_controls[GAME_PANEL_GEMS].value =
9539 local_player->gems_still_needed;
9541 DisplayGameControlValues();
9546 case CA_SET_LEVEL_WIND:
9548 game.wind_direction = action_arg_direction;
9553 case CA_SET_LEVEL_RANDOM_SEED:
9555 /* ensure that setting a new random seed while playing is predictable */
9556 InitRND(action_arg_number_new ? action_arg_number_new : RND(1000000) + 1);
9561 /* ---------- player actions ------------------------------------------ */
9563 case CA_MOVE_PLAYER:
9565 /* automatically move to the next field in specified direction */
9566 for (i = 0; i < MAX_PLAYERS; i++)
9567 if (trigger_player_bits & (1 << i))
9568 stored_player[i].programmed_action = action_arg_direction;
9573 case CA_EXIT_PLAYER:
9575 for (i = 0; i < MAX_PLAYERS; i++)
9576 if (action_arg_player_bits & (1 << i))
9577 PlayerWins(&stored_player[i]);
9582 case CA_KILL_PLAYER:
9584 for (i = 0; i < MAX_PLAYERS; i++)
9585 if (action_arg_player_bits & (1 << i))
9586 KillPlayer(&stored_player[i]);
9591 case CA_SET_PLAYER_KEYS:
9593 int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
9594 int element = getSpecialActionElement(action_arg_element,
9595 action_arg_number, EL_KEY_1);
9597 if (IS_KEY(element))
9599 for (i = 0; i < MAX_PLAYERS; i++)
9601 if (trigger_player_bits & (1 << i))
9603 stored_player[i].key[KEY_NR(element)] = key_state;
9605 DrawGameDoorValues();
9613 case CA_SET_PLAYER_SPEED:
9615 for (i = 0; i < MAX_PLAYERS; i++)
9617 if (trigger_player_bits & (1 << i))
9619 int move_stepsize = TILEX / stored_player[i].move_delay_value;
9621 if (action_arg == CA_ARG_SPEED_FASTER &&
9622 stored_player[i].cannot_move)
9624 action_arg_number = STEPSIZE_VERY_SLOW;
9626 else if (action_arg == CA_ARG_SPEED_SLOWER ||
9627 action_arg == CA_ARG_SPEED_FASTER)
9629 action_arg_number = 2;
9630 action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
9633 else if (action_arg == CA_ARG_NUMBER_RESET)
9635 action_arg_number = level.initial_player_stepsize[i];
9639 getModifiedActionNumber(move_stepsize,
9642 action_arg_number_min,
9643 action_arg_number_max);
9645 SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
9652 case CA_SET_PLAYER_SHIELD:
9654 for (i = 0; i < MAX_PLAYERS; i++)
9656 if (trigger_player_bits & (1 << i))
9658 if (action_arg == CA_ARG_SHIELD_OFF)
9660 stored_player[i].shield_normal_time_left = 0;
9661 stored_player[i].shield_deadly_time_left = 0;
9663 else if (action_arg == CA_ARG_SHIELD_NORMAL)
9665 stored_player[i].shield_normal_time_left = 999999;
9667 else if (action_arg == CA_ARG_SHIELD_DEADLY)
9669 stored_player[i].shield_normal_time_left = 999999;
9670 stored_player[i].shield_deadly_time_left = 999999;
9678 case CA_SET_PLAYER_GRAVITY:
9680 for (i = 0; i < MAX_PLAYERS; i++)
9682 if (trigger_player_bits & (1 << i))
9684 stored_player[i].gravity =
9685 (action_arg == CA_ARG_GRAVITY_OFF ? FALSE :
9686 action_arg == CA_ARG_GRAVITY_ON ? TRUE :
9687 action_arg == CA_ARG_GRAVITY_TOGGLE ? !stored_player[i].gravity :
9688 stored_player[i].gravity);
9695 case CA_SET_PLAYER_ARTWORK:
9697 for (i = 0; i < MAX_PLAYERS; i++)
9699 if (trigger_player_bits & (1 << i))
9701 int artwork_element = action_arg_element;
9703 if (action_arg == CA_ARG_ELEMENT_RESET)
9705 (level.use_artwork_element[i] ? level.artwork_element[i] :
9706 stored_player[i].element_nr);
9708 if (stored_player[i].artwork_element != artwork_element)
9709 stored_player[i].Frame = 0;
9711 stored_player[i].artwork_element = artwork_element;
9713 SetPlayerWaiting(&stored_player[i], FALSE);
9715 /* set number of special actions for bored and sleeping animation */
9716 stored_player[i].num_special_action_bored =
9717 get_num_special_action(artwork_element,
9718 ACTION_BORING_1, ACTION_BORING_LAST);
9719 stored_player[i].num_special_action_sleeping =
9720 get_num_special_action(artwork_element,
9721 ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
9728 case CA_SET_PLAYER_INVENTORY:
9730 for (i = 0; i < MAX_PLAYERS; i++)
9732 struct PlayerInfo *player = &stored_player[i];
9735 if (trigger_player_bits & (1 << i))
9737 int inventory_element = action_arg_element;
9739 if (action_arg == CA_ARG_ELEMENT_TARGET ||
9740 action_arg == CA_ARG_ELEMENT_TRIGGER ||
9741 action_arg == CA_ARG_ELEMENT_ACTION)
9743 int element = inventory_element;
9744 int collect_count = element_info[element].collect_count_initial;
9746 if (!IS_CUSTOM_ELEMENT(element))
9749 if (collect_count == 0)
9750 player->inventory_infinite_element = element;
9752 for (k = 0; k < collect_count; k++)
9753 if (player->inventory_size < MAX_INVENTORY_SIZE)
9754 player->inventory_element[player->inventory_size++] =
9757 else if (action_arg == CA_ARG_INVENTORY_RM_TARGET ||
9758 action_arg == CA_ARG_INVENTORY_RM_TRIGGER ||
9759 action_arg == CA_ARG_INVENTORY_RM_ACTION)
9761 if (player->inventory_infinite_element != EL_UNDEFINED &&
9762 IS_EQUAL_OR_IN_GROUP(player->inventory_infinite_element,
9763 action_arg_element_raw))
9764 player->inventory_infinite_element = EL_UNDEFINED;
9766 for (k = 0, j = 0; j < player->inventory_size; j++)
9768 if (!IS_EQUAL_OR_IN_GROUP(player->inventory_element[j],
9769 action_arg_element_raw))
9770 player->inventory_element[k++] = player->inventory_element[j];
9773 player->inventory_size = k;
9775 else if (action_arg == CA_ARG_INVENTORY_RM_FIRST)
9777 if (player->inventory_size > 0)
9779 for (j = 0; j < player->inventory_size - 1; j++)
9780 player->inventory_element[j] = player->inventory_element[j + 1];
9782 player->inventory_size--;
9785 else if (action_arg == CA_ARG_INVENTORY_RM_LAST)
9787 if (player->inventory_size > 0)
9788 player->inventory_size--;
9790 else if (action_arg == CA_ARG_INVENTORY_RM_ALL)
9792 player->inventory_infinite_element = EL_UNDEFINED;
9793 player->inventory_size = 0;
9795 else if (action_arg == CA_ARG_INVENTORY_RESET)
9797 player->inventory_infinite_element = EL_UNDEFINED;
9798 player->inventory_size = 0;
9800 if (level.use_initial_inventory[i])
9802 for (j = 0; j < level.initial_inventory_size[i]; j++)
9804 int element = level.initial_inventory_content[i][j];
9805 int collect_count = element_info[element].collect_count_initial;
9807 if (!IS_CUSTOM_ELEMENT(element))
9810 if (collect_count == 0)
9811 player->inventory_infinite_element = element;
9813 for (k = 0; k < collect_count; k++)
9814 if (player->inventory_size < MAX_INVENTORY_SIZE)
9815 player->inventory_element[player->inventory_size++] =
9826 /* ---------- CE actions ---------------------------------------------- */
9828 case CA_SET_CE_VALUE:
9830 int last_ce_value = CustomValue[x][y];
9832 CustomValue[x][y] = action_arg_number_new;
9834 if (CustomValue[x][y] != last_ce_value)
9836 CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_CHANGES);
9837 CheckTriggeredElementChange(x, y, element, CE_VALUE_CHANGES_OF_X);
9839 if (CustomValue[x][y] == 0)
9841 CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
9842 CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
9849 case CA_SET_CE_SCORE:
9851 int last_ce_score = ei->collect_score;
9853 ei->collect_score = action_arg_number_new;
9855 if (ei->collect_score != last_ce_score)
9857 CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
9858 CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
9860 if (ei->collect_score == 0)
9864 CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
9865 CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
9868 This is a very special case that seems to be a mixture between
9869 CheckElementChange() and CheckTriggeredElementChange(): while
9870 the first one only affects single elements that are triggered
9871 directly, the second one affects multiple elements in the playfield
9872 that are triggered indirectly by another element. This is a third
9873 case: Changing the CE score always affects multiple identical CEs,
9874 so every affected CE must be checked, not only the single CE for
9875 which the CE score was changed in the first place (as every instance
9876 of that CE shares the same CE score, and therefore also can change)!
9878 SCAN_PLAYFIELD(xx, yy)
9880 if (Feld[xx][yy] == element)
9881 CheckElementChange(xx, yy, element, EL_UNDEFINED,
9882 CE_SCORE_GETS_ZERO);
9890 case CA_SET_CE_ARTWORK:
9892 int artwork_element = action_arg_element;
9893 boolean reset_frame = FALSE;
9896 if (action_arg == CA_ARG_ELEMENT_RESET)
9897 artwork_element = (ei->use_gfx_element ? ei->gfx_element_initial :
9900 if (ei->gfx_element != artwork_element)
9903 ei->gfx_element = artwork_element;
9905 SCAN_PLAYFIELD(xx, yy)
9907 if (Feld[xx][yy] == element)
9911 ResetGfxAnimation(xx, yy);
9912 ResetRandomAnimationValue(xx, yy);
9915 TEST_DrawLevelField(xx, yy);
9922 /* ---------- engine actions ------------------------------------------ */
9924 case CA_SET_ENGINE_SCAN_MODE:
9926 InitPlayfieldScanMode(action_arg);
9936 static void CreateFieldExt(int x, int y, int element, boolean is_change)
9938 int old_element = Feld[x][y];
9939 int new_element = GetElementFromGroupElement(element);
9940 int previous_move_direction = MovDir[x][y];
9941 int last_ce_value = CustomValue[x][y];
9942 boolean player_explosion_protected = PLAYER_EXPLOSION_PROTECTED(x, y);
9943 boolean new_element_is_player = ELEM_IS_PLAYER(new_element);
9944 boolean add_player_onto_element = (new_element_is_player &&
9945 new_element != EL_SOKOBAN_FIELD_PLAYER &&
9946 IS_WALKABLE(old_element));
9948 if (!add_player_onto_element)
9950 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
9951 RemoveMovingField(x, y);
9955 Feld[x][y] = new_element;
9957 if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
9958 MovDir[x][y] = previous_move_direction;
9960 if (element_info[new_element].use_last_ce_value)
9961 CustomValue[x][y] = last_ce_value;
9963 InitField_WithBug1(x, y, FALSE);
9965 new_element = Feld[x][y]; /* element may have changed */
9967 ResetGfxAnimation(x, y);
9968 ResetRandomAnimationValue(x, y);
9970 TEST_DrawLevelField(x, y);
9972 if (GFX_CRUMBLED(new_element))
9973 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
9976 /* check if element under the player changes from accessible to unaccessible
9977 (needed for special case of dropping element which then changes) */
9978 /* (must be checked after creating new element for walkable group elements) */
9979 if (IS_PLAYER(x, y) && !player_explosion_protected &&
9980 IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
9987 /* "ChangeCount" not set yet to allow "entered by player" change one time */
9988 if (new_element_is_player)
9989 RelocatePlayer(x, y, new_element);
9992 ChangeCount[x][y]++; /* count number of changes in the same frame */
9994 TestIfBadThingTouchesPlayer(x, y);
9995 TestIfPlayerTouchesCustomElement(x, y);
9996 TestIfElementTouchesCustomElement(x, y);
9999 static void CreateField(int x, int y, int element)
10001 CreateFieldExt(x, y, element, FALSE);
10004 static void CreateElementFromChange(int x, int y, int element)
10006 element = GET_VALID_RUNTIME_ELEMENT(element);
10008 if (game.engine_version >= VERSION_IDENT(3,2,0,7))
10010 int old_element = Feld[x][y];
10012 /* prevent changed element from moving in same engine frame
10013 unless both old and new element can either fall or move */
10014 if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
10015 (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
10019 CreateFieldExt(x, y, element, TRUE);
10022 static boolean ChangeElement(int x, int y, int element, int page)
10024 struct ElementInfo *ei = &element_info[element];
10025 struct ElementChangeInfo *change = &ei->change_page[page];
10026 int ce_value = CustomValue[x][y];
10027 int ce_score = ei->collect_score;
10028 int target_element;
10029 int old_element = Feld[x][y];
10031 /* always use default change event to prevent running into a loop */
10032 if (ChangeEvent[x][y] == -1)
10033 ChangeEvent[x][y] = CE_DELAY;
10035 if (ChangeEvent[x][y] == CE_DELAY)
10037 /* reset actual trigger element, trigger player and action element */
10038 change->actual_trigger_element = EL_EMPTY;
10039 change->actual_trigger_player = EL_EMPTY;
10040 change->actual_trigger_player_bits = CH_PLAYER_NONE;
10041 change->actual_trigger_side = CH_SIDE_NONE;
10042 change->actual_trigger_ce_value = 0;
10043 change->actual_trigger_ce_score = 0;
10046 /* do not change elements more than a specified maximum number of changes */
10047 if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
10050 ChangeCount[x][y]++; /* count number of changes in the same frame */
10052 if (change->explode)
10059 if (change->use_target_content)
10061 boolean complete_replace = TRUE;
10062 boolean can_replace[3][3];
10065 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10068 boolean is_walkable;
10069 boolean is_diggable;
10070 boolean is_collectible;
10071 boolean is_removable;
10072 boolean is_destructible;
10073 int ex = x + xx - 1;
10074 int ey = y + yy - 1;
10075 int content_element = change->target_content.e[xx][yy];
10078 can_replace[xx][yy] = TRUE;
10080 if (ex == x && ey == y) /* do not check changing element itself */
10083 if (content_element == EL_EMPTY_SPACE)
10085 can_replace[xx][yy] = FALSE; /* do not replace border with space */
10090 if (!IN_LEV_FIELD(ex, ey))
10092 can_replace[xx][yy] = FALSE;
10093 complete_replace = FALSE;
10100 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10101 e = MovingOrBlocked2Element(ex, ey);
10103 is_empty = (IS_FREE(ex, ey) ||
10104 (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
10106 is_walkable = (is_empty || IS_WALKABLE(e));
10107 is_diggable = (is_empty || IS_DIGGABLE(e));
10108 is_collectible = (is_empty || IS_COLLECTIBLE(e));
10109 is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
10110 is_removable = (is_diggable || is_collectible);
10112 can_replace[xx][yy] =
10113 (((change->replace_when == CP_WHEN_EMPTY && is_empty) ||
10114 (change->replace_when == CP_WHEN_WALKABLE && is_walkable) ||
10115 (change->replace_when == CP_WHEN_DIGGABLE && is_diggable) ||
10116 (change->replace_when == CP_WHEN_COLLECTIBLE && is_collectible) ||
10117 (change->replace_when == CP_WHEN_REMOVABLE && is_removable) ||
10118 (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
10119 !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
10121 if (!can_replace[xx][yy])
10122 complete_replace = FALSE;
10125 if (!change->only_if_complete || complete_replace)
10127 boolean something_has_changed = FALSE;
10129 if (change->only_if_complete && change->use_random_replace &&
10130 RND(100) < change->random_percentage)
10133 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10135 int ex = x + xx - 1;
10136 int ey = y + yy - 1;
10137 int content_element;
10139 if (can_replace[xx][yy] && (!change->use_random_replace ||
10140 RND(100) < change->random_percentage))
10142 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10143 RemoveMovingField(ex, ey);
10145 ChangeEvent[ex][ey] = ChangeEvent[x][y];
10147 content_element = change->target_content.e[xx][yy];
10148 target_element = GET_TARGET_ELEMENT(element, content_element, change,
10149 ce_value, ce_score);
10151 CreateElementFromChange(ex, ey, target_element);
10153 something_has_changed = TRUE;
10155 /* for symmetry reasons, freeze newly created border elements */
10156 if (ex != x || ey != y)
10157 Stop[ex][ey] = TRUE; /* no more moving in this frame */
10161 if (something_has_changed)
10163 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10164 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10170 target_element = GET_TARGET_ELEMENT(element, change->target_element, change,
10171 ce_value, ce_score);
10173 if (element == EL_DIAGONAL_GROWING ||
10174 element == EL_DIAGONAL_SHRINKING)
10176 target_element = Store[x][y];
10178 Store[x][y] = EL_EMPTY;
10181 CreateElementFromChange(x, y, target_element);
10183 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10184 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10187 /* this uses direct change before indirect change */
10188 CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
10193 static void HandleElementChange(int x, int y, int page)
10195 int element = MovingOrBlocked2Element(x, y);
10196 struct ElementInfo *ei = &element_info[element];
10197 struct ElementChangeInfo *change = &ei->change_page[page];
10198 boolean handle_action_before_change = FALSE;
10201 if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
10202 !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
10205 printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
10206 x, y, element, element_info[element].token_name);
10207 printf("HandleElementChange(): This should never happen!\n");
10212 /* this can happen with classic bombs on walkable, changing elements */
10213 if (!CAN_CHANGE_OR_HAS_ACTION(element))
10218 if (ChangeDelay[x][y] == 0) /* initialize element change */
10220 ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
10222 if (change->can_change)
10224 /* !!! not clear why graphic animation should be reset at all here !!! */
10225 /* !!! UPDATE: but is needed for correct Snake Bite tail animation !!! */
10226 /* when a custom element is about to change (for example by change delay),
10227 do not reset graphic animation when the custom element is moving */
10228 if (!IS_MOVING(x, y))
10230 ResetGfxAnimation(x, y);
10231 ResetRandomAnimationValue(x, y);
10234 if (change->pre_change_function)
10235 change->pre_change_function(x, y);
10239 ChangeDelay[x][y]--;
10241 if (ChangeDelay[x][y] != 0) /* continue element change */
10243 if (change->can_change)
10245 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10247 if (IS_ANIMATED(graphic))
10248 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10250 if (change->change_function)
10251 change->change_function(x, y);
10254 else /* finish element change */
10256 if (ChangePage[x][y] != -1) /* remember page from delayed change */
10258 page = ChangePage[x][y];
10259 ChangePage[x][y] = -1;
10261 change = &ei->change_page[page];
10264 if (IS_MOVING(x, y)) /* never change a running system ;-) */
10266 ChangeDelay[x][y] = 1; /* try change after next move step */
10267 ChangePage[x][y] = page; /* remember page to use for change */
10272 /* special case: set new level random seed before changing element */
10273 if (change->has_action && change->action_type == CA_SET_LEVEL_RANDOM_SEED)
10274 handle_action_before_change = TRUE;
10276 if (change->has_action && handle_action_before_change)
10277 ExecuteCustomElementAction(x, y, element, page);
10279 if (change->can_change)
10281 if (ChangeElement(x, y, element, page))
10283 if (change->post_change_function)
10284 change->post_change_function(x, y);
10288 if (change->has_action && !handle_action_before_change)
10289 ExecuteCustomElementAction(x, y, element, page);
10293 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
10294 int trigger_element,
10296 int trigger_player,
10300 boolean change_done_any = FALSE;
10301 int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
10304 if (!(trigger_events[trigger_element][trigger_event]))
10307 RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
10309 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
10311 int element = EL_CUSTOM_START + i;
10312 boolean change_done = FALSE;
10315 if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
10316 !HAS_ANY_CHANGE_EVENT(element, trigger_event))
10319 for (p = 0; p < element_info[element].num_change_pages; p++)
10321 struct ElementChangeInfo *change = &element_info[element].change_page[p];
10323 if (change->can_change_or_has_action &&
10324 change->has_event[trigger_event] &&
10325 change->trigger_side & trigger_side &&
10326 change->trigger_player & trigger_player &&
10327 change->trigger_page & trigger_page_bits &&
10328 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
10330 change->actual_trigger_element = trigger_element;
10331 change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
10332 change->actual_trigger_player_bits = trigger_player;
10333 change->actual_trigger_side = trigger_side;
10334 change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
10335 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10337 if ((change->can_change && !change_done) || change->has_action)
10341 SCAN_PLAYFIELD(x, y)
10343 if (Feld[x][y] == element)
10345 if (change->can_change && !change_done)
10347 /* if element already changed in this frame, not only prevent
10348 another element change (checked in ChangeElement()), but
10349 also prevent additional element actions for this element */
10351 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
10352 !level.use_action_after_change_bug)
10355 ChangeDelay[x][y] = 1;
10356 ChangeEvent[x][y] = trigger_event;
10358 HandleElementChange(x, y, p);
10360 else if (change->has_action)
10362 /* if element already changed in this frame, not only prevent
10363 another element change (checked in ChangeElement()), but
10364 also prevent additional element actions for this element */
10366 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
10367 !level.use_action_after_change_bug)
10370 ExecuteCustomElementAction(x, y, element, p);
10371 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10376 if (change->can_change)
10378 change_done = TRUE;
10379 change_done_any = TRUE;
10386 RECURSION_LOOP_DETECTION_END();
10388 return change_done_any;
10391 static boolean CheckElementChangeExt(int x, int y,
10393 int trigger_element,
10395 int trigger_player,
10398 boolean change_done = FALSE;
10401 if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
10402 !HAS_ANY_CHANGE_EVENT(element, trigger_event))
10405 if (Feld[x][y] == EL_BLOCKED)
10407 Blocked2Moving(x, y, &x, &y);
10408 element = Feld[x][y];
10411 /* check if element has already changed or is about to change after moving */
10412 if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
10413 Feld[x][y] != element) ||
10415 (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
10416 (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
10417 ChangePage[x][y] != -1)))
10420 RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
10422 for (p = 0; p < element_info[element].num_change_pages; p++)
10424 struct ElementChangeInfo *change = &element_info[element].change_page[p];
10426 /* check trigger element for all events where the element that is checked
10427 for changing interacts with a directly adjacent element -- this is
10428 different to element changes that affect other elements to change on the
10429 whole playfield (which is handeld by CheckTriggeredElementChangeExt()) */
10430 boolean check_trigger_element =
10431 (trigger_event == CE_TOUCHING_X ||
10432 trigger_event == CE_HITTING_X ||
10433 trigger_event == CE_HIT_BY_X ||
10434 trigger_event == CE_DIGGING_X); /* this one was forgotten until 3.2.3 */
10436 if (change->can_change_or_has_action &&
10437 change->has_event[trigger_event] &&
10438 change->trigger_side & trigger_side &&
10439 change->trigger_player & trigger_player &&
10440 (!check_trigger_element ||
10441 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
10443 change->actual_trigger_element = trigger_element;
10444 change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
10445 change->actual_trigger_player_bits = trigger_player;
10446 change->actual_trigger_side = trigger_side;
10447 change->actual_trigger_ce_value = CustomValue[x][y];
10448 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10450 /* special case: trigger element not at (x,y) position for some events */
10451 if (check_trigger_element)
10463 { 0, 0 }, { 0, 0 }, { 0, 0 },
10467 int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
10468 int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
10470 change->actual_trigger_ce_value = CustomValue[xx][yy];
10471 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10474 if (change->can_change && !change_done)
10476 ChangeDelay[x][y] = 1;
10477 ChangeEvent[x][y] = trigger_event;
10479 HandleElementChange(x, y, p);
10481 change_done = TRUE;
10483 else if (change->has_action)
10485 ExecuteCustomElementAction(x, y, element, p);
10486 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10491 RECURSION_LOOP_DETECTION_END();
10493 return change_done;
10496 static void PlayPlayerSound(struct PlayerInfo *player)
10498 int jx = player->jx, jy = player->jy;
10499 int sound_element = player->artwork_element;
10500 int last_action = player->last_action_waiting;
10501 int action = player->action_waiting;
10503 if (player->is_waiting)
10505 if (action != last_action)
10506 PlayLevelSoundElementAction(jx, jy, sound_element, action);
10508 PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
10512 if (action != last_action)
10513 StopSound(element_info[sound_element].sound[last_action]);
10515 if (last_action == ACTION_SLEEPING)
10516 PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
10520 static void PlayAllPlayersSound()
10524 for (i = 0; i < MAX_PLAYERS; i++)
10525 if (stored_player[i].active)
10526 PlayPlayerSound(&stored_player[i]);
10529 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
10531 boolean last_waiting = player->is_waiting;
10532 int move_dir = player->MovDir;
10534 player->dir_waiting = move_dir;
10535 player->last_action_waiting = player->action_waiting;
10539 if (!last_waiting) /* not waiting -> waiting */
10541 player->is_waiting = TRUE;
10543 player->frame_counter_bored =
10545 game.player_boring_delay_fixed +
10546 GetSimpleRandom(game.player_boring_delay_random);
10547 player->frame_counter_sleeping =
10549 game.player_sleeping_delay_fixed +
10550 GetSimpleRandom(game.player_sleeping_delay_random);
10552 InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
10555 if (game.player_sleeping_delay_fixed +
10556 game.player_sleeping_delay_random > 0 &&
10557 player->anim_delay_counter == 0 &&
10558 player->post_delay_counter == 0 &&
10559 FrameCounter >= player->frame_counter_sleeping)
10560 player->is_sleeping = TRUE;
10561 else if (game.player_boring_delay_fixed +
10562 game.player_boring_delay_random > 0 &&
10563 FrameCounter >= player->frame_counter_bored)
10564 player->is_bored = TRUE;
10566 player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
10567 player->is_bored ? ACTION_BORING :
10570 if (player->is_sleeping && player->use_murphy)
10572 /* special case for sleeping Murphy when leaning against non-free tile */
10574 if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
10575 (Feld[player->jx - 1][player->jy] != EL_EMPTY &&
10576 !IS_MOVING(player->jx - 1, player->jy)))
10577 move_dir = MV_LEFT;
10578 else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
10579 (Feld[player->jx + 1][player->jy] != EL_EMPTY &&
10580 !IS_MOVING(player->jx + 1, player->jy)))
10581 move_dir = MV_RIGHT;
10583 player->is_sleeping = FALSE;
10585 player->dir_waiting = move_dir;
10588 if (player->is_sleeping)
10590 if (player->num_special_action_sleeping > 0)
10592 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
10594 int last_special_action = player->special_action_sleeping;
10595 int num_special_action = player->num_special_action_sleeping;
10596 int special_action =
10597 (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
10598 last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
10599 last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
10600 last_special_action + 1 : ACTION_SLEEPING);
10601 int special_graphic =
10602 el_act_dir2img(player->artwork_element, special_action, move_dir);
10604 player->anim_delay_counter =
10605 graphic_info[special_graphic].anim_delay_fixed +
10606 GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
10607 player->post_delay_counter =
10608 graphic_info[special_graphic].post_delay_fixed +
10609 GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
10611 player->special_action_sleeping = special_action;
10614 if (player->anim_delay_counter > 0)
10616 player->action_waiting = player->special_action_sleeping;
10617 player->anim_delay_counter--;
10619 else if (player->post_delay_counter > 0)
10621 player->post_delay_counter--;
10625 else if (player->is_bored)
10627 if (player->num_special_action_bored > 0)
10629 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
10631 int special_action =
10632 ACTION_BORING_1 + GetSimpleRandom(player->num_special_action_bored);
10633 int special_graphic =
10634 el_act_dir2img(player->artwork_element, special_action, move_dir);
10636 player->anim_delay_counter =
10637 graphic_info[special_graphic].anim_delay_fixed +
10638 GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
10639 player->post_delay_counter =
10640 graphic_info[special_graphic].post_delay_fixed +
10641 GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
10643 player->special_action_bored = special_action;
10646 if (player->anim_delay_counter > 0)
10648 player->action_waiting = player->special_action_bored;
10649 player->anim_delay_counter--;
10651 else if (player->post_delay_counter > 0)
10653 player->post_delay_counter--;
10658 else if (last_waiting) /* waiting -> not waiting */
10660 player->is_waiting = FALSE;
10661 player->is_bored = FALSE;
10662 player->is_sleeping = FALSE;
10664 player->frame_counter_bored = -1;
10665 player->frame_counter_sleeping = -1;
10667 player->anim_delay_counter = 0;
10668 player->post_delay_counter = 0;
10670 player->dir_waiting = player->MovDir;
10671 player->action_waiting = ACTION_DEFAULT;
10673 player->special_action_bored = ACTION_DEFAULT;
10674 player->special_action_sleeping = ACTION_DEFAULT;
10678 static void CheckSaveEngineSnapshot(struct PlayerInfo *player)
10680 static boolean player_was_moving = FALSE;
10681 static boolean player_was_snapping = FALSE;
10682 static boolean player_was_dropping = FALSE;
10684 if ((!player->is_moving && player_was_moving) ||
10685 (player->MovPos == 0 && player_was_moving) ||
10686 (player->is_snapping && !player_was_snapping) ||
10687 (player->is_dropping && !player_was_dropping))
10689 SaveEngineSnapshotToList();
10691 player_was_moving = FALSE;
10692 player_was_snapping = TRUE;
10693 player_was_dropping = TRUE;
10697 if (player->is_moving)
10698 player_was_moving = TRUE;
10700 if (!player->is_snapping)
10701 player_was_snapping = FALSE;
10703 if (!player->is_dropping)
10704 player_was_dropping = FALSE;
10708 static void CheckSingleStepMode(struct PlayerInfo *player)
10710 if (tape.single_step && tape.recording && !tape.pausing)
10712 /* as it is called "single step mode", just return to pause mode when the
10713 player stopped moving after one tile (or never starts moving at all) */
10714 if (!player->is_moving && !player->is_pushing)
10716 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
10717 SnapField(player, 0, 0); /* stop snapping */
10721 CheckSaveEngineSnapshot(player);
10724 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
10726 int left = player_action & JOY_LEFT;
10727 int right = player_action & JOY_RIGHT;
10728 int up = player_action & JOY_UP;
10729 int down = player_action & JOY_DOWN;
10730 int button1 = player_action & JOY_BUTTON_1;
10731 int button2 = player_action & JOY_BUTTON_2;
10732 int dx = (left ? -1 : right ? 1 : 0);
10733 int dy = (up ? -1 : down ? 1 : 0);
10735 if (!player->active || tape.pausing)
10741 SnapField(player, dx, dy);
10745 DropElement(player);
10747 MovePlayer(player, dx, dy);
10750 CheckSingleStepMode(player);
10752 SetPlayerWaiting(player, FALSE);
10754 return player_action;
10758 /* no actions for this player (no input at player's configured device) */
10760 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
10761 SnapField(player, 0, 0);
10762 CheckGravityMovementWhenNotMoving(player);
10764 if (player->MovPos == 0)
10765 SetPlayerWaiting(player, TRUE);
10767 if (player->MovPos == 0) /* needed for tape.playing */
10768 player->is_moving = FALSE;
10770 player->is_dropping = FALSE;
10771 player->is_dropping_pressed = FALSE;
10772 player->drop_pressed_delay = 0;
10774 CheckSingleStepMode(player);
10780 static void CheckLevelTime()
10784 /* !!! SAME CODE AS IN "GameActions()" -- FIX THIS !!! */
10785 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
10787 if (level.native_em_level->lev->home == 0) /* all players at home */
10789 PlayerWins(local_player);
10791 AllPlayersGone = TRUE;
10793 level.native_em_level->lev->home = -1;
10796 if (level.native_em_level->ply[0]->alive == 0 &&
10797 level.native_em_level->ply[1]->alive == 0 &&
10798 level.native_em_level->ply[2]->alive == 0 &&
10799 level.native_em_level->ply[3]->alive == 0) /* all dead */
10800 AllPlayersGone = TRUE;
10802 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
10804 if (game_sp.LevelSolved &&
10805 !game_sp.GameOver) /* game won */
10807 PlayerWins(local_player);
10809 game_sp.GameOver = TRUE;
10811 AllPlayersGone = TRUE;
10814 if (game_sp.GameOver) /* game lost */
10815 AllPlayersGone = TRUE;
10818 if (TimeFrames >= FRAMES_PER_SECOND)
10823 for (i = 0; i < MAX_PLAYERS; i++)
10825 struct PlayerInfo *player = &stored_player[i];
10827 if (SHIELD_ON(player))
10829 player->shield_normal_time_left--;
10831 if (player->shield_deadly_time_left > 0)
10832 player->shield_deadly_time_left--;
10836 if (!local_player->LevelSolved && !level.use_step_counter)
10844 if (TimeLeft <= 10 && setup.time_limit)
10845 PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
10847 /* this does not make sense: game_panel_controls[GAME_PANEL_TIME].value
10848 is reset from other values in UpdateGameDoorValues() -- FIX THIS */
10850 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
10852 if (!TimeLeft && setup.time_limit)
10854 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
10855 level.native_em_level->lev->killed_out_of_time = TRUE;
10857 for (i = 0; i < MAX_PLAYERS; i++)
10858 KillPlayer(&stored_player[i]);
10861 else if (game.no_time_limit && !AllPlayersGone) /* level w/o time limit */
10863 game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
10866 level.native_em_level->lev->time =
10867 (game.no_time_limit ? TimePlayed : TimeLeft);
10870 if (tape.recording || tape.playing)
10871 DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
10874 if (tape.recording || tape.playing)
10875 DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
10877 UpdateAndDisplayGameControlValues();
10880 void AdvanceFrameAndPlayerCounters(int player_nr)
10884 /* advance frame counters (global frame counter and time frame counter) */
10888 /* advance player counters (counters for move delay, move animation etc.) */
10889 for (i = 0; i < MAX_PLAYERS; i++)
10891 boolean advance_player_counters = (player_nr == -1 || player_nr == i);
10892 int move_delay_value = stored_player[i].move_delay_value;
10893 int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
10895 if (!advance_player_counters) /* not all players may be affected */
10898 if (move_frames == 0) /* less than one move per game frame */
10900 int stepsize = TILEX / move_delay_value;
10901 int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
10902 int count = (stored_player[i].is_moving ?
10903 ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
10905 if (count % delay == 0)
10909 stored_player[i].Frame += move_frames;
10911 if (stored_player[i].MovPos != 0)
10912 stored_player[i].StepFrame += move_frames;
10914 if (stored_player[i].move_delay > 0)
10915 stored_player[i].move_delay--;
10917 /* due to bugs in previous versions, counter must count up, not down */
10918 if (stored_player[i].push_delay != -1)
10919 stored_player[i].push_delay++;
10921 if (stored_player[i].drop_delay > 0)
10922 stored_player[i].drop_delay--;
10924 if (stored_player[i].is_dropping_pressed)
10925 stored_player[i].drop_pressed_delay++;
10929 void StartGameActions(boolean init_network_game, boolean record_tape,
10932 unsigned int new_random_seed = InitRND(random_seed);
10935 TapeStartRecording(new_random_seed);
10937 #if defined(NETWORK_AVALIABLE)
10938 if (init_network_game)
10940 SendToServer_StartPlaying();
10951 static unsigned int game_frame_delay = 0;
10952 unsigned int game_frame_delay_value;
10953 byte *recorded_player_action;
10954 byte summarized_player_action = 0;
10955 byte tape_action[MAX_PLAYERS];
10958 /* detect endless loops, caused by custom element programming */
10959 if (recursion_loop_detected && recursion_loop_depth == 0)
10961 char *message = getStringCat3("Internal Error! Element ",
10962 EL_NAME(recursion_loop_element),
10963 " caused endless loop! Quit the game?");
10965 Error(ERR_WARN, "element '%s' caused endless loop in game engine",
10966 EL_NAME(recursion_loop_element));
10968 RequestQuitGameExt(FALSE, level_editor_test_game, message);
10970 recursion_loop_detected = FALSE; /* if game should be continued */
10977 if (game.restart_level)
10978 StartGameActions(options.network, setup.autorecord, level.random_seed);
10980 /* !!! SAME CODE AS IN "CheckLevelTime()" -- FIX THIS !!! */
10981 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
10983 if (level.native_em_level->lev->home == 0) /* all players at home */
10985 PlayerWins(local_player);
10987 AllPlayersGone = TRUE;
10989 level.native_em_level->lev->home = -1;
10992 if (level.native_em_level->ply[0]->alive == 0 &&
10993 level.native_em_level->ply[1]->alive == 0 &&
10994 level.native_em_level->ply[2]->alive == 0 &&
10995 level.native_em_level->ply[3]->alive == 0) /* all dead */
10996 AllPlayersGone = TRUE;
10998 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11000 if (game_sp.LevelSolved &&
11001 !game_sp.GameOver) /* game won */
11003 PlayerWins(local_player);
11005 game_sp.GameOver = TRUE;
11007 AllPlayersGone = TRUE;
11010 if (game_sp.GameOver) /* game lost */
11011 AllPlayersGone = TRUE;
11014 if (local_player->LevelSolved && !local_player->LevelSolved_GameEnd)
11017 if (AllPlayersGone && !TAPE_IS_STOPPED(tape))
11020 if (game_status != GAME_MODE_PLAYING) /* status might have changed */
11023 game_frame_delay_value =
11024 (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
11026 if (tape.playing && tape.warp_forward && !tape.pausing)
11027 game_frame_delay_value = 0;
11029 /* ---------- main game synchronization point ---------- */
11031 WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11033 if (network_playing && !network_player_action_received)
11035 /* try to get network player actions in time */
11037 #if defined(NETWORK_AVALIABLE)
11038 /* last chance to get network player actions without main loop delay */
11039 HandleNetworking();
11042 /* game was quit by network peer */
11043 if (game_status != GAME_MODE_PLAYING)
11046 if (!network_player_action_received)
11047 return; /* failed to get network player actions in time */
11049 /* do not yet reset "network_player_action_received" (for tape.pausing) */
11055 /* at this point we know that we really continue executing the game */
11057 network_player_action_received = FALSE;
11059 /* when playing tape, read previously recorded player input from tape data */
11060 recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
11062 /* TapePlayAction() may return NULL when toggling to "pause before death" */
11066 if (tape.set_centered_player)
11068 game.centered_player_nr_next = tape.centered_player_nr_next;
11069 game.set_centered_player = TRUE;
11072 for (i = 0; i < MAX_PLAYERS; i++)
11074 summarized_player_action |= stored_player[i].action;
11076 if (!network_playing && (game.team_mode || tape.playing))
11077 stored_player[i].effective_action = stored_player[i].action;
11080 #if defined(NETWORK_AVALIABLE)
11081 if (network_playing)
11082 SendToServer_MovePlayer(summarized_player_action);
11085 if (!options.network && !game.team_mode)
11086 local_player->effective_action = summarized_player_action;
11088 if (tape.recording &&
11090 setup.input_on_focus &&
11091 game.centered_player_nr != -1)
11093 for (i = 0; i < MAX_PLAYERS; i++)
11094 stored_player[i].effective_action =
11095 (i == game.centered_player_nr ? summarized_player_action : 0);
11098 if (recorded_player_action != NULL)
11099 for (i = 0; i < MAX_PLAYERS; i++)
11100 stored_player[i].effective_action = recorded_player_action[i];
11102 for (i = 0; i < MAX_PLAYERS; i++)
11104 tape_action[i] = stored_player[i].effective_action;
11106 /* (this may happen in the RND game engine if a player was not present on
11107 the playfield on level start, but appeared later from a custom element */
11108 if (setup.team_mode &&
11111 !tape.player_participates[i])
11112 tape.player_participates[i] = TRUE;
11115 /* only record actions from input devices, but not programmed actions */
11116 if (tape.recording)
11117 TapeRecordAction(tape_action);
11119 #if USE_NEW_PLAYER_ASSIGNMENTS
11120 // !!! also map player actions in single player mode !!!
11121 // if (game.team_mode)
11123 byte mapped_action[MAX_PLAYERS];
11125 #if DEBUG_PLAYER_ACTIONS
11127 for (i = 0; i < MAX_PLAYERS; i++)
11128 printf(" %d, ", stored_player[i].effective_action);
11131 for (i = 0; i < MAX_PLAYERS; i++)
11132 mapped_action[i] = stored_player[map_player_action[i]].effective_action;
11134 for (i = 0; i < MAX_PLAYERS; i++)
11135 stored_player[i].effective_action = mapped_action[i];
11137 #if DEBUG_PLAYER_ACTIONS
11139 for (i = 0; i < MAX_PLAYERS; i++)
11140 printf(" %d, ", stored_player[i].effective_action);
11144 #if DEBUG_PLAYER_ACTIONS
11148 for (i = 0; i < MAX_PLAYERS; i++)
11149 printf(" %d, ", stored_player[i].effective_action);
11155 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11157 GameActions_EM_Main();
11159 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11161 GameActions_SP_Main();
11169 void GameActions_EM_Main()
11171 byte effective_action[MAX_PLAYERS];
11172 boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11175 for (i = 0; i < MAX_PLAYERS; i++)
11176 effective_action[i] = stored_player[i].effective_action;
11178 GameActions_EM(effective_action, warp_mode);
11182 AdvanceFrameAndPlayerCounters(-1); /* advance counters for all players */
11185 void GameActions_SP_Main()
11187 byte effective_action[MAX_PLAYERS];
11188 boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11191 for (i = 0; i < MAX_PLAYERS; i++)
11192 effective_action[i] = stored_player[i].effective_action;
11194 GameActions_SP(effective_action, warp_mode);
11198 AdvanceFrameAndPlayerCounters(-1); /* advance counters for all players */
11201 void GameActions_RND()
11203 int magic_wall_x = 0, magic_wall_y = 0;
11204 int i, x, y, element, graphic;
11206 InitPlayfieldScanModeVars();
11208 if (game.engine_version >= VERSION_IDENT(3,2,0,7))
11210 SCAN_PLAYFIELD(x, y)
11212 ChangeCount[x][y] = 0;
11213 ChangeEvent[x][y] = -1;
11217 if (game.set_centered_player)
11219 boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
11221 /* switching to "all players" only possible if all players fit to screen */
11222 if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
11224 game.centered_player_nr_next = game.centered_player_nr;
11225 game.set_centered_player = FALSE;
11228 /* do not switch focus to non-existing (or non-active) player */
11229 if (game.centered_player_nr_next >= 0 &&
11230 !stored_player[game.centered_player_nr_next].active)
11232 game.centered_player_nr_next = game.centered_player_nr;
11233 game.set_centered_player = FALSE;
11237 if (game.set_centered_player &&
11238 ScreenMovPos == 0) /* screen currently aligned at tile position */
11242 if (game.centered_player_nr_next == -1)
11244 setScreenCenteredToAllPlayers(&sx, &sy);
11248 sx = stored_player[game.centered_player_nr_next].jx;
11249 sy = stored_player[game.centered_player_nr_next].jy;
11252 game.centered_player_nr = game.centered_player_nr_next;
11253 game.set_centered_player = FALSE;
11255 DrawRelocateScreen(0, 0, sx, sy, MV_NONE, TRUE, setup.quick_switch);
11256 DrawGameDoorValues();
11259 for (i = 0; i < MAX_PLAYERS; i++)
11261 int actual_player_action = stored_player[i].effective_action;
11264 /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
11265 - rnd_equinox_tetrachloride 048
11266 - rnd_equinox_tetrachloride_ii 096
11267 - rnd_emanuel_schmieg 002
11268 - doctor_sloan_ww 001, 020
11270 if (stored_player[i].MovPos == 0)
11271 CheckGravityMovement(&stored_player[i]);
11274 /* overwrite programmed action with tape action */
11275 if (stored_player[i].programmed_action)
11276 actual_player_action = stored_player[i].programmed_action;
11278 PlayerActions(&stored_player[i], actual_player_action);
11280 ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
11283 ScrollScreen(NULL, SCROLL_GO_ON);
11285 /* for backwards compatibility, the following code emulates a fixed bug that
11286 occured when pushing elements (causing elements that just made their last
11287 pushing step to already (if possible) make their first falling step in the
11288 same game frame, which is bad); this code is also needed to use the famous
11289 "spring push bug" which is used in older levels and might be wanted to be
11290 used also in newer levels, but in this case the buggy pushing code is only
11291 affecting the "spring" element and no other elements */
11293 if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
11295 for (i = 0; i < MAX_PLAYERS; i++)
11297 struct PlayerInfo *player = &stored_player[i];
11298 int x = player->jx;
11299 int y = player->jy;
11301 if (player->active && player->is_pushing && player->is_moving &&
11303 (game.engine_version < VERSION_IDENT(2,2,0,7) ||
11304 Feld[x][y] == EL_SPRING))
11306 ContinueMoving(x, y);
11308 /* continue moving after pushing (this is actually a bug) */
11309 if (!IS_MOVING(x, y))
11310 Stop[x][y] = FALSE;
11315 SCAN_PLAYFIELD(x, y)
11317 ChangeCount[x][y] = 0;
11318 ChangeEvent[x][y] = -1;
11320 /* this must be handled before main playfield loop */
11321 if (Feld[x][y] == EL_PLAYER_IS_LEAVING)
11324 if (MovDelay[x][y] <= 0)
11328 if (Feld[x][y] == EL_ELEMENT_SNAPPING)
11331 if (MovDelay[x][y] <= 0)
11334 TEST_DrawLevelField(x, y);
11336 TestIfElementTouchesCustomElement(x, y); /* for empty space */
11341 if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
11343 printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
11344 printf("GameActions(): This should never happen!\n");
11346 ChangePage[x][y] = -1;
11350 Stop[x][y] = FALSE;
11351 if (WasJustMoving[x][y] > 0)
11352 WasJustMoving[x][y]--;
11353 if (WasJustFalling[x][y] > 0)
11354 WasJustFalling[x][y]--;
11355 if (CheckCollision[x][y] > 0)
11356 CheckCollision[x][y]--;
11357 if (CheckImpact[x][y] > 0)
11358 CheckImpact[x][y]--;
11362 /* reset finished pushing action (not done in ContinueMoving() to allow
11363 continuous pushing animation for elements with zero push delay) */
11364 if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
11366 ResetGfxAnimation(x, y);
11367 TEST_DrawLevelField(x, y);
11371 if (IS_BLOCKED(x, y))
11375 Blocked2Moving(x, y, &oldx, &oldy);
11376 if (!IS_MOVING(oldx, oldy))
11378 printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
11379 printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
11380 printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
11381 printf("GameActions(): This should never happen!\n");
11387 SCAN_PLAYFIELD(x, y)
11389 element = Feld[x][y];
11390 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11392 ResetGfxFrame(x, y, TRUE);
11394 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
11395 IS_NEXT_FRAME(GfxFrame[x][y], graphic))
11396 ResetRandomAnimationValue(x, y);
11398 SetRandomAnimationValue(x, y);
11400 PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
11402 if (IS_INACTIVE(element))
11404 if (IS_ANIMATED(graphic))
11405 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11410 /* this may take place after moving, so 'element' may have changed */
11411 if (IS_CHANGING(x, y) &&
11412 (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
11414 int page = element_info[element].event_page_nr[CE_DELAY];
11416 HandleElementChange(x, y, page);
11418 element = Feld[x][y];
11419 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11422 if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
11426 element = Feld[x][y];
11427 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11429 if (IS_ANIMATED(graphic) &&
11430 !IS_MOVING(x, y) &&
11432 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11434 if (IS_GEM(element) || element == EL_SP_INFOTRON)
11435 TEST_DrawTwinkleOnField(x, y);
11437 else if ((element == EL_ACID ||
11438 element == EL_EXIT_OPEN ||
11439 element == EL_EM_EXIT_OPEN ||
11440 element == EL_SP_EXIT_OPEN ||
11441 element == EL_STEEL_EXIT_OPEN ||
11442 element == EL_EM_STEEL_EXIT_OPEN ||
11443 element == EL_SP_TERMINAL ||
11444 element == EL_SP_TERMINAL_ACTIVE ||
11445 element == EL_EXTRA_TIME ||
11446 element == EL_SHIELD_NORMAL ||
11447 element == EL_SHIELD_DEADLY) &&
11448 IS_ANIMATED(graphic))
11449 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11450 else if (IS_MOVING(x, y))
11451 ContinueMoving(x, y);
11452 else if (IS_ACTIVE_BOMB(element))
11453 CheckDynamite(x, y);
11454 else if (element == EL_AMOEBA_GROWING)
11455 AmoebeWaechst(x, y);
11456 else if (element == EL_AMOEBA_SHRINKING)
11457 AmoebaDisappearing(x, y);
11459 #if !USE_NEW_AMOEBA_CODE
11460 else if (IS_AMOEBALIVE(element))
11461 AmoebeAbleger(x, y);
11464 else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
11466 else if (element == EL_EXIT_CLOSED)
11468 else if (element == EL_EM_EXIT_CLOSED)
11470 else if (element == EL_STEEL_EXIT_CLOSED)
11471 CheckExitSteel(x, y);
11472 else if (element == EL_EM_STEEL_EXIT_CLOSED)
11473 CheckExitSteelEM(x, y);
11474 else if (element == EL_SP_EXIT_CLOSED)
11476 else if (element == EL_EXPANDABLE_WALL_GROWING ||
11477 element == EL_EXPANDABLE_STEELWALL_GROWING)
11478 MauerWaechst(x, y);
11479 else if (element == EL_EXPANDABLE_WALL ||
11480 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
11481 element == EL_EXPANDABLE_WALL_VERTICAL ||
11482 element == EL_EXPANDABLE_WALL_ANY ||
11483 element == EL_BD_EXPANDABLE_WALL)
11484 MauerAbleger(x, y);
11485 else if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
11486 element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
11487 element == EL_EXPANDABLE_STEELWALL_ANY)
11488 MauerAblegerStahl(x, y);
11489 else if (element == EL_FLAMES)
11490 CheckForDragon(x, y);
11491 else if (element == EL_EXPLOSION)
11492 ; /* drawing of correct explosion animation is handled separately */
11493 else if (element == EL_ELEMENT_SNAPPING ||
11494 element == EL_DIAGONAL_SHRINKING ||
11495 element == EL_DIAGONAL_GROWING)
11497 graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
11499 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11501 else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
11502 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11504 if (IS_BELT_ACTIVE(element))
11505 PlayLevelSoundAction(x, y, ACTION_ACTIVE);
11507 if (game.magic_wall_active)
11509 int jx = local_player->jx, jy = local_player->jy;
11511 /* play the element sound at the position nearest to the player */
11512 if ((element == EL_MAGIC_WALL_FULL ||
11513 element == EL_MAGIC_WALL_ACTIVE ||
11514 element == EL_MAGIC_WALL_EMPTYING ||
11515 element == EL_BD_MAGIC_WALL_FULL ||
11516 element == EL_BD_MAGIC_WALL_ACTIVE ||
11517 element == EL_BD_MAGIC_WALL_EMPTYING ||
11518 element == EL_DC_MAGIC_WALL_FULL ||
11519 element == EL_DC_MAGIC_WALL_ACTIVE ||
11520 element == EL_DC_MAGIC_WALL_EMPTYING) &&
11521 ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
11529 #if USE_NEW_AMOEBA_CODE
11530 /* new experimental amoeba growth stuff */
11531 if (!(FrameCounter % 8))
11533 static unsigned int random = 1684108901;
11535 for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
11537 x = RND(lev_fieldx);
11538 y = RND(lev_fieldy);
11539 element = Feld[x][y];
11541 if (!IS_PLAYER(x,y) &&
11542 (element == EL_EMPTY ||
11543 CAN_GROW_INTO(element) ||
11544 element == EL_QUICKSAND_EMPTY ||
11545 element == EL_QUICKSAND_FAST_EMPTY ||
11546 element == EL_ACID_SPLASH_LEFT ||
11547 element == EL_ACID_SPLASH_RIGHT))
11549 if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
11550 (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
11551 (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
11552 (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
11553 Feld[x][y] = EL_AMOEBA_DROP;
11556 random = random * 129 + 1;
11561 game.explosions_delayed = FALSE;
11563 SCAN_PLAYFIELD(x, y)
11565 element = Feld[x][y];
11567 if (ExplodeField[x][y])
11568 Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
11569 else if (element == EL_EXPLOSION)
11570 Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
11572 ExplodeField[x][y] = EX_TYPE_NONE;
11575 game.explosions_delayed = TRUE;
11577 if (game.magic_wall_active)
11579 if (!(game.magic_wall_time_left % 4))
11581 int element = Feld[magic_wall_x][magic_wall_y];
11583 if (element == EL_BD_MAGIC_WALL_FULL ||
11584 element == EL_BD_MAGIC_WALL_ACTIVE ||
11585 element == EL_BD_MAGIC_WALL_EMPTYING)
11586 PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
11587 else if (element == EL_DC_MAGIC_WALL_FULL ||
11588 element == EL_DC_MAGIC_WALL_ACTIVE ||
11589 element == EL_DC_MAGIC_WALL_EMPTYING)
11590 PlayLevelSound(magic_wall_x, magic_wall_y, SND_DC_MAGIC_WALL_ACTIVE);
11592 PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
11595 if (game.magic_wall_time_left > 0)
11597 game.magic_wall_time_left--;
11599 if (!game.magic_wall_time_left)
11601 SCAN_PLAYFIELD(x, y)
11603 element = Feld[x][y];
11605 if (element == EL_MAGIC_WALL_ACTIVE ||
11606 element == EL_MAGIC_WALL_FULL)
11608 Feld[x][y] = EL_MAGIC_WALL_DEAD;
11609 TEST_DrawLevelField(x, y);
11611 else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
11612 element == EL_BD_MAGIC_WALL_FULL)
11614 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
11615 TEST_DrawLevelField(x, y);
11617 else if (element == EL_DC_MAGIC_WALL_ACTIVE ||
11618 element == EL_DC_MAGIC_WALL_FULL)
11620 Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
11621 TEST_DrawLevelField(x, y);
11625 game.magic_wall_active = FALSE;
11630 if (game.light_time_left > 0)
11632 game.light_time_left--;
11634 if (game.light_time_left == 0)
11635 RedrawAllLightSwitchesAndInvisibleElements();
11638 if (game.timegate_time_left > 0)
11640 game.timegate_time_left--;
11642 if (game.timegate_time_left == 0)
11643 CloseAllOpenTimegates();
11646 if (game.lenses_time_left > 0)
11648 game.lenses_time_left--;
11650 if (game.lenses_time_left == 0)
11651 RedrawAllInvisibleElementsForLenses();
11654 if (game.magnify_time_left > 0)
11656 game.magnify_time_left--;
11658 if (game.magnify_time_left == 0)
11659 RedrawAllInvisibleElementsForMagnifier();
11662 for (i = 0; i < MAX_PLAYERS; i++)
11664 struct PlayerInfo *player = &stored_player[i];
11666 if (SHIELD_ON(player))
11668 if (player->shield_deadly_time_left)
11669 PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
11670 else if (player->shield_normal_time_left)
11671 PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
11675 #if USE_DELAYED_GFX_REDRAW
11676 SCAN_PLAYFIELD(x, y)
11678 if (GfxRedraw[x][y] != GFX_REDRAW_NONE)
11680 /* !!! PROBLEM: THIS REDRAWS THE PLAYFIELD _AFTER_ THE SCAN, BUT TILES
11681 !!! MAY HAVE CHANGED AFTER BEING DRAWN DURING PLAYFIELD SCAN !!! */
11683 if (GfxRedraw[x][y] & GFX_REDRAW_TILE)
11684 DrawLevelField(x, y);
11686 if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED)
11687 DrawLevelFieldCrumbled(x, y);
11689 if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS)
11690 DrawLevelFieldCrumbledNeighbours(x, y);
11692 if (GfxRedraw[x][y] & GFX_REDRAW_TILE_TWINKLED)
11693 DrawTwinkleOnField(x, y);
11696 GfxRedraw[x][y] = GFX_REDRAW_NONE;
11703 PlayAllPlayersSound();
11705 if (options.debug) /* calculate frames per second */
11707 static unsigned int fps_counter = 0;
11708 static int fps_frames = 0;
11709 unsigned int fps_delay_ms = Counter() - fps_counter;
11713 if (fps_delay_ms >= 500) /* calculate fps every 0.5 seconds */
11715 global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
11718 fps_counter = Counter();
11721 redraw_mask |= REDRAW_FPS;
11724 AdvanceFrameAndPlayerCounters(-1); /* advance counters for all players */
11726 if (local_player->show_envelope != 0 && local_player->MovPos == 0)
11728 ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
11730 local_player->show_envelope = 0;
11733 /* use random number generator in every frame to make it less predictable */
11734 if (game.engine_version >= VERSION_IDENT(3,1,1,0))
11738 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
11740 int min_x = x, min_y = y, max_x = x, max_y = y;
11743 for (i = 0; i < MAX_PLAYERS; i++)
11745 int jx = stored_player[i].jx, jy = stored_player[i].jy;
11747 if (!stored_player[i].active || &stored_player[i] == player)
11750 min_x = MIN(min_x, jx);
11751 min_y = MIN(min_y, jy);
11752 max_x = MAX(max_x, jx);
11753 max_y = MAX(max_y, jy);
11756 return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
11759 static boolean AllPlayersInVisibleScreen()
11763 for (i = 0; i < MAX_PLAYERS; i++)
11765 int jx = stored_player[i].jx, jy = stored_player[i].jy;
11767 if (!stored_player[i].active)
11770 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
11777 void ScrollLevel(int dx, int dy)
11779 int softscroll_offset = (setup.soft_scrolling ? 2 * TILEX_VAR : 0);
11782 BlitBitmap(drawto_field, drawto_field,
11783 FX + TILEX_VAR * (dx == -1) - softscroll_offset,
11784 FY + TILEY_VAR * (dy == -1) - softscroll_offset,
11785 SXSIZE - TILEX_VAR * (dx != 0) + 2 * softscroll_offset,
11786 SYSIZE - TILEY_VAR * (dy != 0) + 2 * softscroll_offset,
11787 FX + TILEX_VAR * (dx == 1) - softscroll_offset,
11788 FY + TILEY_VAR * (dy == 1) - softscroll_offset);
11792 x = (dx == 1 ? BX1 : BX2);
11793 for (y = BY1; y <= BY2; y++)
11794 DrawScreenField(x, y);
11799 y = (dy == 1 ? BY1 : BY2);
11800 for (x = BX1; x <= BX2; x++)
11801 DrawScreenField(x, y);
11804 redraw_mask |= REDRAW_FIELD;
11807 static boolean canFallDown(struct PlayerInfo *player)
11809 int jx = player->jx, jy = player->jy;
11811 return (IN_LEV_FIELD(jx, jy + 1) &&
11812 (IS_FREE(jx, jy + 1) ||
11813 (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
11814 IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
11815 !IS_WALKABLE_INSIDE(Feld[jx][jy]));
11818 static boolean canPassField(int x, int y, int move_dir)
11820 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
11821 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
11822 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
11823 int nextx = x + dx;
11824 int nexty = y + dy;
11825 int element = Feld[x][y];
11827 return (IS_PASSABLE_FROM(element, opposite_dir) &&
11828 !CAN_MOVE(element) &&
11829 IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
11830 IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
11831 (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
11834 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
11836 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
11837 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
11838 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
11842 return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
11843 IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
11844 (IS_DIGGABLE(Feld[newx][newy]) ||
11845 IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
11846 canPassField(newx, newy, move_dir)));
11849 static void CheckGravityMovement(struct PlayerInfo *player)
11851 if (player->gravity && !player->programmed_action)
11853 int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
11854 int move_dir_vertical = player->effective_action & MV_VERTICAL;
11855 boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
11856 int jx = player->jx, jy = player->jy;
11857 boolean player_is_moving_to_valid_field =
11858 (!player_is_snapping &&
11859 (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
11860 canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
11861 boolean player_can_fall_down = canFallDown(player);
11863 if (player_can_fall_down &&
11864 !player_is_moving_to_valid_field)
11865 player->programmed_action = MV_DOWN;
11869 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
11871 return CheckGravityMovement(player);
11873 if (player->gravity && !player->programmed_action)
11875 int jx = player->jx, jy = player->jy;
11876 boolean field_under_player_is_free =
11877 (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
11878 boolean player_is_standing_on_valid_field =
11879 (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
11880 (IS_WALKABLE(Feld[jx][jy]) &&
11881 !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
11883 if (field_under_player_is_free && !player_is_standing_on_valid_field)
11884 player->programmed_action = MV_DOWN;
11889 MovePlayerOneStep()
11890 -----------------------------------------------------------------------------
11891 dx, dy: direction (non-diagonal) to try to move the player to
11892 real_dx, real_dy: direction as read from input device (can be diagonal)
11895 boolean MovePlayerOneStep(struct PlayerInfo *player,
11896 int dx, int dy, int real_dx, int real_dy)
11898 int jx = player->jx, jy = player->jy;
11899 int new_jx = jx + dx, new_jy = jy + dy;
11901 boolean player_can_move = !player->cannot_move;
11903 if (!player->active || (!dx && !dy))
11904 return MP_NO_ACTION;
11906 player->MovDir = (dx < 0 ? MV_LEFT :
11907 dx > 0 ? MV_RIGHT :
11909 dy > 0 ? MV_DOWN : MV_NONE);
11911 if (!IN_LEV_FIELD(new_jx, new_jy))
11912 return MP_NO_ACTION;
11914 if (!player_can_move)
11916 if (player->MovPos == 0)
11918 player->is_moving = FALSE;
11919 player->is_digging = FALSE;
11920 player->is_collecting = FALSE;
11921 player->is_snapping = FALSE;
11922 player->is_pushing = FALSE;
11926 if (!options.network && game.centered_player_nr == -1 &&
11927 !AllPlayersInSight(player, new_jx, new_jy))
11928 return MP_NO_ACTION;
11930 can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
11931 if (can_move != MP_MOVING)
11934 /* check if DigField() has caused relocation of the player */
11935 if (player->jx != jx || player->jy != jy)
11936 return MP_NO_ACTION; /* <-- !!! CHECK THIS [-> MP_ACTION ?] !!! */
11938 StorePlayer[jx][jy] = 0;
11939 player->last_jx = jx;
11940 player->last_jy = jy;
11941 player->jx = new_jx;
11942 player->jy = new_jy;
11943 StorePlayer[new_jx][new_jy] = player->element_nr;
11945 if (player->move_delay_value_next != -1)
11947 player->move_delay_value = player->move_delay_value_next;
11948 player->move_delay_value_next = -1;
11952 (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
11954 player->step_counter++;
11956 PlayerVisit[jx][jy] = FrameCounter;
11958 player->is_moving = TRUE;
11961 /* should better be called in MovePlayer(), but this breaks some tapes */
11962 ScrollPlayer(player, SCROLL_INIT);
11968 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
11970 int jx = player->jx, jy = player->jy;
11971 int old_jx = jx, old_jy = jy;
11972 int moved = MP_NO_ACTION;
11974 if (!player->active)
11979 if (player->MovPos == 0)
11981 player->is_moving = FALSE;
11982 player->is_digging = FALSE;
11983 player->is_collecting = FALSE;
11984 player->is_snapping = FALSE;
11985 player->is_pushing = FALSE;
11991 if (player->move_delay > 0)
11994 player->move_delay = -1; /* set to "uninitialized" value */
11996 /* store if player is automatically moved to next field */
11997 player->is_auto_moving = (player->programmed_action != MV_NONE);
11999 /* remove the last programmed player action */
12000 player->programmed_action = 0;
12002 if (player->MovPos)
12004 /* should only happen if pre-1.2 tape recordings are played */
12005 /* this is only for backward compatibility */
12007 int original_move_delay_value = player->move_delay_value;
12010 printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%d]\n",
12014 /* scroll remaining steps with finest movement resolution */
12015 player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
12017 while (player->MovPos)
12019 ScrollPlayer(player, SCROLL_GO_ON);
12020 ScrollScreen(NULL, SCROLL_GO_ON);
12022 AdvanceFrameAndPlayerCounters(player->index_nr);
12028 player->move_delay_value = original_move_delay_value;
12031 player->is_active = FALSE;
12033 if (player->last_move_dir & MV_HORIZONTAL)
12035 if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
12036 moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
12040 if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
12041 moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
12044 if (!moved && !player->is_active)
12046 player->is_moving = FALSE;
12047 player->is_digging = FALSE;
12048 player->is_collecting = FALSE;
12049 player->is_snapping = FALSE;
12050 player->is_pushing = FALSE;
12056 if (moved & MP_MOVING && !ScreenMovPos &&
12057 (player->index_nr == game.centered_player_nr ||
12058 game.centered_player_nr == -1))
12060 int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
12061 int offset = game.scroll_delay_value;
12063 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12065 /* actual player has left the screen -- scroll in that direction */
12066 if (jx != old_jx) /* player has moved horizontally */
12067 scroll_x += (jx - old_jx);
12068 else /* player has moved vertically */
12069 scroll_y += (jy - old_jy);
12073 if (jx != old_jx) /* player has moved horizontally */
12075 if ((player->MovDir == MV_LEFT && scroll_x > jx - MIDPOSX + offset) ||
12076 (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
12077 scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
12079 /* don't scroll over playfield boundaries */
12080 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
12081 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
12083 /* don't scroll more than one field at a time */
12084 scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
12086 /* don't scroll against the player's moving direction */
12087 if ((player->MovDir == MV_LEFT && scroll_x > old_scroll_x) ||
12088 (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
12089 scroll_x = old_scroll_x;
12091 else /* player has moved vertically */
12093 if ((player->MovDir == MV_UP && scroll_y > jy - MIDPOSY + offset) ||
12094 (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
12095 scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
12097 /* don't scroll over playfield boundaries */
12098 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
12099 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
12101 /* don't scroll more than one field at a time */
12102 scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
12104 /* don't scroll against the player's moving direction */
12105 if ((player->MovDir == MV_UP && scroll_y > old_scroll_y) ||
12106 (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
12107 scroll_y = old_scroll_y;
12111 if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
12113 if (!options.network && game.centered_player_nr == -1 &&
12114 !AllPlayersInVisibleScreen())
12116 scroll_x = old_scroll_x;
12117 scroll_y = old_scroll_y;
12121 ScrollScreen(player, SCROLL_INIT);
12122 ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
12127 player->StepFrame = 0;
12129 if (moved & MP_MOVING)
12131 if (old_jx != jx && old_jy == jy)
12132 player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
12133 else if (old_jx == jx && old_jy != jy)
12134 player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
12136 TEST_DrawLevelField(jx, jy); /* for "crumbled sand" */
12138 player->last_move_dir = player->MovDir;
12139 player->is_moving = TRUE;
12140 player->is_snapping = FALSE;
12141 player->is_switching = FALSE;
12142 player->is_dropping = FALSE;
12143 player->is_dropping_pressed = FALSE;
12144 player->drop_pressed_delay = 0;
12147 /* should better be called here than above, but this breaks some tapes */
12148 ScrollPlayer(player, SCROLL_INIT);
12153 CheckGravityMovementWhenNotMoving(player);
12155 player->is_moving = FALSE;
12157 /* at this point, the player is allowed to move, but cannot move right now
12158 (e.g. because of something blocking the way) -- ensure that the player
12159 is also allowed to move in the next frame (in old versions before 3.1.1,
12160 the player was forced to wait again for eight frames before next try) */
12162 if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12163 player->move_delay = 0; /* allow direct movement in the next frame */
12166 if (player->move_delay == -1) /* not yet initialized by DigField() */
12167 player->move_delay = player->move_delay_value;
12169 if (game.engine_version < VERSION_IDENT(3,0,7,0))
12171 TestIfPlayerTouchesBadThing(jx, jy);
12172 TestIfPlayerTouchesCustomElement(jx, jy);
12175 if (!player->active)
12176 RemovePlayer(player);
12181 void ScrollPlayer(struct PlayerInfo *player, int mode)
12183 int jx = player->jx, jy = player->jy;
12184 int last_jx = player->last_jx, last_jy = player->last_jy;
12185 int move_stepsize = TILEX / player->move_delay_value;
12187 if (!player->active)
12190 if (player->MovPos == 0 && mode == SCROLL_GO_ON) /* player not moving */
12193 if (mode == SCROLL_INIT)
12195 player->actual_frame_counter = FrameCounter;
12196 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
12198 if ((player->block_last_field || player->block_delay_adjustment > 0) &&
12199 Feld[last_jx][last_jy] == EL_EMPTY)
12201 int last_field_block_delay = 0; /* start with no blocking at all */
12202 int block_delay_adjustment = player->block_delay_adjustment;
12204 /* if player blocks last field, add delay for exactly one move */
12205 if (player->block_last_field)
12207 last_field_block_delay += player->move_delay_value;
12209 /* when blocking enabled, prevent moving up despite gravity */
12210 if (player->gravity && player->MovDir == MV_UP)
12211 block_delay_adjustment = -1;
12214 /* add block delay adjustment (also possible when not blocking) */
12215 last_field_block_delay += block_delay_adjustment;
12217 Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
12218 MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
12221 if (player->MovPos != 0) /* player has not yet reached destination */
12224 else if (!FrameReached(&player->actual_frame_counter, 1))
12227 if (player->MovPos != 0)
12229 player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
12230 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
12232 /* before DrawPlayer() to draw correct player graphic for this case */
12233 if (player->MovPos == 0)
12234 CheckGravityMovement(player);
12237 if (player->MovPos == 0) /* player reached destination field */
12239 if (player->move_delay_reset_counter > 0)
12241 player->move_delay_reset_counter--;
12243 if (player->move_delay_reset_counter == 0)
12245 /* continue with normal speed after quickly moving through gate */
12246 HALVE_PLAYER_SPEED(player);
12248 /* be able to make the next move without delay */
12249 player->move_delay = 0;
12253 player->last_jx = jx;
12254 player->last_jy = jy;
12256 if (Feld[jx][jy] == EL_EXIT_OPEN ||
12257 Feld[jx][jy] == EL_EM_EXIT_OPEN ||
12258 Feld[jx][jy] == EL_EM_EXIT_OPENING ||
12259 Feld[jx][jy] == EL_STEEL_EXIT_OPEN ||
12260 Feld[jx][jy] == EL_EM_STEEL_EXIT_OPEN ||
12261 Feld[jx][jy] == EL_EM_STEEL_EXIT_OPENING ||
12262 Feld[jx][jy] == EL_SP_EXIT_OPEN ||
12263 Feld[jx][jy] == EL_SP_EXIT_OPENING) /* <-- special case */
12265 DrawPlayer(player); /* needed here only to cleanup last field */
12266 RemovePlayer(player);
12268 if (local_player->friends_still_needed == 0 ||
12269 IS_SP_ELEMENT(Feld[jx][jy]))
12270 PlayerWins(player);
12273 /* this breaks one level: "machine", level 000 */
12275 int move_direction = player->MovDir;
12276 int enter_side = MV_DIR_OPPOSITE(move_direction);
12277 int leave_side = move_direction;
12278 int old_jx = last_jx;
12279 int old_jy = last_jy;
12280 int old_element = Feld[old_jx][old_jy];
12281 int new_element = Feld[jx][jy];
12283 if (IS_CUSTOM_ELEMENT(old_element))
12284 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
12286 player->index_bit, leave_side);
12288 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
12289 CE_PLAYER_LEAVES_X,
12290 player->index_bit, leave_side);
12292 if (IS_CUSTOM_ELEMENT(new_element))
12293 CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
12294 player->index_bit, enter_side);
12296 CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
12297 CE_PLAYER_ENTERS_X,
12298 player->index_bit, enter_side);
12300 CheckTriggeredElementChangeBySide(jx, jy, player->initial_element,
12301 CE_MOVE_OF_X, move_direction);
12304 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
12306 TestIfPlayerTouchesBadThing(jx, jy);
12307 TestIfPlayerTouchesCustomElement(jx, jy);
12309 /* needed because pushed element has not yet reached its destination,
12310 so it would trigger a change event at its previous field location */
12311 if (!player->is_pushing)
12312 TestIfElementTouchesCustomElement(jx, jy); /* for empty space */
12314 if (!player->active)
12315 RemovePlayer(player);
12318 if (!local_player->LevelSolved && level.use_step_counter)
12328 if (TimeLeft <= 10 && setup.time_limit)
12329 PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
12331 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
12333 DisplayGameControlValues();
12335 if (!TimeLeft && setup.time_limit)
12336 for (i = 0; i < MAX_PLAYERS; i++)
12337 KillPlayer(&stored_player[i]);
12339 else if (game.no_time_limit && !AllPlayersGone) /* level w/o time limit */
12341 game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
12343 DisplayGameControlValues();
12347 if (tape.single_step && tape.recording && !tape.pausing &&
12348 !player->programmed_action)
12349 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
12351 if (!player->programmed_action)
12352 CheckSaveEngineSnapshot(player);
12356 void ScrollScreen(struct PlayerInfo *player, int mode)
12358 static unsigned int screen_frame_counter = 0;
12360 if (mode == SCROLL_INIT)
12362 /* set scrolling step size according to actual player's moving speed */
12363 ScrollStepSize = TILEX / player->move_delay_value;
12365 screen_frame_counter = FrameCounter;
12366 ScreenMovDir = player->MovDir;
12367 ScreenMovPos = player->MovPos;
12368 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
12371 else if (!FrameReached(&screen_frame_counter, 1))
12376 ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
12377 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
12378 redraw_mask |= REDRAW_FIELD;
12381 ScreenMovDir = MV_NONE;
12384 void TestIfPlayerTouchesCustomElement(int x, int y)
12386 static int xy[4][2] =
12393 static int trigger_sides[4][2] =
12395 /* center side border side */
12396 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
12397 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
12398 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
12399 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
12401 static int touch_dir[4] =
12403 MV_LEFT | MV_RIGHT,
12408 int center_element = Feld[x][y]; /* should always be non-moving! */
12411 for (i = 0; i < NUM_DIRECTIONS; i++)
12413 int xx = x + xy[i][0];
12414 int yy = y + xy[i][1];
12415 int center_side = trigger_sides[i][0];
12416 int border_side = trigger_sides[i][1];
12417 int border_element;
12419 if (!IN_LEV_FIELD(xx, yy))
12422 if (IS_PLAYER(x, y)) /* player found at center element */
12424 struct PlayerInfo *player = PLAYERINFO(x, y);
12426 if (game.engine_version < VERSION_IDENT(3,0,7,0))
12427 border_element = Feld[xx][yy]; /* may be moving! */
12428 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
12429 border_element = Feld[xx][yy];
12430 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
12431 border_element = MovingOrBlocked2Element(xx, yy);
12433 continue; /* center and border element do not touch */
12435 CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
12436 player->index_bit, border_side);
12437 CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
12438 CE_PLAYER_TOUCHES_X,
12439 player->index_bit, border_side);
12442 /* use player element that is initially defined in the level playfield,
12443 not the player element that corresponds to the runtime player number
12444 (example: a level that contains EL_PLAYER_3 as the only player would
12445 incorrectly give EL_PLAYER_1 for "player->element_nr") */
12446 int player_element = PLAYERINFO(x, y)->initial_element;
12448 CheckElementChangeBySide(xx, yy, border_element, player_element,
12449 CE_TOUCHING_X, border_side);
12452 else if (IS_PLAYER(xx, yy)) /* player found at border element */
12454 struct PlayerInfo *player = PLAYERINFO(xx, yy);
12456 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
12458 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
12459 continue; /* center and border element do not touch */
12462 CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
12463 player->index_bit, center_side);
12464 CheckTriggeredElementChangeByPlayer(x, y, center_element,
12465 CE_PLAYER_TOUCHES_X,
12466 player->index_bit, center_side);
12469 /* use player element that is initially defined in the level playfield,
12470 not the player element that corresponds to the runtime player number
12471 (example: a level that contains EL_PLAYER_3 as the only player would
12472 incorrectly give EL_PLAYER_1 for "player->element_nr") */
12473 int player_element = PLAYERINFO(xx, yy)->initial_element;
12475 CheckElementChangeBySide(x, y, center_element, player_element,
12476 CE_TOUCHING_X, center_side);
12484 void TestIfElementTouchesCustomElement(int x, int y)
12486 static int xy[4][2] =
12493 static int trigger_sides[4][2] =
12495 /* center side border side */
12496 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
12497 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
12498 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
12499 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
12501 static int touch_dir[4] =
12503 MV_LEFT | MV_RIGHT,
12508 boolean change_center_element = FALSE;
12509 int center_element = Feld[x][y]; /* should always be non-moving! */
12510 int border_element_old[NUM_DIRECTIONS];
12513 for (i = 0; i < NUM_DIRECTIONS; i++)
12515 int xx = x + xy[i][0];
12516 int yy = y + xy[i][1];
12517 int border_element;
12519 border_element_old[i] = -1;
12521 if (!IN_LEV_FIELD(xx, yy))
12524 if (game.engine_version < VERSION_IDENT(3,0,7,0))
12525 border_element = Feld[xx][yy]; /* may be moving! */
12526 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
12527 border_element = Feld[xx][yy];
12528 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
12529 border_element = MovingOrBlocked2Element(xx, yy);
12531 continue; /* center and border element do not touch */
12533 border_element_old[i] = border_element;
12536 for (i = 0; i < NUM_DIRECTIONS; i++)
12538 int xx = x + xy[i][0];
12539 int yy = y + xy[i][1];
12540 int center_side = trigger_sides[i][0];
12541 int border_element = border_element_old[i];
12543 if (border_element == -1)
12546 /* check for change of border element */
12547 CheckElementChangeBySide(xx, yy, border_element, center_element,
12548 CE_TOUCHING_X, center_side);
12550 /* (center element cannot be player, so we dont have to check this here) */
12553 for (i = 0; i < NUM_DIRECTIONS; i++)
12555 int xx = x + xy[i][0];
12556 int yy = y + xy[i][1];
12557 int border_side = trigger_sides[i][1];
12558 int border_element = border_element_old[i];
12560 if (border_element == -1)
12563 /* check for change of center element (but change it only once) */
12564 if (!change_center_element)
12565 change_center_element =
12566 CheckElementChangeBySide(x, y, center_element, border_element,
12567 CE_TOUCHING_X, border_side);
12569 if (IS_PLAYER(xx, yy))
12571 /* use player element that is initially defined in the level playfield,
12572 not the player element that corresponds to the runtime player number
12573 (example: a level that contains EL_PLAYER_3 as the only player would
12574 incorrectly give EL_PLAYER_1 for "player->element_nr") */
12575 int player_element = PLAYERINFO(xx, yy)->initial_element;
12577 CheckElementChangeBySide(x, y, center_element, player_element,
12578 CE_TOUCHING_X, border_side);
12583 void TestIfElementHitsCustomElement(int x, int y, int direction)
12585 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
12586 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
12587 int hitx = x + dx, hity = y + dy;
12588 int hitting_element = Feld[x][y];
12589 int touched_element;
12591 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
12594 touched_element = (IN_LEV_FIELD(hitx, hity) ?
12595 MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
12597 if (IN_LEV_FIELD(hitx, hity))
12599 int opposite_direction = MV_DIR_OPPOSITE(direction);
12600 int hitting_side = direction;
12601 int touched_side = opposite_direction;
12602 boolean object_hit = (!IS_MOVING(hitx, hity) ||
12603 MovDir[hitx][hity] != direction ||
12604 ABS(MovPos[hitx][hity]) <= TILEY / 2);
12610 CheckElementChangeBySide(x, y, hitting_element, touched_element,
12611 CE_HITTING_X, touched_side);
12613 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
12614 CE_HIT_BY_X, hitting_side);
12616 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
12617 CE_HIT_BY_SOMETHING, opposite_direction);
12619 if (IS_PLAYER(hitx, hity))
12621 /* use player element that is initially defined in the level playfield,
12622 not the player element that corresponds to the runtime player number
12623 (example: a level that contains EL_PLAYER_3 as the only player would
12624 incorrectly give EL_PLAYER_1 for "player->element_nr") */
12625 int player_element = PLAYERINFO(hitx, hity)->initial_element;
12627 CheckElementChangeBySide(x, y, hitting_element, player_element,
12628 CE_HITTING_X, touched_side);
12633 /* "hitting something" is also true when hitting the playfield border */
12634 CheckElementChangeBySide(x, y, hitting_element, touched_element,
12635 CE_HITTING_SOMETHING, direction);
12638 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
12640 int i, kill_x = -1, kill_y = -1;
12642 int bad_element = -1;
12643 static int test_xy[4][2] =
12650 static int test_dir[4] =
12658 for (i = 0; i < NUM_DIRECTIONS; i++)
12660 int test_x, test_y, test_move_dir, test_element;
12662 test_x = good_x + test_xy[i][0];
12663 test_y = good_y + test_xy[i][1];
12665 if (!IN_LEV_FIELD(test_x, test_y))
12669 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
12671 test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
12673 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
12674 2nd case: DONT_TOUCH style bad thing does not move away from good thing
12676 if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
12677 (DONT_TOUCH(test_element) && test_move_dir != test_dir[i]))
12681 bad_element = test_element;
12687 if (kill_x != -1 || kill_y != -1)
12689 if (IS_PLAYER(good_x, good_y))
12691 struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
12693 if (player->shield_deadly_time_left > 0 &&
12694 !IS_INDESTRUCTIBLE(bad_element))
12695 Bang(kill_x, kill_y);
12696 else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
12697 KillPlayer(player);
12700 Bang(good_x, good_y);
12704 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
12706 int i, kill_x = -1, kill_y = -1;
12707 int bad_element = Feld[bad_x][bad_y];
12708 static int test_xy[4][2] =
12715 static int touch_dir[4] =
12717 MV_LEFT | MV_RIGHT,
12722 static int test_dir[4] =
12730 if (bad_element == EL_EXPLOSION) /* skip just exploding bad things */
12733 for (i = 0; i < NUM_DIRECTIONS; i++)
12735 int test_x, test_y, test_move_dir, test_element;
12737 test_x = bad_x + test_xy[i][0];
12738 test_y = bad_y + test_xy[i][1];
12740 if (!IN_LEV_FIELD(test_x, test_y))
12744 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
12746 test_element = Feld[test_x][test_y];
12748 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
12749 2nd case: DONT_TOUCH style bad thing does not move away from good thing
12751 if ((DONT_RUN_INTO(bad_element) && bad_move_dir == test_dir[i]) ||
12752 (DONT_TOUCH(bad_element) && test_move_dir != test_dir[i]))
12754 /* good thing is player or penguin that does not move away */
12755 if (IS_PLAYER(test_x, test_y))
12757 struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
12759 if (bad_element == EL_ROBOT && player->is_moving)
12760 continue; /* robot does not kill player if he is moving */
12762 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
12764 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
12765 continue; /* center and border element do not touch */
12773 else if (test_element == EL_PENGUIN)
12783 if (kill_x != -1 || kill_y != -1)
12785 if (IS_PLAYER(kill_x, kill_y))
12787 struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
12789 if (player->shield_deadly_time_left > 0 &&
12790 !IS_INDESTRUCTIBLE(bad_element))
12791 Bang(bad_x, bad_y);
12792 else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
12793 KillPlayer(player);
12796 Bang(kill_x, kill_y);
12800 void TestIfGoodThingGetsHitByBadThing(int bad_x, int bad_y, int bad_move_dir)
12802 int bad_element = Feld[bad_x][bad_y];
12803 int dx = (bad_move_dir == MV_LEFT ? -1 : bad_move_dir == MV_RIGHT ? +1 : 0);
12804 int dy = (bad_move_dir == MV_UP ? -1 : bad_move_dir == MV_DOWN ? +1 : 0);
12805 int test_x = bad_x + dx, test_y = bad_y + dy;
12806 int test_move_dir, test_element;
12807 int kill_x = -1, kill_y = -1;
12809 if (!IN_LEV_FIELD(test_x, test_y))
12813 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
12815 test_element = Feld[test_x][test_y];
12817 if (test_move_dir != bad_move_dir)
12819 /* good thing can be player or penguin that does not move away */
12820 if (IS_PLAYER(test_x, test_y))
12822 struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
12824 /* (note: in comparison to DONT_RUN_TO and DONT_TOUCH, also handle the
12825 player as being hit when he is moving towards the bad thing, because
12826 the "get hit by" condition would be lost after the player stops) */
12827 if (player->MovPos != 0 && player->MovDir == bad_move_dir)
12828 return; /* player moves away from bad thing */
12833 else if (test_element == EL_PENGUIN)
12840 if (kill_x != -1 || kill_y != -1)
12842 if (IS_PLAYER(kill_x, kill_y))
12844 struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
12846 if (player->shield_deadly_time_left > 0 &&
12847 !IS_INDESTRUCTIBLE(bad_element))
12848 Bang(bad_x, bad_y);
12849 else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
12850 KillPlayer(player);
12853 Bang(kill_x, kill_y);
12857 void TestIfPlayerTouchesBadThing(int x, int y)
12859 TestIfGoodThingHitsBadThing(x, y, MV_NONE);
12862 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
12864 TestIfGoodThingHitsBadThing(x, y, move_dir);
12867 void TestIfBadThingTouchesPlayer(int x, int y)
12869 TestIfBadThingHitsGoodThing(x, y, MV_NONE);
12872 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
12874 TestIfBadThingHitsGoodThing(x, y, move_dir);
12877 void TestIfFriendTouchesBadThing(int x, int y)
12879 TestIfGoodThingHitsBadThing(x, y, MV_NONE);
12882 void TestIfBadThingTouchesFriend(int x, int y)
12884 TestIfBadThingHitsGoodThing(x, y, MV_NONE);
12887 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
12889 int i, kill_x = bad_x, kill_y = bad_y;
12890 static int xy[4][2] =
12898 for (i = 0; i < NUM_DIRECTIONS; i++)
12902 x = bad_x + xy[i][0];
12903 y = bad_y + xy[i][1];
12904 if (!IN_LEV_FIELD(x, y))
12907 element = Feld[x][y];
12908 if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
12909 element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
12917 if (kill_x != bad_x || kill_y != bad_y)
12918 Bang(bad_x, bad_y);
12921 void KillPlayer(struct PlayerInfo *player)
12923 int jx = player->jx, jy = player->jy;
12925 if (!player->active)
12929 printf("::: 0: killed == %d, active == %d, reanimated == %d\n",
12930 player->killed, player->active, player->reanimated);
12933 /* the following code was introduced to prevent an infinite loop when calling
12935 -> CheckTriggeredElementChangeExt()
12936 -> ExecuteCustomElementAction()
12938 -> (infinitely repeating the above sequence of function calls)
12939 which occurs when killing the player while having a CE with the setting
12940 "kill player X when explosion of <player X>"; the solution using a new
12941 field "player->killed" was chosen for backwards compatibility, although
12942 clever use of the fields "player->active" etc. would probably also work */
12944 if (player->killed)
12948 player->killed = TRUE;
12950 /* remove accessible field at the player's position */
12951 Feld[jx][jy] = EL_EMPTY;
12953 /* deactivate shield (else Bang()/Explode() would not work right) */
12954 player->shield_normal_time_left = 0;
12955 player->shield_deadly_time_left = 0;
12958 printf("::: 1: killed == %d, active == %d, reanimated == %d\n",
12959 player->killed, player->active, player->reanimated);
12965 printf("::: 2: killed == %d, active == %d, reanimated == %d\n",
12966 player->killed, player->active, player->reanimated);
12969 if (player->reanimated) /* killed player may have been reanimated */
12970 player->killed = player->reanimated = FALSE;
12972 BuryPlayer(player);
12975 static void KillPlayerUnlessEnemyProtected(int x, int y)
12977 if (!PLAYER_ENEMY_PROTECTED(x, y))
12978 KillPlayer(PLAYERINFO(x, y));
12981 static void KillPlayerUnlessExplosionProtected(int x, int y)
12983 if (!PLAYER_EXPLOSION_PROTECTED(x, y))
12984 KillPlayer(PLAYERINFO(x, y));
12987 void BuryPlayer(struct PlayerInfo *player)
12989 int jx = player->jx, jy = player->jy;
12991 if (!player->active)
12994 PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
12995 PlayLevelSound(jx, jy, SND_GAME_LOSING);
12997 player->GameOver = TRUE;
12998 RemovePlayer(player);
13001 void RemovePlayer(struct PlayerInfo *player)
13003 int jx = player->jx, jy = player->jy;
13004 int i, found = FALSE;
13006 player->present = FALSE;
13007 player->active = FALSE;
13009 if (!ExplodeField[jx][jy])
13010 StorePlayer[jx][jy] = 0;
13012 if (player->is_moving)
13013 TEST_DrawLevelField(player->last_jx, player->last_jy);
13015 for (i = 0; i < MAX_PLAYERS; i++)
13016 if (stored_player[i].active)
13020 AllPlayersGone = TRUE;
13026 static void setFieldForSnapping(int x, int y, int element, int direction)
13028 struct ElementInfo *ei = &element_info[element];
13029 int direction_bit = MV_DIR_TO_BIT(direction);
13030 int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
13031 int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
13032 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
13034 Feld[x][y] = EL_ELEMENT_SNAPPING;
13035 MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
13037 ResetGfxAnimation(x, y);
13039 GfxElement[x][y] = element;
13040 GfxAction[x][y] = action;
13041 GfxDir[x][y] = direction;
13042 GfxFrame[x][y] = -1;
13046 =============================================================================
13047 checkDiagonalPushing()
13048 -----------------------------------------------------------------------------
13049 check if diagonal input device direction results in pushing of object
13050 (by checking if the alternative direction is walkable, diggable, ...)
13051 =============================================================================
13054 static boolean checkDiagonalPushing(struct PlayerInfo *player,
13055 int x, int y, int real_dx, int real_dy)
13057 int jx, jy, dx, dy, xx, yy;
13059 if (real_dx == 0 || real_dy == 0) /* no diagonal direction => push */
13062 /* diagonal direction: check alternative direction */
13067 xx = jx + (dx == 0 ? real_dx : 0);
13068 yy = jy + (dy == 0 ? real_dy : 0);
13070 return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
13074 =============================================================================
13076 -----------------------------------------------------------------------------
13077 x, y: field next to player (non-diagonal) to try to dig to
13078 real_dx, real_dy: direction as read from input device (can be diagonal)
13079 =============================================================================
13082 static int DigField(struct PlayerInfo *player,
13083 int oldx, int oldy, int x, int y,
13084 int real_dx, int real_dy, int mode)
13086 boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
13087 boolean player_was_pushing = player->is_pushing;
13088 boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
13089 boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
13090 int jx = oldx, jy = oldy;
13091 int dx = x - jx, dy = y - jy;
13092 int nextx = x + dx, nexty = y + dy;
13093 int move_direction = (dx == -1 ? MV_LEFT :
13094 dx == +1 ? MV_RIGHT :
13096 dy == +1 ? MV_DOWN : MV_NONE);
13097 int opposite_direction = MV_DIR_OPPOSITE(move_direction);
13098 int dig_side = MV_DIR_OPPOSITE(move_direction);
13099 int old_element = Feld[jx][jy];
13100 int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
13103 if (is_player) /* function can also be called by EL_PENGUIN */
13105 if (player->MovPos == 0)
13107 player->is_digging = FALSE;
13108 player->is_collecting = FALSE;
13111 if (player->MovPos == 0) /* last pushing move finished */
13112 player->is_pushing = FALSE;
13114 if (mode == DF_NO_PUSH) /* player just stopped pushing */
13116 player->is_switching = FALSE;
13117 player->push_delay = -1;
13119 return MP_NO_ACTION;
13123 if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
13124 old_element = Back[jx][jy];
13126 /* in case of element dropped at player position, check background */
13127 else if (Back[jx][jy] != EL_EMPTY &&
13128 game.engine_version >= VERSION_IDENT(2,2,0,0))
13129 old_element = Back[jx][jy];
13131 if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
13132 return MP_NO_ACTION; /* field has no opening in this direction */
13134 if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
13135 return MP_NO_ACTION; /* field has no opening in this direction */
13137 if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
13141 Feld[jx][jy] = player->artwork_element;
13142 InitMovingField(jx, jy, MV_DOWN);
13143 Store[jx][jy] = EL_ACID;
13144 ContinueMoving(jx, jy);
13145 BuryPlayer(player);
13147 return MP_DONT_RUN_INTO;
13150 if (player_can_move && DONT_RUN_INTO(element))
13152 TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
13154 return MP_DONT_RUN_INTO;
13157 if (IS_MOVING(x, y) || IS_PLAYER(x, y))
13158 return MP_NO_ACTION;
13160 collect_count = element_info[element].collect_count_initial;
13162 if (!is_player && !IS_COLLECTIBLE(element)) /* penguin cannot collect it */
13163 return MP_NO_ACTION;
13165 if (game.engine_version < VERSION_IDENT(2,2,0,0))
13166 player_can_move = player_can_move_or_snap;
13168 if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
13169 game.engine_version >= VERSION_IDENT(2,2,0,0))
13171 CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
13172 player->index_bit, dig_side);
13173 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13174 player->index_bit, dig_side);
13176 if (element == EL_DC_LANDMINE)
13179 if (Feld[x][y] != element) /* field changed by snapping */
13182 return MP_NO_ACTION;
13185 if (player->gravity && is_player && !player->is_auto_moving &&
13186 canFallDown(player) && move_direction != MV_DOWN &&
13187 !canMoveToValidFieldWithGravity(jx, jy, move_direction))
13188 return MP_NO_ACTION; /* player cannot walk here due to gravity */
13190 if (player_can_move &&
13191 IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
13193 int sound_element = SND_ELEMENT(element);
13194 int sound_action = ACTION_WALKING;
13196 if (IS_RND_GATE(element))
13198 if (!player->key[RND_GATE_NR(element)])
13199 return MP_NO_ACTION;
13201 else if (IS_RND_GATE_GRAY(element))
13203 if (!player->key[RND_GATE_GRAY_NR(element)])
13204 return MP_NO_ACTION;
13206 else if (IS_RND_GATE_GRAY_ACTIVE(element))
13208 if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
13209 return MP_NO_ACTION;
13211 else if (element == EL_EXIT_OPEN ||
13212 element == EL_EM_EXIT_OPEN ||
13213 element == EL_EM_EXIT_OPENING ||
13214 element == EL_STEEL_EXIT_OPEN ||
13215 element == EL_EM_STEEL_EXIT_OPEN ||
13216 element == EL_EM_STEEL_EXIT_OPENING ||
13217 element == EL_SP_EXIT_OPEN ||
13218 element == EL_SP_EXIT_OPENING)
13220 sound_action = ACTION_PASSING; /* player is passing exit */
13222 else if (element == EL_EMPTY)
13224 sound_action = ACTION_MOVING; /* nothing to walk on */
13227 /* play sound from background or player, whatever is available */
13228 if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
13229 PlayLevelSoundElementAction(x, y, sound_element, sound_action);
13231 PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
13233 else if (player_can_move &&
13234 IS_PASSABLE(element) && canPassField(x, y, move_direction))
13236 if (!ACCESS_FROM(element, opposite_direction))
13237 return MP_NO_ACTION; /* field not accessible from this direction */
13239 if (CAN_MOVE(element)) /* only fixed elements can be passed! */
13240 return MP_NO_ACTION;
13242 if (IS_EM_GATE(element))
13244 if (!player->key[EM_GATE_NR(element)])
13245 return MP_NO_ACTION;
13247 else if (IS_EM_GATE_GRAY(element))
13249 if (!player->key[EM_GATE_GRAY_NR(element)])
13250 return MP_NO_ACTION;
13252 else if (IS_EM_GATE_GRAY_ACTIVE(element))
13254 if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
13255 return MP_NO_ACTION;
13257 else if (IS_EMC_GATE(element))
13259 if (!player->key[EMC_GATE_NR(element)])
13260 return MP_NO_ACTION;
13262 else if (IS_EMC_GATE_GRAY(element))
13264 if (!player->key[EMC_GATE_GRAY_NR(element)])
13265 return MP_NO_ACTION;
13267 else if (IS_EMC_GATE_GRAY_ACTIVE(element))
13269 if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
13270 return MP_NO_ACTION;
13272 else if (element == EL_DC_GATE_WHITE ||
13273 element == EL_DC_GATE_WHITE_GRAY ||
13274 element == EL_DC_GATE_WHITE_GRAY_ACTIVE)
13276 if (player->num_white_keys == 0)
13277 return MP_NO_ACTION;
13279 player->num_white_keys--;
13281 else if (IS_SP_PORT(element))
13283 if (element == EL_SP_GRAVITY_PORT_LEFT ||
13284 element == EL_SP_GRAVITY_PORT_RIGHT ||
13285 element == EL_SP_GRAVITY_PORT_UP ||
13286 element == EL_SP_GRAVITY_PORT_DOWN)
13287 player->gravity = !player->gravity;
13288 else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
13289 element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
13290 element == EL_SP_GRAVITY_ON_PORT_UP ||
13291 element == EL_SP_GRAVITY_ON_PORT_DOWN)
13292 player->gravity = TRUE;
13293 else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
13294 element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
13295 element == EL_SP_GRAVITY_OFF_PORT_UP ||
13296 element == EL_SP_GRAVITY_OFF_PORT_DOWN)
13297 player->gravity = FALSE;
13300 /* automatically move to the next field with double speed */
13301 player->programmed_action = move_direction;
13303 if (player->move_delay_reset_counter == 0)
13305 player->move_delay_reset_counter = 2; /* two double speed steps */
13307 DOUBLE_PLAYER_SPEED(player);
13310 PlayLevelSoundAction(x, y, ACTION_PASSING);
13312 else if (player_can_move_or_snap && IS_DIGGABLE(element))
13316 if (mode != DF_SNAP)
13318 GfxElement[x][y] = GFX_ELEMENT(element);
13319 player->is_digging = TRUE;
13322 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
13324 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
13325 player->index_bit, dig_side);
13327 if (mode == DF_SNAP)
13329 if (level.block_snap_field)
13330 setFieldForSnapping(x, y, element, move_direction);
13332 TestIfElementTouchesCustomElement(x, y); /* for empty space */
13334 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13335 player->index_bit, dig_side);
13338 else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
13342 if (is_player && mode != DF_SNAP)
13344 GfxElement[x][y] = element;
13345 player->is_collecting = TRUE;
13348 if (element == EL_SPEED_PILL)
13350 player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
13352 else if (element == EL_EXTRA_TIME && level.time > 0)
13354 TimeLeft += level.extra_time;
13356 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
13358 DisplayGameControlValues();
13360 else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
13362 player->shield_normal_time_left += level.shield_normal_time;
13363 if (element == EL_SHIELD_DEADLY)
13364 player->shield_deadly_time_left += level.shield_deadly_time;
13366 else if (element == EL_DYNAMITE ||
13367 element == EL_EM_DYNAMITE ||
13368 element == EL_SP_DISK_RED)
13370 if (player->inventory_size < MAX_INVENTORY_SIZE)
13371 player->inventory_element[player->inventory_size++] = element;
13373 DrawGameDoorValues();
13375 else if (element == EL_DYNABOMB_INCREASE_NUMBER)
13377 player->dynabomb_count++;
13378 player->dynabombs_left++;
13380 else if (element == EL_DYNABOMB_INCREASE_SIZE)
13382 player->dynabomb_size++;
13384 else if (element == EL_DYNABOMB_INCREASE_POWER)
13386 player->dynabomb_xl = TRUE;
13388 else if (IS_KEY(element))
13390 player->key[KEY_NR(element)] = TRUE;
13392 DrawGameDoorValues();
13394 else if (element == EL_DC_KEY_WHITE)
13396 player->num_white_keys++;
13398 /* display white keys? */
13399 /* DrawGameDoorValues(); */
13401 else if (IS_ENVELOPE(element))
13403 player->show_envelope = element;
13405 else if (element == EL_EMC_LENSES)
13407 game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
13409 RedrawAllInvisibleElementsForLenses();
13411 else if (element == EL_EMC_MAGNIFIER)
13413 game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
13415 RedrawAllInvisibleElementsForMagnifier();
13417 else if (IS_DROPPABLE(element) ||
13418 IS_THROWABLE(element)) /* can be collected and dropped */
13422 if (collect_count == 0)
13423 player->inventory_infinite_element = element;
13425 for (i = 0; i < collect_count; i++)
13426 if (player->inventory_size < MAX_INVENTORY_SIZE)
13427 player->inventory_element[player->inventory_size++] = element;
13429 DrawGameDoorValues();
13431 else if (collect_count > 0)
13433 local_player->gems_still_needed -= collect_count;
13434 if (local_player->gems_still_needed < 0)
13435 local_player->gems_still_needed = 0;
13437 game_panel_controls[GAME_PANEL_GEMS].value = local_player->gems_still_needed;
13439 DisplayGameControlValues();
13442 RaiseScoreElement(element);
13443 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
13446 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
13447 player->index_bit, dig_side);
13449 if (mode == DF_SNAP)
13451 if (level.block_snap_field)
13452 setFieldForSnapping(x, y, element, move_direction);
13454 TestIfElementTouchesCustomElement(x, y); /* for empty space */
13456 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13457 player->index_bit, dig_side);
13460 else if (player_can_move_or_snap && IS_PUSHABLE(element))
13462 if (mode == DF_SNAP && element != EL_BD_ROCK)
13463 return MP_NO_ACTION;
13465 if (CAN_FALL(element) && dy)
13466 return MP_NO_ACTION;
13468 if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
13469 !(element == EL_SPRING && level.use_spring_bug))
13470 return MP_NO_ACTION;
13472 if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
13473 ((move_direction & MV_VERTICAL &&
13474 ((element_info[element].move_pattern & MV_LEFT &&
13475 IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
13476 (element_info[element].move_pattern & MV_RIGHT &&
13477 IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
13478 (move_direction & MV_HORIZONTAL &&
13479 ((element_info[element].move_pattern & MV_UP &&
13480 IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
13481 (element_info[element].move_pattern & MV_DOWN &&
13482 IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
13483 return MP_NO_ACTION;
13485 /* do not push elements already moving away faster than player */
13486 if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
13487 ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
13488 return MP_NO_ACTION;
13490 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
13492 if (player->push_delay_value == -1 || !player_was_pushing)
13493 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13495 else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
13497 if (player->push_delay_value == -1)
13498 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13500 else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
13502 if (!player->is_pushing)
13503 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13506 player->is_pushing = TRUE;
13507 player->is_active = TRUE;
13509 if (!(IN_LEV_FIELD(nextx, nexty) &&
13510 (IS_FREE(nextx, nexty) ||
13511 (IS_SB_ELEMENT(element) &&
13512 Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY) ||
13513 (IS_CUSTOM_ELEMENT(element) &&
13514 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty)))))
13515 return MP_NO_ACTION;
13517 if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
13518 return MP_NO_ACTION;
13520 if (player->push_delay == -1) /* new pushing; restart delay */
13521 player->push_delay = 0;
13523 if (player->push_delay < player->push_delay_value &&
13524 !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
13525 element != EL_SPRING && element != EL_BALLOON)
13527 /* make sure that there is no move delay before next try to push */
13528 if (game.engine_version >= VERSION_IDENT(3,0,7,1))
13529 player->move_delay = 0;
13531 return MP_NO_ACTION;
13534 if (IS_CUSTOM_ELEMENT(element) &&
13535 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty))
13537 if (!DigFieldByCE(nextx, nexty, element))
13538 return MP_NO_ACTION;
13541 if (IS_SB_ELEMENT(element))
13543 if (element == EL_SOKOBAN_FIELD_FULL)
13545 Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
13546 local_player->sokobanfields_still_needed++;
13549 if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
13551 Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
13552 local_player->sokobanfields_still_needed--;
13555 Feld[x][y] = EL_SOKOBAN_OBJECT;
13557 if (Back[x][y] == Back[nextx][nexty])
13558 PlayLevelSoundAction(x, y, ACTION_PUSHING);
13559 else if (Back[x][y] != 0)
13560 PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
13563 PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
13566 if (local_player->sokobanfields_still_needed == 0 &&
13567 (game.emulation == EMU_SOKOBAN || level.auto_exit_sokoban))
13569 PlayerWins(player);
13571 PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
13575 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
13577 InitMovingField(x, y, move_direction);
13578 GfxAction[x][y] = ACTION_PUSHING;
13580 if (mode == DF_SNAP)
13581 ContinueMoving(x, y);
13583 MovPos[x][y] = (dx != 0 ? dx : dy);
13585 Pushed[x][y] = TRUE;
13586 Pushed[nextx][nexty] = TRUE;
13588 if (game.engine_version < VERSION_IDENT(2,2,0,7))
13589 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13591 player->push_delay_value = -1; /* get new value later */
13593 /* check for element change _after_ element has been pushed */
13594 if (game.use_change_when_pushing_bug)
13596 CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
13597 player->index_bit, dig_side);
13598 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
13599 player->index_bit, dig_side);
13602 else if (IS_SWITCHABLE(element))
13604 if (PLAYER_SWITCHING(player, x, y))
13606 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
13607 player->index_bit, dig_side);
13612 player->is_switching = TRUE;
13613 player->switch_x = x;
13614 player->switch_y = y;
13616 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
13618 if (element == EL_ROBOT_WHEEL)
13620 Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
13624 game.robot_wheel_active = TRUE;
13626 TEST_DrawLevelField(x, y);
13628 else if (element == EL_SP_TERMINAL)
13632 SCAN_PLAYFIELD(xx, yy)
13634 if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
13636 else if (Feld[xx][yy] == EL_SP_TERMINAL)
13637 Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
13640 else if (IS_BELT_SWITCH(element))
13642 ToggleBeltSwitch(x, y);
13644 else if (element == EL_SWITCHGATE_SWITCH_UP ||
13645 element == EL_SWITCHGATE_SWITCH_DOWN ||
13646 element == EL_DC_SWITCHGATE_SWITCH_UP ||
13647 element == EL_DC_SWITCHGATE_SWITCH_DOWN)
13649 ToggleSwitchgateSwitch(x, y);
13651 else if (element == EL_LIGHT_SWITCH ||
13652 element == EL_LIGHT_SWITCH_ACTIVE)
13654 ToggleLightSwitch(x, y);
13656 else if (element == EL_TIMEGATE_SWITCH ||
13657 element == EL_DC_TIMEGATE_SWITCH)
13659 ActivateTimegateSwitch(x, y);
13661 else if (element == EL_BALLOON_SWITCH_LEFT ||
13662 element == EL_BALLOON_SWITCH_RIGHT ||
13663 element == EL_BALLOON_SWITCH_UP ||
13664 element == EL_BALLOON_SWITCH_DOWN ||
13665 element == EL_BALLOON_SWITCH_NONE ||
13666 element == EL_BALLOON_SWITCH_ANY)
13668 game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT ? MV_LEFT :
13669 element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
13670 element == EL_BALLOON_SWITCH_UP ? MV_UP :
13671 element == EL_BALLOON_SWITCH_DOWN ? MV_DOWN :
13672 element == EL_BALLOON_SWITCH_NONE ? MV_NONE :
13675 else if (element == EL_LAMP)
13677 Feld[x][y] = EL_LAMP_ACTIVE;
13678 local_player->lights_still_needed--;
13680 ResetGfxAnimation(x, y);
13681 TEST_DrawLevelField(x, y);
13683 else if (element == EL_TIME_ORB_FULL)
13685 Feld[x][y] = EL_TIME_ORB_EMPTY;
13687 if (level.time > 0 || level.use_time_orb_bug)
13689 TimeLeft += level.time_orb_time;
13690 game.no_time_limit = FALSE;
13692 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
13694 DisplayGameControlValues();
13697 ResetGfxAnimation(x, y);
13698 TEST_DrawLevelField(x, y);
13700 else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
13701 element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
13705 game.ball_state = !game.ball_state;
13707 SCAN_PLAYFIELD(xx, yy)
13709 int e = Feld[xx][yy];
13711 if (game.ball_state)
13713 if (e == EL_EMC_MAGIC_BALL)
13714 CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
13715 else if (e == EL_EMC_MAGIC_BALL_SWITCH)
13716 CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
13720 if (e == EL_EMC_MAGIC_BALL_ACTIVE)
13721 CreateField(xx, yy, EL_EMC_MAGIC_BALL);
13722 else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
13723 CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
13728 CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
13729 player->index_bit, dig_side);
13731 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
13732 player->index_bit, dig_side);
13734 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
13735 player->index_bit, dig_side);
13741 if (!PLAYER_SWITCHING(player, x, y))
13743 player->is_switching = TRUE;
13744 player->switch_x = x;
13745 player->switch_y = y;
13747 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
13748 player->index_bit, dig_side);
13749 CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
13750 player->index_bit, dig_side);
13752 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
13753 player->index_bit, dig_side);
13754 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
13755 player->index_bit, dig_side);
13758 CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
13759 player->index_bit, dig_side);
13760 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
13761 player->index_bit, dig_side);
13763 return MP_NO_ACTION;
13766 player->push_delay = -1;
13768 if (is_player) /* function can also be called by EL_PENGUIN */
13770 if (Feld[x][y] != element) /* really digged/collected something */
13772 player->is_collecting = !player->is_digging;
13773 player->is_active = TRUE;
13780 static boolean DigFieldByCE(int x, int y, int digging_element)
13782 int element = Feld[x][y];
13784 if (!IS_FREE(x, y))
13786 int action = (IS_DIGGABLE(element) ? ACTION_DIGGING :
13787 IS_COLLECTIBLE(element) ? ACTION_COLLECTING :
13790 /* no element can dig solid indestructible elements */
13791 if (IS_INDESTRUCTIBLE(element) &&
13792 !IS_DIGGABLE(element) &&
13793 !IS_COLLECTIBLE(element))
13796 if (AmoebaNr[x][y] &&
13797 (element == EL_AMOEBA_FULL ||
13798 element == EL_BD_AMOEBA ||
13799 element == EL_AMOEBA_GROWING))
13801 AmoebaCnt[AmoebaNr[x][y]]--;
13802 AmoebaCnt2[AmoebaNr[x][y]]--;
13805 if (IS_MOVING(x, y))
13806 RemoveMovingField(x, y);
13810 TEST_DrawLevelField(x, y);
13813 /* if digged element was about to explode, prevent the explosion */
13814 ExplodeField[x][y] = EX_TYPE_NONE;
13816 PlayLevelSoundAction(x, y, action);
13819 Store[x][y] = EL_EMPTY;
13821 /* this makes it possible to leave the removed element again */
13822 if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element)))
13823 Store[x][y] = element;
13828 static boolean SnapField(struct PlayerInfo *player, int dx, int dy)
13830 int jx = player->jx, jy = player->jy;
13831 int x = jx + dx, y = jy + dy;
13832 int snap_direction = (dx == -1 ? MV_LEFT :
13833 dx == +1 ? MV_RIGHT :
13835 dy == +1 ? MV_DOWN : MV_NONE);
13836 boolean can_continue_snapping = (level.continuous_snapping &&
13837 WasJustFalling[x][y] < CHECK_DELAY_FALLING);
13839 if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
13842 if (!player->active || !IN_LEV_FIELD(x, y))
13850 if (player->MovPos == 0)
13851 player->is_pushing = FALSE;
13853 player->is_snapping = FALSE;
13855 if (player->MovPos == 0)
13857 player->is_moving = FALSE;
13858 player->is_digging = FALSE;
13859 player->is_collecting = FALSE;
13865 /* prevent snapping with already pressed snap key when not allowed */
13866 if (player->is_snapping && !can_continue_snapping)
13869 player->MovDir = snap_direction;
13871 if (player->MovPos == 0)
13873 player->is_moving = FALSE;
13874 player->is_digging = FALSE;
13875 player->is_collecting = FALSE;
13878 player->is_dropping = FALSE;
13879 player->is_dropping_pressed = FALSE;
13880 player->drop_pressed_delay = 0;
13882 if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
13885 player->is_snapping = TRUE;
13886 player->is_active = TRUE;
13888 if (player->MovPos == 0)
13890 player->is_moving = FALSE;
13891 player->is_digging = FALSE;
13892 player->is_collecting = FALSE;
13895 if (player->MovPos != 0) /* prevent graphic bugs in versions < 2.2.0 */
13896 TEST_DrawLevelField(player->last_jx, player->last_jy);
13898 TEST_DrawLevelField(x, y);
13903 static boolean DropElement(struct PlayerInfo *player)
13905 int old_element, new_element;
13906 int dropx = player->jx, dropy = player->jy;
13907 int drop_direction = player->MovDir;
13908 int drop_side = drop_direction;
13909 int drop_element = get_next_dropped_element(player);
13911 player->is_dropping_pressed = TRUE;
13913 /* do not drop an element on top of another element; when holding drop key
13914 pressed without moving, dropped element must move away before the next
13915 element can be dropped (this is especially important if the next element
13916 is dynamite, which can be placed on background for historical reasons) */
13917 if (PLAYER_DROPPING(player, dropx, dropy) && Feld[dropx][dropy] != EL_EMPTY)
13920 if (IS_THROWABLE(drop_element))
13922 dropx += GET_DX_FROM_DIR(drop_direction);
13923 dropy += GET_DY_FROM_DIR(drop_direction);
13925 if (!IN_LEV_FIELD(dropx, dropy))
13929 old_element = Feld[dropx][dropy]; /* old element at dropping position */
13930 new_element = drop_element; /* default: no change when dropping */
13932 /* check if player is active, not moving and ready to drop */
13933 if (!player->active || player->MovPos || player->drop_delay > 0)
13936 /* check if player has anything that can be dropped */
13937 if (new_element == EL_UNDEFINED)
13940 /* check if drop key was pressed long enough for EM style dynamite */
13941 if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
13944 /* check if anything can be dropped at the current position */
13945 if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
13948 /* collected custom elements can only be dropped on empty fields */
13949 if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
13952 if (old_element != EL_EMPTY)
13953 Back[dropx][dropy] = old_element; /* store old element on this field */
13955 ResetGfxAnimation(dropx, dropy);
13956 ResetRandomAnimationValue(dropx, dropy);
13958 if (player->inventory_size > 0 ||
13959 player->inventory_infinite_element != EL_UNDEFINED)
13961 if (player->inventory_size > 0)
13963 player->inventory_size--;
13965 DrawGameDoorValues();
13967 if (new_element == EL_DYNAMITE)
13968 new_element = EL_DYNAMITE_ACTIVE;
13969 else if (new_element == EL_EM_DYNAMITE)
13970 new_element = EL_EM_DYNAMITE_ACTIVE;
13971 else if (new_element == EL_SP_DISK_RED)
13972 new_element = EL_SP_DISK_RED_ACTIVE;
13975 Feld[dropx][dropy] = new_element;
13977 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
13978 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
13979 el2img(Feld[dropx][dropy]), 0);
13981 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
13983 /* needed if previous element just changed to "empty" in the last frame */
13984 ChangeCount[dropx][dropy] = 0; /* allow at least one more change */
13986 CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
13987 player->index_bit, drop_side);
13988 CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
13990 player->index_bit, drop_side);
13992 TestIfElementTouchesCustomElement(dropx, dropy);
13994 else /* player is dropping a dyna bomb */
13996 player->dynabombs_left--;
13998 Feld[dropx][dropy] = new_element;
14000 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
14001 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
14002 el2img(Feld[dropx][dropy]), 0);
14004 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
14007 if (Feld[dropx][dropy] == new_element) /* uninitialized unless CE change */
14008 InitField_WithBug1(dropx, dropy, FALSE);
14010 new_element = Feld[dropx][dropy]; /* element might have changed */
14012 if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
14013 element_info[new_element].move_pattern == MV_WHEN_DROPPED)
14015 if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
14016 MovDir[dropx][dropy] = drop_direction;
14018 ChangeCount[dropx][dropy] = 0; /* allow at least one more change */
14020 /* do not cause impact style collision by dropping elements that can fall */
14021 CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
14024 player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
14025 player->is_dropping = TRUE;
14027 player->drop_pressed_delay = 0;
14028 player->is_dropping_pressed = FALSE;
14030 player->drop_x = dropx;
14031 player->drop_y = dropy;
14036 /* ------------------------------------------------------------------------- */
14037 /* game sound playing functions */
14038 /* ------------------------------------------------------------------------- */
14040 static int *loop_sound_frame = NULL;
14041 static int *loop_sound_volume = NULL;
14043 void InitPlayLevelSound()
14045 int num_sounds = getSoundListSize();
14047 checked_free(loop_sound_frame);
14048 checked_free(loop_sound_volume);
14050 loop_sound_frame = checked_calloc(num_sounds * sizeof(int));
14051 loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
14054 static void PlayLevelSound(int x, int y, int nr)
14056 int sx = SCREENX(x), sy = SCREENY(y);
14057 int volume, stereo_position;
14058 int max_distance = 8;
14059 int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
14061 if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
14062 (!setup.sound_loops && IS_LOOP_SOUND(nr)))
14065 if (!IN_LEV_FIELD(x, y) ||
14066 sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
14067 sy < -max_distance || sy >= SCR_FIELDY + max_distance)
14070 volume = SOUND_MAX_VOLUME;
14072 if (!IN_SCR_FIELD(sx, sy))
14074 int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
14075 int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
14077 volume -= volume * (dx > dy ? dx : dy) / max_distance;
14080 stereo_position = (SOUND_MAX_LEFT +
14081 (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
14082 (SCR_FIELDX + 2 * max_distance));
14084 if (IS_LOOP_SOUND(nr))
14086 /* This assures that quieter loop sounds do not overwrite louder ones,
14087 while restarting sound volume comparison with each new game frame. */
14089 if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
14092 loop_sound_volume[nr] = volume;
14093 loop_sound_frame[nr] = FrameCounter;
14096 PlaySoundExt(nr, volume, stereo_position, type);
14099 static void PlayLevelSoundNearest(int x, int y, int sound_action)
14101 PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
14102 x > LEVELX(BX2) ? LEVELX(BX2) : x,
14103 y < LEVELY(BY1) ? LEVELY(BY1) :
14104 y > LEVELY(BY2) ? LEVELY(BY2) : y,
14108 static void PlayLevelSoundAction(int x, int y, int action)
14110 PlayLevelSoundElementAction(x, y, Feld[x][y], action);
14113 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
14115 int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
14117 if (sound_effect != SND_UNDEFINED)
14118 PlayLevelSound(x, y, sound_effect);
14121 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
14124 int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
14126 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14127 PlayLevelSound(x, y, sound_effect);
14130 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
14132 int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
14134 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14135 PlayLevelSound(x, y, sound_effect);
14138 static void StopLevelSoundActionIfLoop(int x, int y, int action)
14140 int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
14142 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14143 StopSound(sound_effect);
14146 static void PlayLevelMusic()
14148 if (levelset.music[level_nr] != MUS_UNDEFINED)
14149 PlayMusic(levelset.music[level_nr]); /* from config file */
14151 PlayMusic(MAP_NOCONF_MUSIC(level_nr)); /* from music dir */
14154 void PlayLevelSound_EM(int xx, int yy, int element_em, int sample)
14156 int element = (element_em > -1 ? map_element_EM_to_RND(element_em) : 0);
14157 int offset = (BorderElement == EL_STEELWALL ? 1 : 0);
14158 int x = xx - 1 - offset;
14159 int y = yy - 1 - offset;
14164 PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
14168 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14172 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14176 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14180 PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
14184 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14188 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14191 case SAMPLE_android_clone:
14192 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14195 case SAMPLE_android_move:
14196 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14199 case SAMPLE_spring:
14200 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14204 PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
14208 PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
14211 case SAMPLE_eater_eat:
14212 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14216 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14219 case SAMPLE_collect:
14220 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
14223 case SAMPLE_diamond:
14224 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14227 case SAMPLE_squash:
14228 /* !!! CHECK THIS !!! */
14230 PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
14232 PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
14236 case SAMPLE_wonderfall:
14237 PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
14241 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14245 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14249 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14253 PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
14257 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14261 PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
14264 case SAMPLE_wonder:
14265 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14269 PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
14272 case SAMPLE_exit_open:
14273 PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
14276 case SAMPLE_exit_leave:
14277 PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
14280 case SAMPLE_dynamite:
14281 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14285 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14289 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
14293 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14297 PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
14301 PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
14305 PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
14309 PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
14314 void PlayLevelSound_SP(int xx, int yy, int element_sp, int action_sp)
14316 int element = map_element_SP_to_RND(element_sp);
14317 int action = map_action_SP_to_RND(action_sp);
14318 int offset = (setup.sp_show_border_elements ? 0 : 1);
14319 int x = xx - offset;
14320 int y = yy - offset;
14322 PlayLevelSoundElementAction(x, y, element, action);
14325 void RaiseScore(int value)
14327 local_player->score += value;
14329 game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
14331 DisplayGameControlValues();
14334 void RaiseScoreElement(int element)
14339 case EL_BD_DIAMOND:
14340 case EL_EMERALD_YELLOW:
14341 case EL_EMERALD_RED:
14342 case EL_EMERALD_PURPLE:
14343 case EL_SP_INFOTRON:
14344 RaiseScore(level.score[SC_EMERALD]);
14347 RaiseScore(level.score[SC_DIAMOND]);
14350 RaiseScore(level.score[SC_CRYSTAL]);
14353 RaiseScore(level.score[SC_PEARL]);
14356 case EL_BD_BUTTERFLY:
14357 case EL_SP_ELECTRON:
14358 RaiseScore(level.score[SC_BUG]);
14361 case EL_BD_FIREFLY:
14362 case EL_SP_SNIKSNAK:
14363 RaiseScore(level.score[SC_SPACESHIP]);
14366 case EL_DARK_YAMYAM:
14367 RaiseScore(level.score[SC_YAMYAM]);
14370 RaiseScore(level.score[SC_ROBOT]);
14373 RaiseScore(level.score[SC_PACMAN]);
14376 RaiseScore(level.score[SC_NUT]);
14379 case EL_EM_DYNAMITE:
14380 case EL_SP_DISK_RED:
14381 case EL_DYNABOMB_INCREASE_NUMBER:
14382 case EL_DYNABOMB_INCREASE_SIZE:
14383 case EL_DYNABOMB_INCREASE_POWER:
14384 RaiseScore(level.score[SC_DYNAMITE]);
14386 case EL_SHIELD_NORMAL:
14387 case EL_SHIELD_DEADLY:
14388 RaiseScore(level.score[SC_SHIELD]);
14390 case EL_EXTRA_TIME:
14391 RaiseScore(level.extra_time_score);
14405 case EL_DC_KEY_WHITE:
14406 RaiseScore(level.score[SC_KEY]);
14409 RaiseScore(element_info[element].collect_score);
14414 void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message)
14416 if (skip_request || Request(message, REQ_ASK | REQ_STAY_CLOSED))
14418 /* closing door required in case of envelope style request dialogs */
14420 CloseDoor(DOOR_CLOSE_1);
14422 #if defined(NETWORK_AVALIABLE)
14423 if (options.network)
14424 SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
14430 FadeSkipNextFadeIn();
14432 game_status = GAME_MODE_MAIN;
14434 DrawAndFadeInMainMenu(REDRAW_FIELD);
14438 game_status = GAME_MODE_MAIN;
14440 DrawAndFadeInMainMenu(REDRAW_FIELD);
14444 else /* continue playing the game */
14446 if (tape.playing && tape.deactivate_display)
14447 TapeDeactivateDisplayOff(TRUE);
14449 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
14451 if (tape.playing && tape.deactivate_display)
14452 TapeDeactivateDisplayOn();
14456 void RequestQuitGame(boolean ask_if_really_quit)
14458 boolean quick_quit = (!ask_if_really_quit || level_editor_test_game);
14459 boolean skip_request = AllPlayersGone || quick_quit;
14461 RequestQuitGameExt(skip_request, quick_quit,
14462 "Do you really want to quit the game?");
14466 /* ------------------------------------------------------------------------- */
14467 /* random generator functions */
14468 /* ------------------------------------------------------------------------- */
14470 unsigned int InitEngineRandom_RND(int seed)
14472 game.num_random_calls = 0;
14474 return InitEngineRandom(seed);
14477 unsigned int RND(int max)
14481 game.num_random_calls++;
14483 return GetEngineRandom(max);
14490 /* ------------------------------------------------------------------------- */
14491 /* game engine snapshot handling functions */
14492 /* ------------------------------------------------------------------------- */
14494 struct EngineSnapshotInfo
14496 /* runtime values for custom element collect score */
14497 int collect_score[NUM_CUSTOM_ELEMENTS];
14499 /* runtime values for group element choice position */
14500 int choice_pos[NUM_GROUP_ELEMENTS];
14502 /* runtime values for belt position animations */
14503 int belt_graphic[4][NUM_BELT_PARTS];
14504 int belt_anim_mode[4][NUM_BELT_PARTS];
14507 static struct EngineSnapshotInfo engine_snapshot_rnd;
14508 static char *snapshot_level_identifier = NULL;
14509 static int snapshot_level_nr = -1;
14511 static void SaveEngineSnapshotValues_RND()
14513 static int belt_base_active_element[4] =
14515 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
14516 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
14517 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
14518 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
14522 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
14524 int element = EL_CUSTOM_START + i;
14526 engine_snapshot_rnd.collect_score[i] = element_info[element].collect_score;
14529 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
14531 int element = EL_GROUP_START + i;
14533 engine_snapshot_rnd.choice_pos[i] = element_info[element].group->choice_pos;
14536 for (i = 0; i < 4; i++)
14538 for (j = 0; j < NUM_BELT_PARTS; j++)
14540 int element = belt_base_active_element[i] + j;
14541 int graphic = el2img(element);
14542 int anim_mode = graphic_info[graphic].anim_mode;
14544 engine_snapshot_rnd.belt_graphic[i][j] = graphic;
14545 engine_snapshot_rnd.belt_anim_mode[i][j] = anim_mode;
14550 static void LoadEngineSnapshotValues_RND()
14552 unsigned int num_random_calls = game.num_random_calls;
14555 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
14557 int element = EL_CUSTOM_START + i;
14559 element_info[element].collect_score = engine_snapshot_rnd.collect_score[i];
14562 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
14564 int element = EL_GROUP_START + i;
14566 element_info[element].group->choice_pos = engine_snapshot_rnd.choice_pos[i];
14569 for (i = 0; i < 4; i++)
14571 for (j = 0; j < NUM_BELT_PARTS; j++)
14573 int graphic = engine_snapshot_rnd.belt_graphic[i][j];
14574 int anim_mode = engine_snapshot_rnd.belt_anim_mode[i][j];
14576 graphic_info[graphic].anim_mode = anim_mode;
14580 if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
14582 InitRND(tape.random_seed);
14583 for (i = 0; i < num_random_calls; i++)
14587 if (game.num_random_calls != num_random_calls)
14589 Error(ERR_INFO, "number of random calls out of sync");
14590 Error(ERR_INFO, "number of random calls should be %d", num_random_calls);
14591 Error(ERR_INFO, "number of random calls is %d", game.num_random_calls);
14592 Error(ERR_EXIT, "this should not happen -- please debug");
14596 void FreeEngineSnapshotSingle()
14598 FreeSnapshotSingle();
14600 setString(&snapshot_level_identifier, NULL);
14601 snapshot_level_nr = -1;
14604 void FreeEngineSnapshotList()
14606 FreeSnapshotList();
14609 ListNode *SaveEngineSnapshotBuffers()
14611 ListNode *buffers = NULL;
14613 /* copy some special values to a structure better suited for the snapshot */
14615 if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
14616 SaveEngineSnapshotValues_RND();
14617 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
14618 SaveEngineSnapshotValues_EM();
14619 if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
14620 SaveEngineSnapshotValues_SP(&buffers);
14622 /* save values stored in special snapshot structure */
14624 if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
14625 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
14626 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
14627 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
14628 if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
14629 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_sp));
14631 /* save further RND engine values */
14633 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(stored_player));
14634 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(game));
14635 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(tape));
14637 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ZX));
14638 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ZY));
14639 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExitX));
14640 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExitY));
14642 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(FrameCounter));
14643 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeFrames));
14644 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimePlayed));
14645 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeLeft));
14646 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TapeTime));
14648 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovDir));
14649 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovPos));
14650 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenGfxPos));
14652 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScrollStepSize));
14654 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AllPlayersGone));
14656 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt));
14657 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2));
14659 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Feld));
14660 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovPos));
14661 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDir));
14662 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDelay));
14663 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeDelay));
14664 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangePage));
14665 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CustomValue));
14666 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store));
14667 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store2));
14668 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(StorePlayer));
14669 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Back));
14670 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaNr));
14671 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustMoving));
14672 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustFalling));
14673 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckCollision));
14674 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckImpact));
14675 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Stop));
14676 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Pushed));
14678 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeCount));
14679 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeEvent));
14681 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodePhase));
14682 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeDelay));
14683 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeField));
14685 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(RunnerVisit));
14686 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(PlayerVisit));
14688 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxFrame));
14689 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxRandom));
14690 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxElement));
14691 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxAction));
14692 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxDir));
14694 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_x));
14695 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_y));
14697 /* save level identification information */
14699 setString(&snapshot_level_identifier, leveldir_current->identifier);
14700 snapshot_level_nr = level_nr;
14703 ListNode *node = engine_snapshot_list_rnd;
14706 while (node != NULL)
14708 num_bytes += ((struct EngineSnapshotNodeInfo *)node->content)->size;
14713 printf("::: size of engine snapshot: %d bytes\n", num_bytes);
14719 void SaveEngineSnapshotSingle()
14721 ListNode *buffers = SaveEngineSnapshotBuffers();
14723 /* finally save all snapshot buffers to single snapshot */
14724 SaveSnapshotSingle(buffers);
14727 void SaveEngineSnapshotToList()
14729 ListNode *buffers = SaveEngineSnapshotBuffers();
14731 /* finally save all snapshot buffers to snapshot list */
14732 SaveSnapshotToList(buffers);
14735 void LoadEngineSnapshotValues()
14737 /* restore special values from snapshot structure */
14739 if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
14740 LoadEngineSnapshotValues_RND();
14741 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
14742 LoadEngineSnapshotValues_EM();
14743 if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
14744 LoadEngineSnapshotValues_SP();
14747 void LoadEngineSnapshotSingle()
14749 LoadSnapshotSingle();
14751 LoadEngineSnapshotValues();
14754 void LoadEngineSnapshot_Undo(int steps)
14756 LoadSnapshotFromList_Older(steps);
14758 LoadEngineSnapshotValues();
14761 void LoadEngineSnapshot_Redo(int steps)
14763 LoadSnapshotFromList_Newer(steps);
14765 LoadEngineSnapshotValues();
14768 boolean CheckEngineSnapshot()
14770 return (strEqual(snapshot_level_identifier, leveldir_current->identifier) &&
14771 snapshot_level_nr == level_nr);
14775 /* ---------- new game button stuff ---------------------------------------- */
14783 } gamebutton_info[NUM_GAME_BUTTONS] =
14786 IMG_GAME_BUTTON_GFX_STOP, &game.button.stop,
14787 GAME_CTRL_ID_STOP, "stop game"
14790 IMG_GAME_BUTTON_GFX_PAUSE, &game.button.pause,
14791 GAME_CTRL_ID_PAUSE, "pause game"
14794 IMG_GAME_BUTTON_GFX_PLAY, &game.button.play,
14795 GAME_CTRL_ID_PLAY, "play game"
14798 IMG_GAME_BUTTON_GFX_UNDO, &game.button.undo,
14799 GAME_CTRL_ID_UNDO, "undo step"
14802 IMG_GAME_BUTTON_GFX_REDO, &game.button.redo,
14803 GAME_CTRL_ID_REDO, "redo step"
14806 IMG_GAME_BUTTON_GFX_SAVE, &game.button.save,
14807 GAME_CTRL_ID_SAVE, "save game"
14810 IMG_GAME_BUTTON_GFX_LOAD, &game.button.load,
14811 GAME_CTRL_ID_LOAD, "load game"
14814 IMG_GAME_BUTTON_GFX_SOUND_MUSIC, &game.button.sound_music,
14815 SOUND_CTRL_ID_MUSIC, "background music on/off"
14818 IMG_GAME_BUTTON_GFX_SOUND_LOOPS, &game.button.sound_loops,
14819 SOUND_CTRL_ID_LOOPS, "sound loops on/off"
14822 IMG_GAME_BUTTON_GFX_SOUND_SIMPLE, &game.button.sound_simple,
14823 SOUND_CTRL_ID_SIMPLE, "normal sounds on/off"
14827 void CreateGameButtons()
14831 for (i = 0; i < NUM_GAME_BUTTONS; i++)
14833 struct GraphicInfo *gfx = &graphic_info[gamebutton_info[i].graphic];
14834 struct XY *pos = gamebutton_info[i].pos;
14835 struct GadgetInfo *gi;
14838 unsigned int event_mask;
14839 int base_x = (tape.show_game_buttons ? VX : DX);
14840 int base_y = (tape.show_game_buttons ? VY : DY);
14841 int gd_x = gfx->src_x;
14842 int gd_y = gfx->src_y;
14843 int gd_xp = gfx->src_x + gfx->pressed_xoffset;
14844 int gd_yp = gfx->src_y + gfx->pressed_yoffset;
14845 int gd_xa = gfx->src_x + gfx->active_xoffset;
14846 int gd_ya = gfx->src_y + gfx->active_yoffset;
14847 int gd_xap = gfx->src_x + gfx->active_xoffset + gfx->pressed_xoffset;
14848 int gd_yap = gfx->src_y + gfx->active_yoffset + gfx->pressed_yoffset;
14851 if (gfx->bitmap == NULL)
14853 game_gadget[id] = NULL;
14858 if (id == GAME_CTRL_ID_STOP ||
14859 id == GAME_CTRL_ID_PAUSE ||
14860 id == GAME_CTRL_ID_PLAY ||
14861 id == GAME_CTRL_ID_SAVE ||
14862 id == GAME_CTRL_ID_LOAD)
14864 button_type = GD_TYPE_NORMAL_BUTTON;
14866 event_mask = GD_EVENT_RELEASED;
14868 else if (id == GAME_CTRL_ID_UNDO ||
14869 id == GAME_CTRL_ID_REDO)
14871 button_type = GD_TYPE_NORMAL_BUTTON;
14873 event_mask = GD_EVENT_PRESSED | GD_EVENT_REPEATED;
14877 button_type = GD_TYPE_CHECK_BUTTON;
14879 ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
14880 (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
14881 (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
14882 event_mask = GD_EVENT_PRESSED;
14885 gi = CreateGadget(GDI_CUSTOM_ID, id,
14886 GDI_INFO_TEXT, gamebutton_info[i].infotext,
14887 GDI_X, base_x + GDI_ACTIVE_POS(pos->x),
14888 GDI_Y, base_y + GDI_ACTIVE_POS(pos->y),
14889 GDI_WIDTH, gfx->width,
14890 GDI_HEIGHT, gfx->height,
14891 GDI_TYPE, button_type,
14892 GDI_STATE, GD_BUTTON_UNPRESSED,
14893 GDI_CHECKED, checked,
14894 GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
14895 GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
14896 GDI_ALT_DESIGN_UNPRESSED, gfx->bitmap, gd_xa, gd_ya,
14897 GDI_ALT_DESIGN_PRESSED, gfx->bitmap, gd_xap, gd_yap,
14898 GDI_DIRECT_DRAW, FALSE,
14899 GDI_EVENT_MASK, event_mask,
14900 GDI_CALLBACK_ACTION, HandleGameButtons,
14904 Error(ERR_EXIT, "cannot create gadget");
14906 game_gadget[id] = gi;
14910 void FreeGameButtons()
14914 for (i = 0; i < NUM_GAME_BUTTONS; i++)
14915 FreeGadget(game_gadget[i]);
14918 void MapStopPlayButtons()
14920 UnmapGadget(game_gadget[GAME_CTRL_ID_UNDO]);
14921 UnmapGadget(game_gadget[GAME_CTRL_ID_REDO]);
14923 MapGadget(game_gadget[GAME_CTRL_ID_STOP]);
14924 MapGadget(game_gadget[GAME_CTRL_ID_PLAY]);
14927 void MapUndoRedoButtons()
14929 UnmapGadget(game_gadget[GAME_CTRL_ID_STOP]);
14930 UnmapGadget(game_gadget[GAME_CTRL_ID_PLAY]);
14932 MapGadget(game_gadget[GAME_CTRL_ID_UNDO]);
14933 MapGadget(game_gadget[GAME_CTRL_ID_REDO]);
14936 void MapGameButtons()
14940 for (i = 0; i < NUM_GAME_BUTTONS; i++)
14941 if (i != GAME_CTRL_ID_UNDO &&
14942 i != GAME_CTRL_ID_REDO)
14943 MapGadget(game_gadget[i]);
14946 void UnmapGameButtons()
14950 for (i = 0; i < NUM_GAME_BUTTONS; i++)
14951 UnmapGadget(game_gadget[i]);
14954 void RedrawGameButtons()
14958 for (i = 0; i < NUM_GAME_BUTTONS; i++)
14959 RedrawGadget(game_gadget[i]);
14961 // RedrawGadget() may have set REDRAW_ALL if buttons are defined off-area
14962 redraw_mask &= ~REDRAW_ALL;
14965 void GameUndoRedoExt()
14967 ClearPlayerAction();
14969 tape.pausing = TRUE;
14972 UpdateAndDisplayGameControlValues();
14974 DrawCompleteVideoDisplay();
14975 DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
14976 DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
14981 void GameUndo(int steps)
14983 if (!CheckEngineSnapshot())
14986 LoadEngineSnapshot_Undo(steps);
14991 void GameRedo(int steps)
14993 if (!CheckEngineSnapshot())
14996 LoadEngineSnapshot_Redo(steps);
15001 static void HandleGameButtonsExt(int id, int button)
15003 int steps = BUTTON_STEPSIZE(button);
15004 boolean handle_game_buttons =
15005 (game_status == GAME_MODE_PLAYING ||
15006 (game_status == GAME_MODE_MAIN && tape.show_game_buttons));
15008 if (!handle_game_buttons)
15013 case GAME_CTRL_ID_STOP:
15014 if (game_status == GAME_MODE_MAIN)
15020 RequestQuitGame(TRUE);
15024 case GAME_CTRL_ID_PAUSE:
15025 if (options.network && game_status == GAME_MODE_PLAYING)
15027 #if defined(NETWORK_AVALIABLE)
15029 SendToServer_ContinuePlaying();
15031 SendToServer_PausePlaying();
15035 TapeTogglePause(TAPE_TOGGLE_MANUAL);
15038 case GAME_CTRL_ID_PLAY:
15039 if (game_status == GAME_MODE_MAIN)
15041 StartGameActions(options.network, setup.autorecord, level.random_seed);
15043 else if (tape.pausing)
15045 #if defined(NETWORK_AVALIABLE)
15046 if (options.network)
15047 SendToServer_ContinuePlaying();
15051 tape.pausing = FALSE;
15052 DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF, 0);
15057 case GAME_CTRL_ID_UNDO:
15061 case GAME_CTRL_ID_REDO:
15065 case GAME_CTRL_ID_SAVE:
15069 case GAME_CTRL_ID_LOAD:
15073 case SOUND_CTRL_ID_MUSIC:
15074 if (setup.sound_music)
15076 setup.sound_music = FALSE;
15080 else if (audio.music_available)
15082 setup.sound = setup.sound_music = TRUE;
15084 SetAudioMode(setup.sound);
15090 case SOUND_CTRL_ID_LOOPS:
15091 if (setup.sound_loops)
15092 setup.sound_loops = FALSE;
15093 else if (audio.loops_available)
15095 setup.sound = setup.sound_loops = TRUE;
15097 SetAudioMode(setup.sound);
15101 case SOUND_CTRL_ID_SIMPLE:
15102 if (setup.sound_simple)
15103 setup.sound_simple = FALSE;
15104 else if (audio.sound_available)
15106 setup.sound = setup.sound_simple = TRUE;
15108 SetAudioMode(setup.sound);
15117 static void HandleGameButtons(struct GadgetInfo *gi)
15119 HandleGameButtonsExt(gi->custom_id, gi->event.button);
15122 void HandleSoundButtonKeys(Key key)
15125 if (key == setup.shortcut.sound_simple)
15126 ClickOnGadget(game_gadget[SOUND_CTRL_ID_SIMPLE], MB_LEFTBUTTON);
15127 else if (key == setup.shortcut.sound_loops)
15128 ClickOnGadget(game_gadget[SOUND_CTRL_ID_LOOPS], MB_LEFTBUTTON);
15129 else if (key == setup.shortcut.sound_music)
15130 ClickOnGadget(game_gadget[SOUND_CTRL_ID_MUSIC], MB_LEFTBUTTON);