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 (!tape.recording)
10687 if ((!player->is_moving && player_was_moving) ||
10688 (player->MovPos == 0 && player_was_moving) ||
10689 (player->is_snapping && !player_was_snapping) ||
10690 (player->is_dropping && !player_was_dropping))
10692 SaveEngineSnapshotToList();
10694 player_was_moving = FALSE;
10695 player_was_snapping = TRUE;
10696 player_was_dropping = TRUE;
10700 if (player->is_moving)
10701 player_was_moving = TRUE;
10703 if (!player->is_snapping)
10704 player_was_snapping = FALSE;
10706 if (!player->is_dropping)
10707 player_was_dropping = FALSE;
10711 static void CheckSingleStepMode(struct PlayerInfo *player)
10713 if (tape.single_step && tape.recording && !tape.pausing)
10715 /* as it is called "single step mode", just return to pause mode when the
10716 player stopped moving after one tile (or never starts moving at all) */
10717 if (!player->is_moving && !player->is_pushing)
10719 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
10720 SnapField(player, 0, 0); /* stop snapping */
10724 CheckSaveEngineSnapshot(player);
10727 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
10729 int left = player_action & JOY_LEFT;
10730 int right = player_action & JOY_RIGHT;
10731 int up = player_action & JOY_UP;
10732 int down = player_action & JOY_DOWN;
10733 int button1 = player_action & JOY_BUTTON_1;
10734 int button2 = player_action & JOY_BUTTON_2;
10735 int dx = (left ? -1 : right ? 1 : 0);
10736 int dy = (up ? -1 : down ? 1 : 0);
10738 if (!player->active || tape.pausing)
10744 SnapField(player, dx, dy);
10748 DropElement(player);
10750 MovePlayer(player, dx, dy);
10753 CheckSingleStepMode(player);
10755 SetPlayerWaiting(player, FALSE);
10757 return player_action;
10761 /* no actions for this player (no input at player's configured device) */
10763 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
10764 SnapField(player, 0, 0);
10765 CheckGravityMovementWhenNotMoving(player);
10767 if (player->MovPos == 0)
10768 SetPlayerWaiting(player, TRUE);
10770 if (player->MovPos == 0) /* needed for tape.playing */
10771 player->is_moving = FALSE;
10773 player->is_dropping = FALSE;
10774 player->is_dropping_pressed = FALSE;
10775 player->drop_pressed_delay = 0;
10777 CheckSingleStepMode(player);
10783 static void CheckLevelTime()
10787 /* !!! SAME CODE AS IN "GameActions()" -- FIX THIS !!! */
10788 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
10790 if (level.native_em_level->lev->home == 0) /* all players at home */
10792 PlayerWins(local_player);
10794 AllPlayersGone = TRUE;
10796 level.native_em_level->lev->home = -1;
10799 if (level.native_em_level->ply[0]->alive == 0 &&
10800 level.native_em_level->ply[1]->alive == 0 &&
10801 level.native_em_level->ply[2]->alive == 0 &&
10802 level.native_em_level->ply[3]->alive == 0) /* all dead */
10803 AllPlayersGone = TRUE;
10805 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
10807 if (game_sp.LevelSolved &&
10808 !game_sp.GameOver) /* game won */
10810 PlayerWins(local_player);
10812 game_sp.GameOver = TRUE;
10814 AllPlayersGone = TRUE;
10817 if (game_sp.GameOver) /* game lost */
10818 AllPlayersGone = TRUE;
10821 if (TimeFrames >= FRAMES_PER_SECOND)
10826 for (i = 0; i < MAX_PLAYERS; i++)
10828 struct PlayerInfo *player = &stored_player[i];
10830 if (SHIELD_ON(player))
10832 player->shield_normal_time_left--;
10834 if (player->shield_deadly_time_left > 0)
10835 player->shield_deadly_time_left--;
10839 if (!local_player->LevelSolved && !level.use_step_counter)
10847 if (TimeLeft <= 10 && setup.time_limit)
10848 PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
10850 /* this does not make sense: game_panel_controls[GAME_PANEL_TIME].value
10851 is reset from other values in UpdateGameDoorValues() -- FIX THIS */
10853 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
10855 if (!TimeLeft && setup.time_limit)
10857 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
10858 level.native_em_level->lev->killed_out_of_time = TRUE;
10860 for (i = 0; i < MAX_PLAYERS; i++)
10861 KillPlayer(&stored_player[i]);
10864 else if (game.no_time_limit && !AllPlayersGone) /* level w/o time limit */
10866 game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
10869 level.native_em_level->lev->time =
10870 (game.no_time_limit ? TimePlayed : TimeLeft);
10873 if (tape.recording || tape.playing)
10874 DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
10877 if (tape.recording || tape.playing)
10878 DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
10880 UpdateAndDisplayGameControlValues();
10883 void AdvanceFrameAndPlayerCounters(int player_nr)
10887 /* advance frame counters (global frame counter and time frame counter) */
10891 /* advance player counters (counters for move delay, move animation etc.) */
10892 for (i = 0; i < MAX_PLAYERS; i++)
10894 boolean advance_player_counters = (player_nr == -1 || player_nr == i);
10895 int move_delay_value = stored_player[i].move_delay_value;
10896 int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
10898 if (!advance_player_counters) /* not all players may be affected */
10901 if (move_frames == 0) /* less than one move per game frame */
10903 int stepsize = TILEX / move_delay_value;
10904 int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
10905 int count = (stored_player[i].is_moving ?
10906 ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
10908 if (count % delay == 0)
10912 stored_player[i].Frame += move_frames;
10914 if (stored_player[i].MovPos != 0)
10915 stored_player[i].StepFrame += move_frames;
10917 if (stored_player[i].move_delay > 0)
10918 stored_player[i].move_delay--;
10920 /* due to bugs in previous versions, counter must count up, not down */
10921 if (stored_player[i].push_delay != -1)
10922 stored_player[i].push_delay++;
10924 if (stored_player[i].drop_delay > 0)
10925 stored_player[i].drop_delay--;
10927 if (stored_player[i].is_dropping_pressed)
10928 stored_player[i].drop_pressed_delay++;
10932 void StartGameActions(boolean init_network_game, boolean record_tape,
10935 unsigned int new_random_seed = InitRND(random_seed);
10938 TapeStartRecording(new_random_seed);
10940 #if defined(NETWORK_AVALIABLE)
10941 if (init_network_game)
10943 SendToServer_StartPlaying();
10954 static unsigned int game_frame_delay = 0;
10955 unsigned int game_frame_delay_value;
10956 byte *recorded_player_action;
10957 byte summarized_player_action = 0;
10958 byte tape_action[MAX_PLAYERS];
10961 /* detect endless loops, caused by custom element programming */
10962 if (recursion_loop_detected && recursion_loop_depth == 0)
10964 char *message = getStringCat3("Internal Error! Element ",
10965 EL_NAME(recursion_loop_element),
10966 " caused endless loop! Quit the game?");
10968 Error(ERR_WARN, "element '%s' caused endless loop in game engine",
10969 EL_NAME(recursion_loop_element));
10971 RequestQuitGameExt(FALSE, level_editor_test_game, message);
10973 recursion_loop_detected = FALSE; /* if game should be continued */
10980 if (game.restart_level)
10981 StartGameActions(options.network, setup.autorecord, level.random_seed);
10983 /* !!! SAME CODE AS IN "CheckLevelTime()" -- FIX THIS !!! */
10984 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
10986 if (level.native_em_level->lev->home == 0) /* all players at home */
10988 PlayerWins(local_player);
10990 AllPlayersGone = TRUE;
10992 level.native_em_level->lev->home = -1;
10995 if (level.native_em_level->ply[0]->alive == 0 &&
10996 level.native_em_level->ply[1]->alive == 0 &&
10997 level.native_em_level->ply[2]->alive == 0 &&
10998 level.native_em_level->ply[3]->alive == 0) /* all dead */
10999 AllPlayersGone = TRUE;
11001 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11003 if (game_sp.LevelSolved &&
11004 !game_sp.GameOver) /* game won */
11006 PlayerWins(local_player);
11008 game_sp.GameOver = TRUE;
11010 AllPlayersGone = TRUE;
11013 if (game_sp.GameOver) /* game lost */
11014 AllPlayersGone = TRUE;
11017 if (local_player->LevelSolved && !local_player->LevelSolved_GameEnd)
11020 if (AllPlayersGone && !TAPE_IS_STOPPED(tape))
11023 if (game_status != GAME_MODE_PLAYING) /* status might have changed */
11026 game_frame_delay_value =
11027 (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
11029 if (tape.playing && tape.warp_forward && !tape.pausing)
11030 game_frame_delay_value = 0;
11032 /* ---------- main game synchronization point ---------- */
11034 WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11036 if (network_playing && !network_player_action_received)
11038 /* try to get network player actions in time */
11040 #if defined(NETWORK_AVALIABLE)
11041 /* last chance to get network player actions without main loop delay */
11042 HandleNetworking();
11045 /* game was quit by network peer */
11046 if (game_status != GAME_MODE_PLAYING)
11049 if (!network_player_action_received)
11050 return; /* failed to get network player actions in time */
11052 /* do not yet reset "network_player_action_received" (for tape.pausing) */
11058 /* at this point we know that we really continue executing the game */
11060 network_player_action_received = FALSE;
11062 /* when playing tape, read previously recorded player input from tape data */
11063 recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
11065 /* TapePlayAction() may return NULL when toggling to "pause before death" */
11069 if (tape.set_centered_player)
11071 game.centered_player_nr_next = tape.centered_player_nr_next;
11072 game.set_centered_player = TRUE;
11075 for (i = 0; i < MAX_PLAYERS; i++)
11077 summarized_player_action |= stored_player[i].action;
11079 if (!network_playing && (game.team_mode || tape.playing))
11080 stored_player[i].effective_action = stored_player[i].action;
11083 #if defined(NETWORK_AVALIABLE)
11084 if (network_playing)
11085 SendToServer_MovePlayer(summarized_player_action);
11088 if (!options.network && !game.team_mode)
11089 local_player->effective_action = summarized_player_action;
11091 if (tape.recording &&
11093 setup.input_on_focus &&
11094 game.centered_player_nr != -1)
11096 for (i = 0; i < MAX_PLAYERS; i++)
11097 stored_player[i].effective_action =
11098 (i == game.centered_player_nr ? summarized_player_action : 0);
11101 if (recorded_player_action != NULL)
11102 for (i = 0; i < MAX_PLAYERS; i++)
11103 stored_player[i].effective_action = recorded_player_action[i];
11105 for (i = 0; i < MAX_PLAYERS; i++)
11107 tape_action[i] = stored_player[i].effective_action;
11109 /* (this may happen in the RND game engine if a player was not present on
11110 the playfield on level start, but appeared later from a custom element */
11111 if (setup.team_mode &&
11114 !tape.player_participates[i])
11115 tape.player_participates[i] = TRUE;
11118 /* only record actions from input devices, but not programmed actions */
11119 if (tape.recording)
11120 TapeRecordAction(tape_action);
11122 #if USE_NEW_PLAYER_ASSIGNMENTS
11123 // !!! also map player actions in single player mode !!!
11124 // if (game.team_mode)
11126 byte mapped_action[MAX_PLAYERS];
11128 #if DEBUG_PLAYER_ACTIONS
11130 for (i = 0; i < MAX_PLAYERS; i++)
11131 printf(" %d, ", stored_player[i].effective_action);
11134 for (i = 0; i < MAX_PLAYERS; i++)
11135 mapped_action[i] = stored_player[map_player_action[i]].effective_action;
11137 for (i = 0; i < MAX_PLAYERS; i++)
11138 stored_player[i].effective_action = mapped_action[i];
11140 #if DEBUG_PLAYER_ACTIONS
11142 for (i = 0; i < MAX_PLAYERS; i++)
11143 printf(" %d, ", stored_player[i].effective_action);
11147 #if DEBUG_PLAYER_ACTIONS
11151 for (i = 0; i < MAX_PLAYERS; i++)
11152 printf(" %d, ", stored_player[i].effective_action);
11158 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11160 GameActions_EM_Main();
11162 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11164 GameActions_SP_Main();
11172 void GameActions_EM_Main()
11174 byte effective_action[MAX_PLAYERS];
11175 boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11178 for (i = 0; i < MAX_PLAYERS; i++)
11179 effective_action[i] = stored_player[i].effective_action;
11181 GameActions_EM(effective_action, warp_mode);
11185 AdvanceFrameAndPlayerCounters(-1); /* advance counters for all players */
11188 void GameActions_SP_Main()
11190 byte effective_action[MAX_PLAYERS];
11191 boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11194 for (i = 0; i < MAX_PLAYERS; i++)
11195 effective_action[i] = stored_player[i].effective_action;
11197 GameActions_SP(effective_action, warp_mode);
11201 AdvanceFrameAndPlayerCounters(-1); /* advance counters for all players */
11204 void GameActions_RND()
11206 int magic_wall_x = 0, magic_wall_y = 0;
11207 int i, x, y, element, graphic;
11209 InitPlayfieldScanModeVars();
11211 if (game.engine_version >= VERSION_IDENT(3,2,0,7))
11213 SCAN_PLAYFIELD(x, y)
11215 ChangeCount[x][y] = 0;
11216 ChangeEvent[x][y] = -1;
11220 if (game.set_centered_player)
11222 boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
11224 /* switching to "all players" only possible if all players fit to screen */
11225 if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
11227 game.centered_player_nr_next = game.centered_player_nr;
11228 game.set_centered_player = FALSE;
11231 /* do not switch focus to non-existing (or non-active) player */
11232 if (game.centered_player_nr_next >= 0 &&
11233 !stored_player[game.centered_player_nr_next].active)
11235 game.centered_player_nr_next = game.centered_player_nr;
11236 game.set_centered_player = FALSE;
11240 if (game.set_centered_player &&
11241 ScreenMovPos == 0) /* screen currently aligned at tile position */
11245 if (game.centered_player_nr_next == -1)
11247 setScreenCenteredToAllPlayers(&sx, &sy);
11251 sx = stored_player[game.centered_player_nr_next].jx;
11252 sy = stored_player[game.centered_player_nr_next].jy;
11255 game.centered_player_nr = game.centered_player_nr_next;
11256 game.set_centered_player = FALSE;
11258 DrawRelocateScreen(0, 0, sx, sy, MV_NONE, TRUE, setup.quick_switch);
11259 DrawGameDoorValues();
11262 for (i = 0; i < MAX_PLAYERS; i++)
11264 int actual_player_action = stored_player[i].effective_action;
11267 /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
11268 - rnd_equinox_tetrachloride 048
11269 - rnd_equinox_tetrachloride_ii 096
11270 - rnd_emanuel_schmieg 002
11271 - doctor_sloan_ww 001, 020
11273 if (stored_player[i].MovPos == 0)
11274 CheckGravityMovement(&stored_player[i]);
11277 /* overwrite programmed action with tape action */
11278 if (stored_player[i].programmed_action)
11279 actual_player_action = stored_player[i].programmed_action;
11281 PlayerActions(&stored_player[i], actual_player_action);
11283 ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
11286 ScrollScreen(NULL, SCROLL_GO_ON);
11288 /* for backwards compatibility, the following code emulates a fixed bug that
11289 occured when pushing elements (causing elements that just made their last
11290 pushing step to already (if possible) make their first falling step in the
11291 same game frame, which is bad); this code is also needed to use the famous
11292 "spring push bug" which is used in older levels and might be wanted to be
11293 used also in newer levels, but in this case the buggy pushing code is only
11294 affecting the "spring" element and no other elements */
11296 if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
11298 for (i = 0; i < MAX_PLAYERS; i++)
11300 struct PlayerInfo *player = &stored_player[i];
11301 int x = player->jx;
11302 int y = player->jy;
11304 if (player->active && player->is_pushing && player->is_moving &&
11306 (game.engine_version < VERSION_IDENT(2,2,0,7) ||
11307 Feld[x][y] == EL_SPRING))
11309 ContinueMoving(x, y);
11311 /* continue moving after pushing (this is actually a bug) */
11312 if (!IS_MOVING(x, y))
11313 Stop[x][y] = FALSE;
11318 SCAN_PLAYFIELD(x, y)
11320 ChangeCount[x][y] = 0;
11321 ChangeEvent[x][y] = -1;
11323 /* this must be handled before main playfield loop */
11324 if (Feld[x][y] == EL_PLAYER_IS_LEAVING)
11327 if (MovDelay[x][y] <= 0)
11331 if (Feld[x][y] == EL_ELEMENT_SNAPPING)
11334 if (MovDelay[x][y] <= 0)
11337 TEST_DrawLevelField(x, y);
11339 TestIfElementTouchesCustomElement(x, y); /* for empty space */
11344 if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
11346 printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
11347 printf("GameActions(): This should never happen!\n");
11349 ChangePage[x][y] = -1;
11353 Stop[x][y] = FALSE;
11354 if (WasJustMoving[x][y] > 0)
11355 WasJustMoving[x][y]--;
11356 if (WasJustFalling[x][y] > 0)
11357 WasJustFalling[x][y]--;
11358 if (CheckCollision[x][y] > 0)
11359 CheckCollision[x][y]--;
11360 if (CheckImpact[x][y] > 0)
11361 CheckImpact[x][y]--;
11365 /* reset finished pushing action (not done in ContinueMoving() to allow
11366 continuous pushing animation for elements with zero push delay) */
11367 if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
11369 ResetGfxAnimation(x, y);
11370 TEST_DrawLevelField(x, y);
11374 if (IS_BLOCKED(x, y))
11378 Blocked2Moving(x, y, &oldx, &oldy);
11379 if (!IS_MOVING(oldx, oldy))
11381 printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
11382 printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
11383 printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
11384 printf("GameActions(): This should never happen!\n");
11390 SCAN_PLAYFIELD(x, y)
11392 element = Feld[x][y];
11393 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11395 ResetGfxFrame(x, y, TRUE);
11397 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
11398 IS_NEXT_FRAME(GfxFrame[x][y], graphic))
11399 ResetRandomAnimationValue(x, y);
11401 SetRandomAnimationValue(x, y);
11403 PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
11405 if (IS_INACTIVE(element))
11407 if (IS_ANIMATED(graphic))
11408 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11413 /* this may take place after moving, so 'element' may have changed */
11414 if (IS_CHANGING(x, y) &&
11415 (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
11417 int page = element_info[element].event_page_nr[CE_DELAY];
11419 HandleElementChange(x, y, page);
11421 element = Feld[x][y];
11422 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11425 if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
11429 element = Feld[x][y];
11430 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11432 if (IS_ANIMATED(graphic) &&
11433 !IS_MOVING(x, y) &&
11435 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11437 if (IS_GEM(element) || element == EL_SP_INFOTRON)
11438 TEST_DrawTwinkleOnField(x, y);
11440 else if ((element == EL_ACID ||
11441 element == EL_EXIT_OPEN ||
11442 element == EL_EM_EXIT_OPEN ||
11443 element == EL_SP_EXIT_OPEN ||
11444 element == EL_STEEL_EXIT_OPEN ||
11445 element == EL_EM_STEEL_EXIT_OPEN ||
11446 element == EL_SP_TERMINAL ||
11447 element == EL_SP_TERMINAL_ACTIVE ||
11448 element == EL_EXTRA_TIME ||
11449 element == EL_SHIELD_NORMAL ||
11450 element == EL_SHIELD_DEADLY) &&
11451 IS_ANIMATED(graphic))
11452 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11453 else if (IS_MOVING(x, y))
11454 ContinueMoving(x, y);
11455 else if (IS_ACTIVE_BOMB(element))
11456 CheckDynamite(x, y);
11457 else if (element == EL_AMOEBA_GROWING)
11458 AmoebeWaechst(x, y);
11459 else if (element == EL_AMOEBA_SHRINKING)
11460 AmoebaDisappearing(x, y);
11462 #if !USE_NEW_AMOEBA_CODE
11463 else if (IS_AMOEBALIVE(element))
11464 AmoebeAbleger(x, y);
11467 else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
11469 else if (element == EL_EXIT_CLOSED)
11471 else if (element == EL_EM_EXIT_CLOSED)
11473 else if (element == EL_STEEL_EXIT_CLOSED)
11474 CheckExitSteel(x, y);
11475 else if (element == EL_EM_STEEL_EXIT_CLOSED)
11476 CheckExitSteelEM(x, y);
11477 else if (element == EL_SP_EXIT_CLOSED)
11479 else if (element == EL_EXPANDABLE_WALL_GROWING ||
11480 element == EL_EXPANDABLE_STEELWALL_GROWING)
11481 MauerWaechst(x, y);
11482 else if (element == EL_EXPANDABLE_WALL ||
11483 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
11484 element == EL_EXPANDABLE_WALL_VERTICAL ||
11485 element == EL_EXPANDABLE_WALL_ANY ||
11486 element == EL_BD_EXPANDABLE_WALL)
11487 MauerAbleger(x, y);
11488 else if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
11489 element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
11490 element == EL_EXPANDABLE_STEELWALL_ANY)
11491 MauerAblegerStahl(x, y);
11492 else if (element == EL_FLAMES)
11493 CheckForDragon(x, y);
11494 else if (element == EL_EXPLOSION)
11495 ; /* drawing of correct explosion animation is handled separately */
11496 else if (element == EL_ELEMENT_SNAPPING ||
11497 element == EL_DIAGONAL_SHRINKING ||
11498 element == EL_DIAGONAL_GROWING)
11500 graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
11502 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11504 else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
11505 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11507 if (IS_BELT_ACTIVE(element))
11508 PlayLevelSoundAction(x, y, ACTION_ACTIVE);
11510 if (game.magic_wall_active)
11512 int jx = local_player->jx, jy = local_player->jy;
11514 /* play the element sound at the position nearest to the player */
11515 if ((element == EL_MAGIC_WALL_FULL ||
11516 element == EL_MAGIC_WALL_ACTIVE ||
11517 element == EL_MAGIC_WALL_EMPTYING ||
11518 element == EL_BD_MAGIC_WALL_FULL ||
11519 element == EL_BD_MAGIC_WALL_ACTIVE ||
11520 element == EL_BD_MAGIC_WALL_EMPTYING ||
11521 element == EL_DC_MAGIC_WALL_FULL ||
11522 element == EL_DC_MAGIC_WALL_ACTIVE ||
11523 element == EL_DC_MAGIC_WALL_EMPTYING) &&
11524 ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
11532 #if USE_NEW_AMOEBA_CODE
11533 /* new experimental amoeba growth stuff */
11534 if (!(FrameCounter % 8))
11536 static unsigned int random = 1684108901;
11538 for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
11540 x = RND(lev_fieldx);
11541 y = RND(lev_fieldy);
11542 element = Feld[x][y];
11544 if (!IS_PLAYER(x,y) &&
11545 (element == EL_EMPTY ||
11546 CAN_GROW_INTO(element) ||
11547 element == EL_QUICKSAND_EMPTY ||
11548 element == EL_QUICKSAND_FAST_EMPTY ||
11549 element == EL_ACID_SPLASH_LEFT ||
11550 element == EL_ACID_SPLASH_RIGHT))
11552 if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
11553 (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
11554 (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
11555 (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
11556 Feld[x][y] = EL_AMOEBA_DROP;
11559 random = random * 129 + 1;
11564 game.explosions_delayed = FALSE;
11566 SCAN_PLAYFIELD(x, y)
11568 element = Feld[x][y];
11570 if (ExplodeField[x][y])
11571 Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
11572 else if (element == EL_EXPLOSION)
11573 Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
11575 ExplodeField[x][y] = EX_TYPE_NONE;
11578 game.explosions_delayed = TRUE;
11580 if (game.magic_wall_active)
11582 if (!(game.magic_wall_time_left % 4))
11584 int element = Feld[magic_wall_x][magic_wall_y];
11586 if (element == EL_BD_MAGIC_WALL_FULL ||
11587 element == EL_BD_MAGIC_WALL_ACTIVE ||
11588 element == EL_BD_MAGIC_WALL_EMPTYING)
11589 PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
11590 else if (element == EL_DC_MAGIC_WALL_FULL ||
11591 element == EL_DC_MAGIC_WALL_ACTIVE ||
11592 element == EL_DC_MAGIC_WALL_EMPTYING)
11593 PlayLevelSound(magic_wall_x, magic_wall_y, SND_DC_MAGIC_WALL_ACTIVE);
11595 PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
11598 if (game.magic_wall_time_left > 0)
11600 game.magic_wall_time_left--;
11602 if (!game.magic_wall_time_left)
11604 SCAN_PLAYFIELD(x, y)
11606 element = Feld[x][y];
11608 if (element == EL_MAGIC_WALL_ACTIVE ||
11609 element == EL_MAGIC_WALL_FULL)
11611 Feld[x][y] = EL_MAGIC_WALL_DEAD;
11612 TEST_DrawLevelField(x, y);
11614 else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
11615 element == EL_BD_MAGIC_WALL_FULL)
11617 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
11618 TEST_DrawLevelField(x, y);
11620 else if (element == EL_DC_MAGIC_WALL_ACTIVE ||
11621 element == EL_DC_MAGIC_WALL_FULL)
11623 Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
11624 TEST_DrawLevelField(x, y);
11628 game.magic_wall_active = FALSE;
11633 if (game.light_time_left > 0)
11635 game.light_time_left--;
11637 if (game.light_time_left == 0)
11638 RedrawAllLightSwitchesAndInvisibleElements();
11641 if (game.timegate_time_left > 0)
11643 game.timegate_time_left--;
11645 if (game.timegate_time_left == 0)
11646 CloseAllOpenTimegates();
11649 if (game.lenses_time_left > 0)
11651 game.lenses_time_left--;
11653 if (game.lenses_time_left == 0)
11654 RedrawAllInvisibleElementsForLenses();
11657 if (game.magnify_time_left > 0)
11659 game.magnify_time_left--;
11661 if (game.magnify_time_left == 0)
11662 RedrawAllInvisibleElementsForMagnifier();
11665 for (i = 0; i < MAX_PLAYERS; i++)
11667 struct PlayerInfo *player = &stored_player[i];
11669 if (SHIELD_ON(player))
11671 if (player->shield_deadly_time_left)
11672 PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
11673 else if (player->shield_normal_time_left)
11674 PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
11678 #if USE_DELAYED_GFX_REDRAW
11679 SCAN_PLAYFIELD(x, y)
11681 if (GfxRedraw[x][y] != GFX_REDRAW_NONE)
11683 /* !!! PROBLEM: THIS REDRAWS THE PLAYFIELD _AFTER_ THE SCAN, BUT TILES
11684 !!! MAY HAVE CHANGED AFTER BEING DRAWN DURING PLAYFIELD SCAN !!! */
11686 if (GfxRedraw[x][y] & GFX_REDRAW_TILE)
11687 DrawLevelField(x, y);
11689 if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED)
11690 DrawLevelFieldCrumbled(x, y);
11692 if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS)
11693 DrawLevelFieldCrumbledNeighbours(x, y);
11695 if (GfxRedraw[x][y] & GFX_REDRAW_TILE_TWINKLED)
11696 DrawTwinkleOnField(x, y);
11699 GfxRedraw[x][y] = GFX_REDRAW_NONE;
11706 PlayAllPlayersSound();
11708 if (options.debug) /* calculate frames per second */
11710 static unsigned int fps_counter = 0;
11711 static int fps_frames = 0;
11712 unsigned int fps_delay_ms = Counter() - fps_counter;
11716 if (fps_delay_ms >= 500) /* calculate fps every 0.5 seconds */
11718 global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
11721 fps_counter = Counter();
11724 redraw_mask |= REDRAW_FPS;
11727 AdvanceFrameAndPlayerCounters(-1); /* advance counters for all players */
11729 if (local_player->show_envelope != 0 && local_player->MovPos == 0)
11731 ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
11733 local_player->show_envelope = 0;
11736 /* use random number generator in every frame to make it less predictable */
11737 if (game.engine_version >= VERSION_IDENT(3,1,1,0))
11741 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
11743 int min_x = x, min_y = y, max_x = x, max_y = y;
11746 for (i = 0; i < MAX_PLAYERS; i++)
11748 int jx = stored_player[i].jx, jy = stored_player[i].jy;
11750 if (!stored_player[i].active || &stored_player[i] == player)
11753 min_x = MIN(min_x, jx);
11754 min_y = MIN(min_y, jy);
11755 max_x = MAX(max_x, jx);
11756 max_y = MAX(max_y, jy);
11759 return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
11762 static boolean AllPlayersInVisibleScreen()
11766 for (i = 0; i < MAX_PLAYERS; i++)
11768 int jx = stored_player[i].jx, jy = stored_player[i].jy;
11770 if (!stored_player[i].active)
11773 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
11780 void ScrollLevel(int dx, int dy)
11782 int softscroll_offset = (setup.soft_scrolling ? 2 * TILEX_VAR : 0);
11785 BlitBitmap(drawto_field, drawto_field,
11786 FX + TILEX_VAR * (dx == -1) - softscroll_offset,
11787 FY + TILEY_VAR * (dy == -1) - softscroll_offset,
11788 SXSIZE - TILEX_VAR * (dx != 0) + 2 * softscroll_offset,
11789 SYSIZE - TILEY_VAR * (dy != 0) + 2 * softscroll_offset,
11790 FX + TILEX_VAR * (dx == 1) - softscroll_offset,
11791 FY + TILEY_VAR * (dy == 1) - softscroll_offset);
11795 x = (dx == 1 ? BX1 : BX2);
11796 for (y = BY1; y <= BY2; y++)
11797 DrawScreenField(x, y);
11802 y = (dy == 1 ? BY1 : BY2);
11803 for (x = BX1; x <= BX2; x++)
11804 DrawScreenField(x, y);
11807 redraw_mask |= REDRAW_FIELD;
11810 static boolean canFallDown(struct PlayerInfo *player)
11812 int jx = player->jx, jy = player->jy;
11814 return (IN_LEV_FIELD(jx, jy + 1) &&
11815 (IS_FREE(jx, jy + 1) ||
11816 (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
11817 IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
11818 !IS_WALKABLE_INSIDE(Feld[jx][jy]));
11821 static boolean canPassField(int x, int y, int move_dir)
11823 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
11824 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
11825 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
11826 int nextx = x + dx;
11827 int nexty = y + dy;
11828 int element = Feld[x][y];
11830 return (IS_PASSABLE_FROM(element, opposite_dir) &&
11831 !CAN_MOVE(element) &&
11832 IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
11833 IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
11834 (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
11837 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
11839 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
11840 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
11841 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
11845 return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
11846 IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
11847 (IS_DIGGABLE(Feld[newx][newy]) ||
11848 IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
11849 canPassField(newx, newy, move_dir)));
11852 static void CheckGravityMovement(struct PlayerInfo *player)
11854 if (player->gravity && !player->programmed_action)
11856 int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
11857 int move_dir_vertical = player->effective_action & MV_VERTICAL;
11858 boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
11859 int jx = player->jx, jy = player->jy;
11860 boolean player_is_moving_to_valid_field =
11861 (!player_is_snapping &&
11862 (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
11863 canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
11864 boolean player_can_fall_down = canFallDown(player);
11866 if (player_can_fall_down &&
11867 !player_is_moving_to_valid_field)
11868 player->programmed_action = MV_DOWN;
11872 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
11874 return CheckGravityMovement(player);
11876 if (player->gravity && !player->programmed_action)
11878 int jx = player->jx, jy = player->jy;
11879 boolean field_under_player_is_free =
11880 (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
11881 boolean player_is_standing_on_valid_field =
11882 (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
11883 (IS_WALKABLE(Feld[jx][jy]) &&
11884 !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
11886 if (field_under_player_is_free && !player_is_standing_on_valid_field)
11887 player->programmed_action = MV_DOWN;
11892 MovePlayerOneStep()
11893 -----------------------------------------------------------------------------
11894 dx, dy: direction (non-diagonal) to try to move the player to
11895 real_dx, real_dy: direction as read from input device (can be diagonal)
11898 boolean MovePlayerOneStep(struct PlayerInfo *player,
11899 int dx, int dy, int real_dx, int real_dy)
11901 int jx = player->jx, jy = player->jy;
11902 int new_jx = jx + dx, new_jy = jy + dy;
11904 boolean player_can_move = !player->cannot_move;
11906 if (!player->active || (!dx && !dy))
11907 return MP_NO_ACTION;
11909 player->MovDir = (dx < 0 ? MV_LEFT :
11910 dx > 0 ? MV_RIGHT :
11912 dy > 0 ? MV_DOWN : MV_NONE);
11914 if (!IN_LEV_FIELD(new_jx, new_jy))
11915 return MP_NO_ACTION;
11917 if (!player_can_move)
11919 if (player->MovPos == 0)
11921 player->is_moving = FALSE;
11922 player->is_digging = FALSE;
11923 player->is_collecting = FALSE;
11924 player->is_snapping = FALSE;
11925 player->is_pushing = FALSE;
11929 if (!options.network && game.centered_player_nr == -1 &&
11930 !AllPlayersInSight(player, new_jx, new_jy))
11931 return MP_NO_ACTION;
11933 can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
11934 if (can_move != MP_MOVING)
11937 /* check if DigField() has caused relocation of the player */
11938 if (player->jx != jx || player->jy != jy)
11939 return MP_NO_ACTION; /* <-- !!! CHECK THIS [-> MP_ACTION ?] !!! */
11941 StorePlayer[jx][jy] = 0;
11942 player->last_jx = jx;
11943 player->last_jy = jy;
11944 player->jx = new_jx;
11945 player->jy = new_jy;
11946 StorePlayer[new_jx][new_jy] = player->element_nr;
11948 if (player->move_delay_value_next != -1)
11950 player->move_delay_value = player->move_delay_value_next;
11951 player->move_delay_value_next = -1;
11955 (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
11957 player->step_counter++;
11959 PlayerVisit[jx][jy] = FrameCounter;
11961 player->is_moving = TRUE;
11964 /* should better be called in MovePlayer(), but this breaks some tapes */
11965 ScrollPlayer(player, SCROLL_INIT);
11971 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
11973 int jx = player->jx, jy = player->jy;
11974 int old_jx = jx, old_jy = jy;
11975 int moved = MP_NO_ACTION;
11977 if (!player->active)
11982 if (player->MovPos == 0)
11984 player->is_moving = FALSE;
11985 player->is_digging = FALSE;
11986 player->is_collecting = FALSE;
11987 player->is_snapping = FALSE;
11988 player->is_pushing = FALSE;
11994 if (player->move_delay > 0)
11997 player->move_delay = -1; /* set to "uninitialized" value */
11999 /* store if player is automatically moved to next field */
12000 player->is_auto_moving = (player->programmed_action != MV_NONE);
12002 /* remove the last programmed player action */
12003 player->programmed_action = 0;
12005 if (player->MovPos)
12007 /* should only happen if pre-1.2 tape recordings are played */
12008 /* this is only for backward compatibility */
12010 int original_move_delay_value = player->move_delay_value;
12013 printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%d]\n",
12017 /* scroll remaining steps with finest movement resolution */
12018 player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
12020 while (player->MovPos)
12022 ScrollPlayer(player, SCROLL_GO_ON);
12023 ScrollScreen(NULL, SCROLL_GO_ON);
12025 AdvanceFrameAndPlayerCounters(player->index_nr);
12031 player->move_delay_value = original_move_delay_value;
12034 player->is_active = FALSE;
12036 if (player->last_move_dir & MV_HORIZONTAL)
12038 if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
12039 moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
12043 if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
12044 moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
12047 if (!moved && !player->is_active)
12049 player->is_moving = FALSE;
12050 player->is_digging = FALSE;
12051 player->is_collecting = FALSE;
12052 player->is_snapping = FALSE;
12053 player->is_pushing = FALSE;
12059 if (moved & MP_MOVING && !ScreenMovPos &&
12060 (player->index_nr == game.centered_player_nr ||
12061 game.centered_player_nr == -1))
12063 int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
12064 int offset = game.scroll_delay_value;
12066 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12068 /* actual player has left the screen -- scroll in that direction */
12069 if (jx != old_jx) /* player has moved horizontally */
12070 scroll_x += (jx - old_jx);
12071 else /* player has moved vertically */
12072 scroll_y += (jy - old_jy);
12076 if (jx != old_jx) /* player has moved horizontally */
12078 if ((player->MovDir == MV_LEFT && scroll_x > jx - MIDPOSX + offset) ||
12079 (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
12080 scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
12082 /* don't scroll over playfield boundaries */
12083 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
12084 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
12086 /* don't scroll more than one field at a time */
12087 scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
12089 /* don't scroll against the player's moving direction */
12090 if ((player->MovDir == MV_LEFT && scroll_x > old_scroll_x) ||
12091 (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
12092 scroll_x = old_scroll_x;
12094 else /* player has moved vertically */
12096 if ((player->MovDir == MV_UP && scroll_y > jy - MIDPOSY + offset) ||
12097 (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
12098 scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
12100 /* don't scroll over playfield boundaries */
12101 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
12102 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
12104 /* don't scroll more than one field at a time */
12105 scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
12107 /* don't scroll against the player's moving direction */
12108 if ((player->MovDir == MV_UP && scroll_y > old_scroll_y) ||
12109 (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
12110 scroll_y = old_scroll_y;
12114 if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
12116 if (!options.network && game.centered_player_nr == -1 &&
12117 !AllPlayersInVisibleScreen())
12119 scroll_x = old_scroll_x;
12120 scroll_y = old_scroll_y;
12124 ScrollScreen(player, SCROLL_INIT);
12125 ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
12130 player->StepFrame = 0;
12132 if (moved & MP_MOVING)
12134 if (old_jx != jx && old_jy == jy)
12135 player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
12136 else if (old_jx == jx && old_jy != jy)
12137 player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
12139 TEST_DrawLevelField(jx, jy); /* for "crumbled sand" */
12141 player->last_move_dir = player->MovDir;
12142 player->is_moving = TRUE;
12143 player->is_snapping = FALSE;
12144 player->is_switching = FALSE;
12145 player->is_dropping = FALSE;
12146 player->is_dropping_pressed = FALSE;
12147 player->drop_pressed_delay = 0;
12150 /* should better be called here than above, but this breaks some tapes */
12151 ScrollPlayer(player, SCROLL_INIT);
12156 CheckGravityMovementWhenNotMoving(player);
12158 player->is_moving = FALSE;
12160 /* at this point, the player is allowed to move, but cannot move right now
12161 (e.g. because of something blocking the way) -- ensure that the player
12162 is also allowed to move in the next frame (in old versions before 3.1.1,
12163 the player was forced to wait again for eight frames before next try) */
12165 if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12166 player->move_delay = 0; /* allow direct movement in the next frame */
12169 if (player->move_delay == -1) /* not yet initialized by DigField() */
12170 player->move_delay = player->move_delay_value;
12172 if (game.engine_version < VERSION_IDENT(3,0,7,0))
12174 TestIfPlayerTouchesBadThing(jx, jy);
12175 TestIfPlayerTouchesCustomElement(jx, jy);
12178 if (!player->active)
12179 RemovePlayer(player);
12184 void ScrollPlayer(struct PlayerInfo *player, int mode)
12186 int jx = player->jx, jy = player->jy;
12187 int last_jx = player->last_jx, last_jy = player->last_jy;
12188 int move_stepsize = TILEX / player->move_delay_value;
12190 if (!player->active)
12193 if (player->MovPos == 0 && mode == SCROLL_GO_ON) /* player not moving */
12196 if (mode == SCROLL_INIT)
12198 player->actual_frame_counter = FrameCounter;
12199 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
12201 if ((player->block_last_field || player->block_delay_adjustment > 0) &&
12202 Feld[last_jx][last_jy] == EL_EMPTY)
12204 int last_field_block_delay = 0; /* start with no blocking at all */
12205 int block_delay_adjustment = player->block_delay_adjustment;
12207 /* if player blocks last field, add delay for exactly one move */
12208 if (player->block_last_field)
12210 last_field_block_delay += player->move_delay_value;
12212 /* when blocking enabled, prevent moving up despite gravity */
12213 if (player->gravity && player->MovDir == MV_UP)
12214 block_delay_adjustment = -1;
12217 /* add block delay adjustment (also possible when not blocking) */
12218 last_field_block_delay += block_delay_adjustment;
12220 Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
12221 MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
12224 if (player->MovPos != 0) /* player has not yet reached destination */
12227 else if (!FrameReached(&player->actual_frame_counter, 1))
12230 if (player->MovPos != 0)
12232 player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
12233 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
12235 /* before DrawPlayer() to draw correct player graphic for this case */
12236 if (player->MovPos == 0)
12237 CheckGravityMovement(player);
12240 if (player->MovPos == 0) /* player reached destination field */
12242 if (player->move_delay_reset_counter > 0)
12244 player->move_delay_reset_counter--;
12246 if (player->move_delay_reset_counter == 0)
12248 /* continue with normal speed after quickly moving through gate */
12249 HALVE_PLAYER_SPEED(player);
12251 /* be able to make the next move without delay */
12252 player->move_delay = 0;
12256 player->last_jx = jx;
12257 player->last_jy = jy;
12259 if (Feld[jx][jy] == EL_EXIT_OPEN ||
12260 Feld[jx][jy] == EL_EM_EXIT_OPEN ||
12261 Feld[jx][jy] == EL_EM_EXIT_OPENING ||
12262 Feld[jx][jy] == EL_STEEL_EXIT_OPEN ||
12263 Feld[jx][jy] == EL_EM_STEEL_EXIT_OPEN ||
12264 Feld[jx][jy] == EL_EM_STEEL_EXIT_OPENING ||
12265 Feld[jx][jy] == EL_SP_EXIT_OPEN ||
12266 Feld[jx][jy] == EL_SP_EXIT_OPENING) /* <-- special case */
12268 DrawPlayer(player); /* needed here only to cleanup last field */
12269 RemovePlayer(player);
12271 if (local_player->friends_still_needed == 0 ||
12272 IS_SP_ELEMENT(Feld[jx][jy]))
12273 PlayerWins(player);
12276 /* this breaks one level: "machine", level 000 */
12278 int move_direction = player->MovDir;
12279 int enter_side = MV_DIR_OPPOSITE(move_direction);
12280 int leave_side = move_direction;
12281 int old_jx = last_jx;
12282 int old_jy = last_jy;
12283 int old_element = Feld[old_jx][old_jy];
12284 int new_element = Feld[jx][jy];
12286 if (IS_CUSTOM_ELEMENT(old_element))
12287 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
12289 player->index_bit, leave_side);
12291 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
12292 CE_PLAYER_LEAVES_X,
12293 player->index_bit, leave_side);
12295 if (IS_CUSTOM_ELEMENT(new_element))
12296 CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
12297 player->index_bit, enter_side);
12299 CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
12300 CE_PLAYER_ENTERS_X,
12301 player->index_bit, enter_side);
12303 CheckTriggeredElementChangeBySide(jx, jy, player->initial_element,
12304 CE_MOVE_OF_X, move_direction);
12307 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
12309 TestIfPlayerTouchesBadThing(jx, jy);
12310 TestIfPlayerTouchesCustomElement(jx, jy);
12312 /* needed because pushed element has not yet reached its destination,
12313 so it would trigger a change event at its previous field location */
12314 if (!player->is_pushing)
12315 TestIfElementTouchesCustomElement(jx, jy); /* for empty space */
12317 if (!player->active)
12318 RemovePlayer(player);
12321 if (!local_player->LevelSolved && level.use_step_counter)
12331 if (TimeLeft <= 10 && setup.time_limit)
12332 PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
12334 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
12336 DisplayGameControlValues();
12338 if (!TimeLeft && setup.time_limit)
12339 for (i = 0; i < MAX_PLAYERS; i++)
12340 KillPlayer(&stored_player[i]);
12342 else if (game.no_time_limit && !AllPlayersGone) /* level w/o time limit */
12344 game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
12346 DisplayGameControlValues();
12350 if (tape.single_step && tape.recording && !tape.pausing &&
12351 !player->programmed_action)
12352 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
12354 if (!player->programmed_action)
12355 CheckSaveEngineSnapshot(player);
12359 void ScrollScreen(struct PlayerInfo *player, int mode)
12361 static unsigned int screen_frame_counter = 0;
12363 if (mode == SCROLL_INIT)
12365 /* set scrolling step size according to actual player's moving speed */
12366 ScrollStepSize = TILEX / player->move_delay_value;
12368 screen_frame_counter = FrameCounter;
12369 ScreenMovDir = player->MovDir;
12370 ScreenMovPos = player->MovPos;
12371 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
12374 else if (!FrameReached(&screen_frame_counter, 1))
12379 ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
12380 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
12381 redraw_mask |= REDRAW_FIELD;
12384 ScreenMovDir = MV_NONE;
12387 void TestIfPlayerTouchesCustomElement(int x, int y)
12389 static int xy[4][2] =
12396 static int trigger_sides[4][2] =
12398 /* center side border side */
12399 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
12400 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
12401 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
12402 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
12404 static int touch_dir[4] =
12406 MV_LEFT | MV_RIGHT,
12411 int center_element = Feld[x][y]; /* should always be non-moving! */
12414 for (i = 0; i < NUM_DIRECTIONS; i++)
12416 int xx = x + xy[i][0];
12417 int yy = y + xy[i][1];
12418 int center_side = trigger_sides[i][0];
12419 int border_side = trigger_sides[i][1];
12420 int border_element;
12422 if (!IN_LEV_FIELD(xx, yy))
12425 if (IS_PLAYER(x, y)) /* player found at center element */
12427 struct PlayerInfo *player = PLAYERINFO(x, y);
12429 if (game.engine_version < VERSION_IDENT(3,0,7,0))
12430 border_element = Feld[xx][yy]; /* may be moving! */
12431 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
12432 border_element = Feld[xx][yy];
12433 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
12434 border_element = MovingOrBlocked2Element(xx, yy);
12436 continue; /* center and border element do not touch */
12438 CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
12439 player->index_bit, border_side);
12440 CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
12441 CE_PLAYER_TOUCHES_X,
12442 player->index_bit, border_side);
12445 /* use player element that is initially defined in the level playfield,
12446 not the player element that corresponds to the runtime player number
12447 (example: a level that contains EL_PLAYER_3 as the only player would
12448 incorrectly give EL_PLAYER_1 for "player->element_nr") */
12449 int player_element = PLAYERINFO(x, y)->initial_element;
12451 CheckElementChangeBySide(xx, yy, border_element, player_element,
12452 CE_TOUCHING_X, border_side);
12455 else if (IS_PLAYER(xx, yy)) /* player found at border element */
12457 struct PlayerInfo *player = PLAYERINFO(xx, yy);
12459 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
12461 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
12462 continue; /* center and border element do not touch */
12465 CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
12466 player->index_bit, center_side);
12467 CheckTriggeredElementChangeByPlayer(x, y, center_element,
12468 CE_PLAYER_TOUCHES_X,
12469 player->index_bit, center_side);
12472 /* use player element that is initially defined in the level playfield,
12473 not the player element that corresponds to the runtime player number
12474 (example: a level that contains EL_PLAYER_3 as the only player would
12475 incorrectly give EL_PLAYER_1 for "player->element_nr") */
12476 int player_element = PLAYERINFO(xx, yy)->initial_element;
12478 CheckElementChangeBySide(x, y, center_element, player_element,
12479 CE_TOUCHING_X, center_side);
12487 void TestIfElementTouchesCustomElement(int x, int y)
12489 static int xy[4][2] =
12496 static int trigger_sides[4][2] =
12498 /* center side border side */
12499 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
12500 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
12501 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
12502 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
12504 static int touch_dir[4] =
12506 MV_LEFT | MV_RIGHT,
12511 boolean change_center_element = FALSE;
12512 int center_element = Feld[x][y]; /* should always be non-moving! */
12513 int border_element_old[NUM_DIRECTIONS];
12516 for (i = 0; i < NUM_DIRECTIONS; i++)
12518 int xx = x + xy[i][0];
12519 int yy = y + xy[i][1];
12520 int border_element;
12522 border_element_old[i] = -1;
12524 if (!IN_LEV_FIELD(xx, yy))
12527 if (game.engine_version < VERSION_IDENT(3,0,7,0))
12528 border_element = Feld[xx][yy]; /* may be moving! */
12529 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
12530 border_element = Feld[xx][yy];
12531 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
12532 border_element = MovingOrBlocked2Element(xx, yy);
12534 continue; /* center and border element do not touch */
12536 border_element_old[i] = border_element;
12539 for (i = 0; i < NUM_DIRECTIONS; i++)
12541 int xx = x + xy[i][0];
12542 int yy = y + xy[i][1];
12543 int center_side = trigger_sides[i][0];
12544 int border_element = border_element_old[i];
12546 if (border_element == -1)
12549 /* check for change of border element */
12550 CheckElementChangeBySide(xx, yy, border_element, center_element,
12551 CE_TOUCHING_X, center_side);
12553 /* (center element cannot be player, so we dont have to check this here) */
12556 for (i = 0; i < NUM_DIRECTIONS; i++)
12558 int xx = x + xy[i][0];
12559 int yy = y + xy[i][1];
12560 int border_side = trigger_sides[i][1];
12561 int border_element = border_element_old[i];
12563 if (border_element == -1)
12566 /* check for change of center element (but change it only once) */
12567 if (!change_center_element)
12568 change_center_element =
12569 CheckElementChangeBySide(x, y, center_element, border_element,
12570 CE_TOUCHING_X, border_side);
12572 if (IS_PLAYER(xx, yy))
12574 /* use player element that is initially defined in the level playfield,
12575 not the player element that corresponds to the runtime player number
12576 (example: a level that contains EL_PLAYER_3 as the only player would
12577 incorrectly give EL_PLAYER_1 for "player->element_nr") */
12578 int player_element = PLAYERINFO(xx, yy)->initial_element;
12580 CheckElementChangeBySide(x, y, center_element, player_element,
12581 CE_TOUCHING_X, border_side);
12586 void TestIfElementHitsCustomElement(int x, int y, int direction)
12588 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
12589 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
12590 int hitx = x + dx, hity = y + dy;
12591 int hitting_element = Feld[x][y];
12592 int touched_element;
12594 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
12597 touched_element = (IN_LEV_FIELD(hitx, hity) ?
12598 MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
12600 if (IN_LEV_FIELD(hitx, hity))
12602 int opposite_direction = MV_DIR_OPPOSITE(direction);
12603 int hitting_side = direction;
12604 int touched_side = opposite_direction;
12605 boolean object_hit = (!IS_MOVING(hitx, hity) ||
12606 MovDir[hitx][hity] != direction ||
12607 ABS(MovPos[hitx][hity]) <= TILEY / 2);
12613 CheckElementChangeBySide(x, y, hitting_element, touched_element,
12614 CE_HITTING_X, touched_side);
12616 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
12617 CE_HIT_BY_X, hitting_side);
12619 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
12620 CE_HIT_BY_SOMETHING, opposite_direction);
12622 if (IS_PLAYER(hitx, hity))
12624 /* use player element that is initially defined in the level playfield,
12625 not the player element that corresponds to the runtime player number
12626 (example: a level that contains EL_PLAYER_3 as the only player would
12627 incorrectly give EL_PLAYER_1 for "player->element_nr") */
12628 int player_element = PLAYERINFO(hitx, hity)->initial_element;
12630 CheckElementChangeBySide(x, y, hitting_element, player_element,
12631 CE_HITTING_X, touched_side);
12636 /* "hitting something" is also true when hitting the playfield border */
12637 CheckElementChangeBySide(x, y, hitting_element, touched_element,
12638 CE_HITTING_SOMETHING, direction);
12641 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
12643 int i, kill_x = -1, kill_y = -1;
12645 int bad_element = -1;
12646 static int test_xy[4][2] =
12653 static int test_dir[4] =
12661 for (i = 0; i < NUM_DIRECTIONS; i++)
12663 int test_x, test_y, test_move_dir, test_element;
12665 test_x = good_x + test_xy[i][0];
12666 test_y = good_y + test_xy[i][1];
12668 if (!IN_LEV_FIELD(test_x, test_y))
12672 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
12674 test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
12676 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
12677 2nd case: DONT_TOUCH style bad thing does not move away from good thing
12679 if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
12680 (DONT_TOUCH(test_element) && test_move_dir != test_dir[i]))
12684 bad_element = test_element;
12690 if (kill_x != -1 || kill_y != -1)
12692 if (IS_PLAYER(good_x, good_y))
12694 struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
12696 if (player->shield_deadly_time_left > 0 &&
12697 !IS_INDESTRUCTIBLE(bad_element))
12698 Bang(kill_x, kill_y);
12699 else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
12700 KillPlayer(player);
12703 Bang(good_x, good_y);
12707 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
12709 int i, kill_x = -1, kill_y = -1;
12710 int bad_element = Feld[bad_x][bad_y];
12711 static int test_xy[4][2] =
12718 static int touch_dir[4] =
12720 MV_LEFT | MV_RIGHT,
12725 static int test_dir[4] =
12733 if (bad_element == EL_EXPLOSION) /* skip just exploding bad things */
12736 for (i = 0; i < NUM_DIRECTIONS; i++)
12738 int test_x, test_y, test_move_dir, test_element;
12740 test_x = bad_x + test_xy[i][0];
12741 test_y = bad_y + test_xy[i][1];
12743 if (!IN_LEV_FIELD(test_x, test_y))
12747 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
12749 test_element = Feld[test_x][test_y];
12751 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
12752 2nd case: DONT_TOUCH style bad thing does not move away from good thing
12754 if ((DONT_RUN_INTO(bad_element) && bad_move_dir == test_dir[i]) ||
12755 (DONT_TOUCH(bad_element) && test_move_dir != test_dir[i]))
12757 /* good thing is player or penguin that does not move away */
12758 if (IS_PLAYER(test_x, test_y))
12760 struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
12762 if (bad_element == EL_ROBOT && player->is_moving)
12763 continue; /* robot does not kill player if he is moving */
12765 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
12767 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
12768 continue; /* center and border element do not touch */
12776 else if (test_element == EL_PENGUIN)
12786 if (kill_x != -1 || kill_y != -1)
12788 if (IS_PLAYER(kill_x, kill_y))
12790 struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
12792 if (player->shield_deadly_time_left > 0 &&
12793 !IS_INDESTRUCTIBLE(bad_element))
12794 Bang(bad_x, bad_y);
12795 else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
12796 KillPlayer(player);
12799 Bang(kill_x, kill_y);
12803 void TestIfGoodThingGetsHitByBadThing(int bad_x, int bad_y, int bad_move_dir)
12805 int bad_element = Feld[bad_x][bad_y];
12806 int dx = (bad_move_dir == MV_LEFT ? -1 : bad_move_dir == MV_RIGHT ? +1 : 0);
12807 int dy = (bad_move_dir == MV_UP ? -1 : bad_move_dir == MV_DOWN ? +1 : 0);
12808 int test_x = bad_x + dx, test_y = bad_y + dy;
12809 int test_move_dir, test_element;
12810 int kill_x = -1, kill_y = -1;
12812 if (!IN_LEV_FIELD(test_x, test_y))
12816 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
12818 test_element = Feld[test_x][test_y];
12820 if (test_move_dir != bad_move_dir)
12822 /* good thing can be player or penguin that does not move away */
12823 if (IS_PLAYER(test_x, test_y))
12825 struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
12827 /* (note: in comparison to DONT_RUN_TO and DONT_TOUCH, also handle the
12828 player as being hit when he is moving towards the bad thing, because
12829 the "get hit by" condition would be lost after the player stops) */
12830 if (player->MovPos != 0 && player->MovDir == bad_move_dir)
12831 return; /* player moves away from bad thing */
12836 else if (test_element == EL_PENGUIN)
12843 if (kill_x != -1 || kill_y != -1)
12845 if (IS_PLAYER(kill_x, kill_y))
12847 struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
12849 if (player->shield_deadly_time_left > 0 &&
12850 !IS_INDESTRUCTIBLE(bad_element))
12851 Bang(bad_x, bad_y);
12852 else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
12853 KillPlayer(player);
12856 Bang(kill_x, kill_y);
12860 void TestIfPlayerTouchesBadThing(int x, int y)
12862 TestIfGoodThingHitsBadThing(x, y, MV_NONE);
12865 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
12867 TestIfGoodThingHitsBadThing(x, y, move_dir);
12870 void TestIfBadThingTouchesPlayer(int x, int y)
12872 TestIfBadThingHitsGoodThing(x, y, MV_NONE);
12875 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
12877 TestIfBadThingHitsGoodThing(x, y, move_dir);
12880 void TestIfFriendTouchesBadThing(int x, int y)
12882 TestIfGoodThingHitsBadThing(x, y, MV_NONE);
12885 void TestIfBadThingTouchesFriend(int x, int y)
12887 TestIfBadThingHitsGoodThing(x, y, MV_NONE);
12890 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
12892 int i, kill_x = bad_x, kill_y = bad_y;
12893 static int xy[4][2] =
12901 for (i = 0; i < NUM_DIRECTIONS; i++)
12905 x = bad_x + xy[i][0];
12906 y = bad_y + xy[i][1];
12907 if (!IN_LEV_FIELD(x, y))
12910 element = Feld[x][y];
12911 if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
12912 element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
12920 if (kill_x != bad_x || kill_y != bad_y)
12921 Bang(bad_x, bad_y);
12924 void KillPlayer(struct PlayerInfo *player)
12926 int jx = player->jx, jy = player->jy;
12928 if (!player->active)
12932 printf("::: 0: killed == %d, active == %d, reanimated == %d\n",
12933 player->killed, player->active, player->reanimated);
12936 /* the following code was introduced to prevent an infinite loop when calling
12938 -> CheckTriggeredElementChangeExt()
12939 -> ExecuteCustomElementAction()
12941 -> (infinitely repeating the above sequence of function calls)
12942 which occurs when killing the player while having a CE with the setting
12943 "kill player X when explosion of <player X>"; the solution using a new
12944 field "player->killed" was chosen for backwards compatibility, although
12945 clever use of the fields "player->active" etc. would probably also work */
12947 if (player->killed)
12951 player->killed = TRUE;
12953 /* remove accessible field at the player's position */
12954 Feld[jx][jy] = EL_EMPTY;
12956 /* deactivate shield (else Bang()/Explode() would not work right) */
12957 player->shield_normal_time_left = 0;
12958 player->shield_deadly_time_left = 0;
12961 printf("::: 1: killed == %d, active == %d, reanimated == %d\n",
12962 player->killed, player->active, player->reanimated);
12968 printf("::: 2: killed == %d, active == %d, reanimated == %d\n",
12969 player->killed, player->active, player->reanimated);
12972 if (player->reanimated) /* killed player may have been reanimated */
12973 player->killed = player->reanimated = FALSE;
12975 BuryPlayer(player);
12978 static void KillPlayerUnlessEnemyProtected(int x, int y)
12980 if (!PLAYER_ENEMY_PROTECTED(x, y))
12981 KillPlayer(PLAYERINFO(x, y));
12984 static void KillPlayerUnlessExplosionProtected(int x, int y)
12986 if (!PLAYER_EXPLOSION_PROTECTED(x, y))
12987 KillPlayer(PLAYERINFO(x, y));
12990 void BuryPlayer(struct PlayerInfo *player)
12992 int jx = player->jx, jy = player->jy;
12994 if (!player->active)
12997 PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
12998 PlayLevelSound(jx, jy, SND_GAME_LOSING);
13000 player->GameOver = TRUE;
13001 RemovePlayer(player);
13004 void RemovePlayer(struct PlayerInfo *player)
13006 int jx = player->jx, jy = player->jy;
13007 int i, found = FALSE;
13009 player->present = FALSE;
13010 player->active = FALSE;
13012 if (!ExplodeField[jx][jy])
13013 StorePlayer[jx][jy] = 0;
13015 if (player->is_moving)
13016 TEST_DrawLevelField(player->last_jx, player->last_jy);
13018 for (i = 0; i < MAX_PLAYERS; i++)
13019 if (stored_player[i].active)
13023 AllPlayersGone = TRUE;
13029 static void setFieldForSnapping(int x, int y, int element, int direction)
13031 struct ElementInfo *ei = &element_info[element];
13032 int direction_bit = MV_DIR_TO_BIT(direction);
13033 int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
13034 int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
13035 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
13037 Feld[x][y] = EL_ELEMENT_SNAPPING;
13038 MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
13040 ResetGfxAnimation(x, y);
13042 GfxElement[x][y] = element;
13043 GfxAction[x][y] = action;
13044 GfxDir[x][y] = direction;
13045 GfxFrame[x][y] = -1;
13049 =============================================================================
13050 checkDiagonalPushing()
13051 -----------------------------------------------------------------------------
13052 check if diagonal input device direction results in pushing of object
13053 (by checking if the alternative direction is walkable, diggable, ...)
13054 =============================================================================
13057 static boolean checkDiagonalPushing(struct PlayerInfo *player,
13058 int x, int y, int real_dx, int real_dy)
13060 int jx, jy, dx, dy, xx, yy;
13062 if (real_dx == 0 || real_dy == 0) /* no diagonal direction => push */
13065 /* diagonal direction: check alternative direction */
13070 xx = jx + (dx == 0 ? real_dx : 0);
13071 yy = jy + (dy == 0 ? real_dy : 0);
13073 return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
13077 =============================================================================
13079 -----------------------------------------------------------------------------
13080 x, y: field next to player (non-diagonal) to try to dig to
13081 real_dx, real_dy: direction as read from input device (can be diagonal)
13082 =============================================================================
13085 static int DigField(struct PlayerInfo *player,
13086 int oldx, int oldy, int x, int y,
13087 int real_dx, int real_dy, int mode)
13089 boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
13090 boolean player_was_pushing = player->is_pushing;
13091 boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
13092 boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
13093 int jx = oldx, jy = oldy;
13094 int dx = x - jx, dy = y - jy;
13095 int nextx = x + dx, nexty = y + dy;
13096 int move_direction = (dx == -1 ? MV_LEFT :
13097 dx == +1 ? MV_RIGHT :
13099 dy == +1 ? MV_DOWN : MV_NONE);
13100 int opposite_direction = MV_DIR_OPPOSITE(move_direction);
13101 int dig_side = MV_DIR_OPPOSITE(move_direction);
13102 int old_element = Feld[jx][jy];
13103 int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
13106 if (is_player) /* function can also be called by EL_PENGUIN */
13108 if (player->MovPos == 0)
13110 player->is_digging = FALSE;
13111 player->is_collecting = FALSE;
13114 if (player->MovPos == 0) /* last pushing move finished */
13115 player->is_pushing = FALSE;
13117 if (mode == DF_NO_PUSH) /* player just stopped pushing */
13119 player->is_switching = FALSE;
13120 player->push_delay = -1;
13122 return MP_NO_ACTION;
13126 if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
13127 old_element = Back[jx][jy];
13129 /* in case of element dropped at player position, check background */
13130 else if (Back[jx][jy] != EL_EMPTY &&
13131 game.engine_version >= VERSION_IDENT(2,2,0,0))
13132 old_element = Back[jx][jy];
13134 if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
13135 return MP_NO_ACTION; /* field has no opening in this direction */
13137 if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
13138 return MP_NO_ACTION; /* field has no opening in this direction */
13140 if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
13144 Feld[jx][jy] = player->artwork_element;
13145 InitMovingField(jx, jy, MV_DOWN);
13146 Store[jx][jy] = EL_ACID;
13147 ContinueMoving(jx, jy);
13148 BuryPlayer(player);
13150 return MP_DONT_RUN_INTO;
13153 if (player_can_move && DONT_RUN_INTO(element))
13155 TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
13157 return MP_DONT_RUN_INTO;
13160 if (IS_MOVING(x, y) || IS_PLAYER(x, y))
13161 return MP_NO_ACTION;
13163 collect_count = element_info[element].collect_count_initial;
13165 if (!is_player && !IS_COLLECTIBLE(element)) /* penguin cannot collect it */
13166 return MP_NO_ACTION;
13168 if (game.engine_version < VERSION_IDENT(2,2,0,0))
13169 player_can_move = player_can_move_or_snap;
13171 if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
13172 game.engine_version >= VERSION_IDENT(2,2,0,0))
13174 CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
13175 player->index_bit, dig_side);
13176 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13177 player->index_bit, dig_side);
13179 if (element == EL_DC_LANDMINE)
13182 if (Feld[x][y] != element) /* field changed by snapping */
13185 return MP_NO_ACTION;
13188 if (player->gravity && is_player && !player->is_auto_moving &&
13189 canFallDown(player) && move_direction != MV_DOWN &&
13190 !canMoveToValidFieldWithGravity(jx, jy, move_direction))
13191 return MP_NO_ACTION; /* player cannot walk here due to gravity */
13193 if (player_can_move &&
13194 IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
13196 int sound_element = SND_ELEMENT(element);
13197 int sound_action = ACTION_WALKING;
13199 if (IS_RND_GATE(element))
13201 if (!player->key[RND_GATE_NR(element)])
13202 return MP_NO_ACTION;
13204 else if (IS_RND_GATE_GRAY(element))
13206 if (!player->key[RND_GATE_GRAY_NR(element)])
13207 return MP_NO_ACTION;
13209 else if (IS_RND_GATE_GRAY_ACTIVE(element))
13211 if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
13212 return MP_NO_ACTION;
13214 else if (element == EL_EXIT_OPEN ||
13215 element == EL_EM_EXIT_OPEN ||
13216 element == EL_EM_EXIT_OPENING ||
13217 element == EL_STEEL_EXIT_OPEN ||
13218 element == EL_EM_STEEL_EXIT_OPEN ||
13219 element == EL_EM_STEEL_EXIT_OPENING ||
13220 element == EL_SP_EXIT_OPEN ||
13221 element == EL_SP_EXIT_OPENING)
13223 sound_action = ACTION_PASSING; /* player is passing exit */
13225 else if (element == EL_EMPTY)
13227 sound_action = ACTION_MOVING; /* nothing to walk on */
13230 /* play sound from background or player, whatever is available */
13231 if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
13232 PlayLevelSoundElementAction(x, y, sound_element, sound_action);
13234 PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
13236 else if (player_can_move &&
13237 IS_PASSABLE(element) && canPassField(x, y, move_direction))
13239 if (!ACCESS_FROM(element, opposite_direction))
13240 return MP_NO_ACTION; /* field not accessible from this direction */
13242 if (CAN_MOVE(element)) /* only fixed elements can be passed! */
13243 return MP_NO_ACTION;
13245 if (IS_EM_GATE(element))
13247 if (!player->key[EM_GATE_NR(element)])
13248 return MP_NO_ACTION;
13250 else if (IS_EM_GATE_GRAY(element))
13252 if (!player->key[EM_GATE_GRAY_NR(element)])
13253 return MP_NO_ACTION;
13255 else if (IS_EM_GATE_GRAY_ACTIVE(element))
13257 if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
13258 return MP_NO_ACTION;
13260 else if (IS_EMC_GATE(element))
13262 if (!player->key[EMC_GATE_NR(element)])
13263 return MP_NO_ACTION;
13265 else if (IS_EMC_GATE_GRAY(element))
13267 if (!player->key[EMC_GATE_GRAY_NR(element)])
13268 return MP_NO_ACTION;
13270 else if (IS_EMC_GATE_GRAY_ACTIVE(element))
13272 if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
13273 return MP_NO_ACTION;
13275 else if (element == EL_DC_GATE_WHITE ||
13276 element == EL_DC_GATE_WHITE_GRAY ||
13277 element == EL_DC_GATE_WHITE_GRAY_ACTIVE)
13279 if (player->num_white_keys == 0)
13280 return MP_NO_ACTION;
13282 player->num_white_keys--;
13284 else if (IS_SP_PORT(element))
13286 if (element == EL_SP_GRAVITY_PORT_LEFT ||
13287 element == EL_SP_GRAVITY_PORT_RIGHT ||
13288 element == EL_SP_GRAVITY_PORT_UP ||
13289 element == EL_SP_GRAVITY_PORT_DOWN)
13290 player->gravity = !player->gravity;
13291 else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
13292 element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
13293 element == EL_SP_GRAVITY_ON_PORT_UP ||
13294 element == EL_SP_GRAVITY_ON_PORT_DOWN)
13295 player->gravity = TRUE;
13296 else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
13297 element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
13298 element == EL_SP_GRAVITY_OFF_PORT_UP ||
13299 element == EL_SP_GRAVITY_OFF_PORT_DOWN)
13300 player->gravity = FALSE;
13303 /* automatically move to the next field with double speed */
13304 player->programmed_action = move_direction;
13306 if (player->move_delay_reset_counter == 0)
13308 player->move_delay_reset_counter = 2; /* two double speed steps */
13310 DOUBLE_PLAYER_SPEED(player);
13313 PlayLevelSoundAction(x, y, ACTION_PASSING);
13315 else if (player_can_move_or_snap && IS_DIGGABLE(element))
13319 if (mode != DF_SNAP)
13321 GfxElement[x][y] = GFX_ELEMENT(element);
13322 player->is_digging = TRUE;
13325 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
13327 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
13328 player->index_bit, dig_side);
13330 if (mode == DF_SNAP)
13332 if (level.block_snap_field)
13333 setFieldForSnapping(x, y, element, move_direction);
13335 TestIfElementTouchesCustomElement(x, y); /* for empty space */
13337 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13338 player->index_bit, dig_side);
13341 else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
13345 if (is_player && mode != DF_SNAP)
13347 GfxElement[x][y] = element;
13348 player->is_collecting = TRUE;
13351 if (element == EL_SPEED_PILL)
13353 player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
13355 else if (element == EL_EXTRA_TIME && level.time > 0)
13357 TimeLeft += level.extra_time;
13359 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
13361 DisplayGameControlValues();
13363 else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
13365 player->shield_normal_time_left += level.shield_normal_time;
13366 if (element == EL_SHIELD_DEADLY)
13367 player->shield_deadly_time_left += level.shield_deadly_time;
13369 else if (element == EL_DYNAMITE ||
13370 element == EL_EM_DYNAMITE ||
13371 element == EL_SP_DISK_RED)
13373 if (player->inventory_size < MAX_INVENTORY_SIZE)
13374 player->inventory_element[player->inventory_size++] = element;
13376 DrawGameDoorValues();
13378 else if (element == EL_DYNABOMB_INCREASE_NUMBER)
13380 player->dynabomb_count++;
13381 player->dynabombs_left++;
13383 else if (element == EL_DYNABOMB_INCREASE_SIZE)
13385 player->dynabomb_size++;
13387 else if (element == EL_DYNABOMB_INCREASE_POWER)
13389 player->dynabomb_xl = TRUE;
13391 else if (IS_KEY(element))
13393 player->key[KEY_NR(element)] = TRUE;
13395 DrawGameDoorValues();
13397 else if (element == EL_DC_KEY_WHITE)
13399 player->num_white_keys++;
13401 /* display white keys? */
13402 /* DrawGameDoorValues(); */
13404 else if (IS_ENVELOPE(element))
13406 player->show_envelope = element;
13408 else if (element == EL_EMC_LENSES)
13410 game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
13412 RedrawAllInvisibleElementsForLenses();
13414 else if (element == EL_EMC_MAGNIFIER)
13416 game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
13418 RedrawAllInvisibleElementsForMagnifier();
13420 else if (IS_DROPPABLE(element) ||
13421 IS_THROWABLE(element)) /* can be collected and dropped */
13425 if (collect_count == 0)
13426 player->inventory_infinite_element = element;
13428 for (i = 0; i < collect_count; i++)
13429 if (player->inventory_size < MAX_INVENTORY_SIZE)
13430 player->inventory_element[player->inventory_size++] = element;
13432 DrawGameDoorValues();
13434 else if (collect_count > 0)
13436 local_player->gems_still_needed -= collect_count;
13437 if (local_player->gems_still_needed < 0)
13438 local_player->gems_still_needed = 0;
13440 game_panel_controls[GAME_PANEL_GEMS].value = local_player->gems_still_needed;
13442 DisplayGameControlValues();
13445 RaiseScoreElement(element);
13446 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
13449 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
13450 player->index_bit, dig_side);
13452 if (mode == DF_SNAP)
13454 if (level.block_snap_field)
13455 setFieldForSnapping(x, y, element, move_direction);
13457 TestIfElementTouchesCustomElement(x, y); /* for empty space */
13459 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13460 player->index_bit, dig_side);
13463 else if (player_can_move_or_snap && IS_PUSHABLE(element))
13465 if (mode == DF_SNAP && element != EL_BD_ROCK)
13466 return MP_NO_ACTION;
13468 if (CAN_FALL(element) && dy)
13469 return MP_NO_ACTION;
13471 if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
13472 !(element == EL_SPRING && level.use_spring_bug))
13473 return MP_NO_ACTION;
13475 if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
13476 ((move_direction & MV_VERTICAL &&
13477 ((element_info[element].move_pattern & MV_LEFT &&
13478 IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
13479 (element_info[element].move_pattern & MV_RIGHT &&
13480 IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
13481 (move_direction & MV_HORIZONTAL &&
13482 ((element_info[element].move_pattern & MV_UP &&
13483 IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
13484 (element_info[element].move_pattern & MV_DOWN &&
13485 IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
13486 return MP_NO_ACTION;
13488 /* do not push elements already moving away faster than player */
13489 if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
13490 ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
13491 return MP_NO_ACTION;
13493 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
13495 if (player->push_delay_value == -1 || !player_was_pushing)
13496 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13498 else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
13500 if (player->push_delay_value == -1)
13501 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13503 else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
13505 if (!player->is_pushing)
13506 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13509 player->is_pushing = TRUE;
13510 player->is_active = TRUE;
13512 if (!(IN_LEV_FIELD(nextx, nexty) &&
13513 (IS_FREE(nextx, nexty) ||
13514 (IS_SB_ELEMENT(element) &&
13515 Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY) ||
13516 (IS_CUSTOM_ELEMENT(element) &&
13517 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty)))))
13518 return MP_NO_ACTION;
13520 if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
13521 return MP_NO_ACTION;
13523 if (player->push_delay == -1) /* new pushing; restart delay */
13524 player->push_delay = 0;
13526 if (player->push_delay < player->push_delay_value &&
13527 !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
13528 element != EL_SPRING && element != EL_BALLOON)
13530 /* make sure that there is no move delay before next try to push */
13531 if (game.engine_version >= VERSION_IDENT(3,0,7,1))
13532 player->move_delay = 0;
13534 return MP_NO_ACTION;
13537 if (IS_CUSTOM_ELEMENT(element) &&
13538 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty))
13540 if (!DigFieldByCE(nextx, nexty, element))
13541 return MP_NO_ACTION;
13544 if (IS_SB_ELEMENT(element))
13546 if (element == EL_SOKOBAN_FIELD_FULL)
13548 Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
13549 local_player->sokobanfields_still_needed++;
13552 if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
13554 Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
13555 local_player->sokobanfields_still_needed--;
13558 Feld[x][y] = EL_SOKOBAN_OBJECT;
13560 if (Back[x][y] == Back[nextx][nexty])
13561 PlayLevelSoundAction(x, y, ACTION_PUSHING);
13562 else if (Back[x][y] != 0)
13563 PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
13566 PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
13569 if (local_player->sokobanfields_still_needed == 0 &&
13570 (game.emulation == EMU_SOKOBAN || level.auto_exit_sokoban))
13572 PlayerWins(player);
13574 PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
13578 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
13580 InitMovingField(x, y, move_direction);
13581 GfxAction[x][y] = ACTION_PUSHING;
13583 if (mode == DF_SNAP)
13584 ContinueMoving(x, y);
13586 MovPos[x][y] = (dx != 0 ? dx : dy);
13588 Pushed[x][y] = TRUE;
13589 Pushed[nextx][nexty] = TRUE;
13591 if (game.engine_version < VERSION_IDENT(2,2,0,7))
13592 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13594 player->push_delay_value = -1; /* get new value later */
13596 /* check for element change _after_ element has been pushed */
13597 if (game.use_change_when_pushing_bug)
13599 CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
13600 player->index_bit, dig_side);
13601 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
13602 player->index_bit, dig_side);
13605 else if (IS_SWITCHABLE(element))
13607 if (PLAYER_SWITCHING(player, x, y))
13609 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
13610 player->index_bit, dig_side);
13615 player->is_switching = TRUE;
13616 player->switch_x = x;
13617 player->switch_y = y;
13619 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
13621 if (element == EL_ROBOT_WHEEL)
13623 Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
13627 game.robot_wheel_active = TRUE;
13629 TEST_DrawLevelField(x, y);
13631 else if (element == EL_SP_TERMINAL)
13635 SCAN_PLAYFIELD(xx, yy)
13637 if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
13639 else if (Feld[xx][yy] == EL_SP_TERMINAL)
13640 Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
13643 else if (IS_BELT_SWITCH(element))
13645 ToggleBeltSwitch(x, y);
13647 else if (element == EL_SWITCHGATE_SWITCH_UP ||
13648 element == EL_SWITCHGATE_SWITCH_DOWN ||
13649 element == EL_DC_SWITCHGATE_SWITCH_UP ||
13650 element == EL_DC_SWITCHGATE_SWITCH_DOWN)
13652 ToggleSwitchgateSwitch(x, y);
13654 else if (element == EL_LIGHT_SWITCH ||
13655 element == EL_LIGHT_SWITCH_ACTIVE)
13657 ToggleLightSwitch(x, y);
13659 else if (element == EL_TIMEGATE_SWITCH ||
13660 element == EL_DC_TIMEGATE_SWITCH)
13662 ActivateTimegateSwitch(x, y);
13664 else if (element == EL_BALLOON_SWITCH_LEFT ||
13665 element == EL_BALLOON_SWITCH_RIGHT ||
13666 element == EL_BALLOON_SWITCH_UP ||
13667 element == EL_BALLOON_SWITCH_DOWN ||
13668 element == EL_BALLOON_SWITCH_NONE ||
13669 element == EL_BALLOON_SWITCH_ANY)
13671 game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT ? MV_LEFT :
13672 element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
13673 element == EL_BALLOON_SWITCH_UP ? MV_UP :
13674 element == EL_BALLOON_SWITCH_DOWN ? MV_DOWN :
13675 element == EL_BALLOON_SWITCH_NONE ? MV_NONE :
13678 else if (element == EL_LAMP)
13680 Feld[x][y] = EL_LAMP_ACTIVE;
13681 local_player->lights_still_needed--;
13683 ResetGfxAnimation(x, y);
13684 TEST_DrawLevelField(x, y);
13686 else if (element == EL_TIME_ORB_FULL)
13688 Feld[x][y] = EL_TIME_ORB_EMPTY;
13690 if (level.time > 0 || level.use_time_orb_bug)
13692 TimeLeft += level.time_orb_time;
13693 game.no_time_limit = FALSE;
13695 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
13697 DisplayGameControlValues();
13700 ResetGfxAnimation(x, y);
13701 TEST_DrawLevelField(x, y);
13703 else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
13704 element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
13708 game.ball_state = !game.ball_state;
13710 SCAN_PLAYFIELD(xx, yy)
13712 int e = Feld[xx][yy];
13714 if (game.ball_state)
13716 if (e == EL_EMC_MAGIC_BALL)
13717 CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
13718 else if (e == EL_EMC_MAGIC_BALL_SWITCH)
13719 CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
13723 if (e == EL_EMC_MAGIC_BALL_ACTIVE)
13724 CreateField(xx, yy, EL_EMC_MAGIC_BALL);
13725 else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
13726 CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
13731 CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
13732 player->index_bit, dig_side);
13734 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
13735 player->index_bit, dig_side);
13737 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
13738 player->index_bit, dig_side);
13744 if (!PLAYER_SWITCHING(player, x, y))
13746 player->is_switching = TRUE;
13747 player->switch_x = x;
13748 player->switch_y = y;
13750 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
13751 player->index_bit, dig_side);
13752 CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
13753 player->index_bit, dig_side);
13755 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
13756 player->index_bit, dig_side);
13757 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
13758 player->index_bit, dig_side);
13761 CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
13762 player->index_bit, dig_side);
13763 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
13764 player->index_bit, dig_side);
13766 return MP_NO_ACTION;
13769 player->push_delay = -1;
13771 if (is_player) /* function can also be called by EL_PENGUIN */
13773 if (Feld[x][y] != element) /* really digged/collected something */
13775 player->is_collecting = !player->is_digging;
13776 player->is_active = TRUE;
13783 static boolean DigFieldByCE(int x, int y, int digging_element)
13785 int element = Feld[x][y];
13787 if (!IS_FREE(x, y))
13789 int action = (IS_DIGGABLE(element) ? ACTION_DIGGING :
13790 IS_COLLECTIBLE(element) ? ACTION_COLLECTING :
13793 /* no element can dig solid indestructible elements */
13794 if (IS_INDESTRUCTIBLE(element) &&
13795 !IS_DIGGABLE(element) &&
13796 !IS_COLLECTIBLE(element))
13799 if (AmoebaNr[x][y] &&
13800 (element == EL_AMOEBA_FULL ||
13801 element == EL_BD_AMOEBA ||
13802 element == EL_AMOEBA_GROWING))
13804 AmoebaCnt[AmoebaNr[x][y]]--;
13805 AmoebaCnt2[AmoebaNr[x][y]]--;
13808 if (IS_MOVING(x, y))
13809 RemoveMovingField(x, y);
13813 TEST_DrawLevelField(x, y);
13816 /* if digged element was about to explode, prevent the explosion */
13817 ExplodeField[x][y] = EX_TYPE_NONE;
13819 PlayLevelSoundAction(x, y, action);
13822 Store[x][y] = EL_EMPTY;
13824 /* this makes it possible to leave the removed element again */
13825 if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element)))
13826 Store[x][y] = element;
13831 static boolean SnapField(struct PlayerInfo *player, int dx, int dy)
13833 int jx = player->jx, jy = player->jy;
13834 int x = jx + dx, y = jy + dy;
13835 int snap_direction = (dx == -1 ? MV_LEFT :
13836 dx == +1 ? MV_RIGHT :
13838 dy == +1 ? MV_DOWN : MV_NONE);
13839 boolean can_continue_snapping = (level.continuous_snapping &&
13840 WasJustFalling[x][y] < CHECK_DELAY_FALLING);
13842 if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
13845 if (!player->active || !IN_LEV_FIELD(x, y))
13853 if (player->MovPos == 0)
13854 player->is_pushing = FALSE;
13856 player->is_snapping = FALSE;
13858 if (player->MovPos == 0)
13860 player->is_moving = FALSE;
13861 player->is_digging = FALSE;
13862 player->is_collecting = FALSE;
13868 /* prevent snapping with already pressed snap key when not allowed */
13869 if (player->is_snapping && !can_continue_snapping)
13872 player->MovDir = snap_direction;
13874 if (player->MovPos == 0)
13876 player->is_moving = FALSE;
13877 player->is_digging = FALSE;
13878 player->is_collecting = FALSE;
13881 player->is_dropping = FALSE;
13882 player->is_dropping_pressed = FALSE;
13883 player->drop_pressed_delay = 0;
13885 if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
13888 player->is_snapping = TRUE;
13889 player->is_active = TRUE;
13891 if (player->MovPos == 0)
13893 player->is_moving = FALSE;
13894 player->is_digging = FALSE;
13895 player->is_collecting = FALSE;
13898 if (player->MovPos != 0) /* prevent graphic bugs in versions < 2.2.0 */
13899 TEST_DrawLevelField(player->last_jx, player->last_jy);
13901 TEST_DrawLevelField(x, y);
13906 static boolean DropElement(struct PlayerInfo *player)
13908 int old_element, new_element;
13909 int dropx = player->jx, dropy = player->jy;
13910 int drop_direction = player->MovDir;
13911 int drop_side = drop_direction;
13912 int drop_element = get_next_dropped_element(player);
13914 player->is_dropping_pressed = TRUE;
13916 /* do not drop an element on top of another element; when holding drop key
13917 pressed without moving, dropped element must move away before the next
13918 element can be dropped (this is especially important if the next element
13919 is dynamite, which can be placed on background for historical reasons) */
13920 if (PLAYER_DROPPING(player, dropx, dropy) && Feld[dropx][dropy] != EL_EMPTY)
13923 if (IS_THROWABLE(drop_element))
13925 dropx += GET_DX_FROM_DIR(drop_direction);
13926 dropy += GET_DY_FROM_DIR(drop_direction);
13928 if (!IN_LEV_FIELD(dropx, dropy))
13932 old_element = Feld[dropx][dropy]; /* old element at dropping position */
13933 new_element = drop_element; /* default: no change when dropping */
13935 /* check if player is active, not moving and ready to drop */
13936 if (!player->active || player->MovPos || player->drop_delay > 0)
13939 /* check if player has anything that can be dropped */
13940 if (new_element == EL_UNDEFINED)
13943 /* check if drop key was pressed long enough for EM style dynamite */
13944 if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
13947 /* check if anything can be dropped at the current position */
13948 if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
13951 /* collected custom elements can only be dropped on empty fields */
13952 if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
13955 if (old_element != EL_EMPTY)
13956 Back[dropx][dropy] = old_element; /* store old element on this field */
13958 ResetGfxAnimation(dropx, dropy);
13959 ResetRandomAnimationValue(dropx, dropy);
13961 if (player->inventory_size > 0 ||
13962 player->inventory_infinite_element != EL_UNDEFINED)
13964 if (player->inventory_size > 0)
13966 player->inventory_size--;
13968 DrawGameDoorValues();
13970 if (new_element == EL_DYNAMITE)
13971 new_element = EL_DYNAMITE_ACTIVE;
13972 else if (new_element == EL_EM_DYNAMITE)
13973 new_element = EL_EM_DYNAMITE_ACTIVE;
13974 else if (new_element == EL_SP_DISK_RED)
13975 new_element = EL_SP_DISK_RED_ACTIVE;
13978 Feld[dropx][dropy] = new_element;
13980 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
13981 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
13982 el2img(Feld[dropx][dropy]), 0);
13984 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
13986 /* needed if previous element just changed to "empty" in the last frame */
13987 ChangeCount[dropx][dropy] = 0; /* allow at least one more change */
13989 CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
13990 player->index_bit, drop_side);
13991 CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
13993 player->index_bit, drop_side);
13995 TestIfElementTouchesCustomElement(dropx, dropy);
13997 else /* player is dropping a dyna bomb */
13999 player->dynabombs_left--;
14001 Feld[dropx][dropy] = new_element;
14003 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
14004 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
14005 el2img(Feld[dropx][dropy]), 0);
14007 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
14010 if (Feld[dropx][dropy] == new_element) /* uninitialized unless CE change */
14011 InitField_WithBug1(dropx, dropy, FALSE);
14013 new_element = Feld[dropx][dropy]; /* element might have changed */
14015 if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
14016 element_info[new_element].move_pattern == MV_WHEN_DROPPED)
14018 if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
14019 MovDir[dropx][dropy] = drop_direction;
14021 ChangeCount[dropx][dropy] = 0; /* allow at least one more change */
14023 /* do not cause impact style collision by dropping elements that can fall */
14024 CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
14027 player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
14028 player->is_dropping = TRUE;
14030 player->drop_pressed_delay = 0;
14031 player->is_dropping_pressed = FALSE;
14033 player->drop_x = dropx;
14034 player->drop_y = dropy;
14039 /* ------------------------------------------------------------------------- */
14040 /* game sound playing functions */
14041 /* ------------------------------------------------------------------------- */
14043 static int *loop_sound_frame = NULL;
14044 static int *loop_sound_volume = NULL;
14046 void InitPlayLevelSound()
14048 int num_sounds = getSoundListSize();
14050 checked_free(loop_sound_frame);
14051 checked_free(loop_sound_volume);
14053 loop_sound_frame = checked_calloc(num_sounds * sizeof(int));
14054 loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
14057 static void PlayLevelSound(int x, int y, int nr)
14059 int sx = SCREENX(x), sy = SCREENY(y);
14060 int volume, stereo_position;
14061 int max_distance = 8;
14062 int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
14064 if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
14065 (!setup.sound_loops && IS_LOOP_SOUND(nr)))
14068 if (!IN_LEV_FIELD(x, y) ||
14069 sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
14070 sy < -max_distance || sy >= SCR_FIELDY + max_distance)
14073 volume = SOUND_MAX_VOLUME;
14075 if (!IN_SCR_FIELD(sx, sy))
14077 int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
14078 int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
14080 volume -= volume * (dx > dy ? dx : dy) / max_distance;
14083 stereo_position = (SOUND_MAX_LEFT +
14084 (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
14085 (SCR_FIELDX + 2 * max_distance));
14087 if (IS_LOOP_SOUND(nr))
14089 /* This assures that quieter loop sounds do not overwrite louder ones,
14090 while restarting sound volume comparison with each new game frame. */
14092 if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
14095 loop_sound_volume[nr] = volume;
14096 loop_sound_frame[nr] = FrameCounter;
14099 PlaySoundExt(nr, volume, stereo_position, type);
14102 static void PlayLevelSoundNearest(int x, int y, int sound_action)
14104 PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
14105 x > LEVELX(BX2) ? LEVELX(BX2) : x,
14106 y < LEVELY(BY1) ? LEVELY(BY1) :
14107 y > LEVELY(BY2) ? LEVELY(BY2) : y,
14111 static void PlayLevelSoundAction(int x, int y, int action)
14113 PlayLevelSoundElementAction(x, y, Feld[x][y], action);
14116 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
14118 int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
14120 if (sound_effect != SND_UNDEFINED)
14121 PlayLevelSound(x, y, sound_effect);
14124 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
14127 int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
14129 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14130 PlayLevelSound(x, y, sound_effect);
14133 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
14135 int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
14137 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14138 PlayLevelSound(x, y, sound_effect);
14141 static void StopLevelSoundActionIfLoop(int x, int y, int action)
14143 int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
14145 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14146 StopSound(sound_effect);
14149 static void PlayLevelMusic()
14151 if (levelset.music[level_nr] != MUS_UNDEFINED)
14152 PlayMusic(levelset.music[level_nr]); /* from config file */
14154 PlayMusic(MAP_NOCONF_MUSIC(level_nr)); /* from music dir */
14157 void PlayLevelSound_EM(int xx, int yy, int element_em, int sample)
14159 int element = (element_em > -1 ? map_element_EM_to_RND(element_em) : 0);
14160 int offset = (BorderElement == EL_STEELWALL ? 1 : 0);
14161 int x = xx - 1 - offset;
14162 int y = yy - 1 - offset;
14167 PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
14171 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14175 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14179 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14183 PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
14187 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14191 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14194 case SAMPLE_android_clone:
14195 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14198 case SAMPLE_android_move:
14199 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14202 case SAMPLE_spring:
14203 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14207 PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
14211 PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
14214 case SAMPLE_eater_eat:
14215 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14219 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14222 case SAMPLE_collect:
14223 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
14226 case SAMPLE_diamond:
14227 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14230 case SAMPLE_squash:
14231 /* !!! CHECK THIS !!! */
14233 PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
14235 PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
14239 case SAMPLE_wonderfall:
14240 PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
14244 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14248 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14252 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14256 PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
14260 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14264 PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
14267 case SAMPLE_wonder:
14268 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14272 PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
14275 case SAMPLE_exit_open:
14276 PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
14279 case SAMPLE_exit_leave:
14280 PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
14283 case SAMPLE_dynamite:
14284 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14288 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14292 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
14296 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14300 PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
14304 PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
14308 PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
14312 PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
14317 void PlayLevelSound_SP(int xx, int yy, int element_sp, int action_sp)
14319 int element = map_element_SP_to_RND(element_sp);
14320 int action = map_action_SP_to_RND(action_sp);
14321 int offset = (setup.sp_show_border_elements ? 0 : 1);
14322 int x = xx - offset;
14323 int y = yy - offset;
14325 PlayLevelSoundElementAction(x, y, element, action);
14328 void RaiseScore(int value)
14330 local_player->score += value;
14332 game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
14334 DisplayGameControlValues();
14337 void RaiseScoreElement(int element)
14342 case EL_BD_DIAMOND:
14343 case EL_EMERALD_YELLOW:
14344 case EL_EMERALD_RED:
14345 case EL_EMERALD_PURPLE:
14346 case EL_SP_INFOTRON:
14347 RaiseScore(level.score[SC_EMERALD]);
14350 RaiseScore(level.score[SC_DIAMOND]);
14353 RaiseScore(level.score[SC_CRYSTAL]);
14356 RaiseScore(level.score[SC_PEARL]);
14359 case EL_BD_BUTTERFLY:
14360 case EL_SP_ELECTRON:
14361 RaiseScore(level.score[SC_BUG]);
14364 case EL_BD_FIREFLY:
14365 case EL_SP_SNIKSNAK:
14366 RaiseScore(level.score[SC_SPACESHIP]);
14369 case EL_DARK_YAMYAM:
14370 RaiseScore(level.score[SC_YAMYAM]);
14373 RaiseScore(level.score[SC_ROBOT]);
14376 RaiseScore(level.score[SC_PACMAN]);
14379 RaiseScore(level.score[SC_NUT]);
14382 case EL_EM_DYNAMITE:
14383 case EL_SP_DISK_RED:
14384 case EL_DYNABOMB_INCREASE_NUMBER:
14385 case EL_DYNABOMB_INCREASE_SIZE:
14386 case EL_DYNABOMB_INCREASE_POWER:
14387 RaiseScore(level.score[SC_DYNAMITE]);
14389 case EL_SHIELD_NORMAL:
14390 case EL_SHIELD_DEADLY:
14391 RaiseScore(level.score[SC_SHIELD]);
14393 case EL_EXTRA_TIME:
14394 RaiseScore(level.extra_time_score);
14408 case EL_DC_KEY_WHITE:
14409 RaiseScore(level.score[SC_KEY]);
14412 RaiseScore(element_info[element].collect_score);
14417 void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message)
14419 if (skip_request || Request(message, REQ_ASK | REQ_STAY_CLOSED))
14421 /* closing door required in case of envelope style request dialogs */
14423 CloseDoor(DOOR_CLOSE_1);
14425 #if defined(NETWORK_AVALIABLE)
14426 if (options.network)
14427 SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
14433 FadeSkipNextFadeIn();
14435 game_status = GAME_MODE_MAIN;
14437 DrawAndFadeInMainMenu(REDRAW_FIELD);
14441 game_status = GAME_MODE_MAIN;
14443 DrawAndFadeInMainMenu(REDRAW_FIELD);
14447 else /* continue playing the game */
14449 if (tape.playing && tape.deactivate_display)
14450 TapeDeactivateDisplayOff(TRUE);
14452 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
14454 if (tape.playing && tape.deactivate_display)
14455 TapeDeactivateDisplayOn();
14459 void RequestQuitGame(boolean ask_if_really_quit)
14461 boolean quick_quit = (!ask_if_really_quit || level_editor_test_game);
14462 boolean skip_request = AllPlayersGone || quick_quit;
14464 RequestQuitGameExt(skip_request, quick_quit,
14465 "Do you really want to quit the game?");
14469 /* ------------------------------------------------------------------------- */
14470 /* random generator functions */
14471 /* ------------------------------------------------------------------------- */
14473 unsigned int InitEngineRandom_RND(int seed)
14475 game.num_random_calls = 0;
14477 return InitEngineRandom(seed);
14480 unsigned int RND(int max)
14484 game.num_random_calls++;
14486 return GetEngineRandom(max);
14493 /* ------------------------------------------------------------------------- */
14494 /* game engine snapshot handling functions */
14495 /* ------------------------------------------------------------------------- */
14497 struct EngineSnapshotInfo
14499 /* runtime values for custom element collect score */
14500 int collect_score[NUM_CUSTOM_ELEMENTS];
14502 /* runtime values for group element choice position */
14503 int choice_pos[NUM_GROUP_ELEMENTS];
14505 /* runtime values for belt position animations */
14506 int belt_graphic[4][NUM_BELT_PARTS];
14507 int belt_anim_mode[4][NUM_BELT_PARTS];
14510 static struct EngineSnapshotInfo engine_snapshot_rnd;
14511 static char *snapshot_level_identifier = NULL;
14512 static int snapshot_level_nr = -1;
14514 static void SaveEngineSnapshotValues_RND()
14516 static int belt_base_active_element[4] =
14518 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
14519 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
14520 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
14521 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
14525 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
14527 int element = EL_CUSTOM_START + i;
14529 engine_snapshot_rnd.collect_score[i] = element_info[element].collect_score;
14532 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
14534 int element = EL_GROUP_START + i;
14536 engine_snapshot_rnd.choice_pos[i] = element_info[element].group->choice_pos;
14539 for (i = 0; i < 4; i++)
14541 for (j = 0; j < NUM_BELT_PARTS; j++)
14543 int element = belt_base_active_element[i] + j;
14544 int graphic = el2img(element);
14545 int anim_mode = graphic_info[graphic].anim_mode;
14547 engine_snapshot_rnd.belt_graphic[i][j] = graphic;
14548 engine_snapshot_rnd.belt_anim_mode[i][j] = anim_mode;
14553 static void LoadEngineSnapshotValues_RND()
14555 unsigned int num_random_calls = game.num_random_calls;
14558 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
14560 int element = EL_CUSTOM_START + i;
14562 element_info[element].collect_score = engine_snapshot_rnd.collect_score[i];
14565 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
14567 int element = EL_GROUP_START + i;
14569 element_info[element].group->choice_pos = engine_snapshot_rnd.choice_pos[i];
14572 for (i = 0; i < 4; i++)
14574 for (j = 0; j < NUM_BELT_PARTS; j++)
14576 int graphic = engine_snapshot_rnd.belt_graphic[i][j];
14577 int anim_mode = engine_snapshot_rnd.belt_anim_mode[i][j];
14579 graphic_info[graphic].anim_mode = anim_mode;
14583 if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
14585 InitRND(tape.random_seed);
14586 for (i = 0; i < num_random_calls; i++)
14590 if (game.num_random_calls != num_random_calls)
14592 Error(ERR_INFO, "number of random calls out of sync");
14593 Error(ERR_INFO, "number of random calls should be %d", num_random_calls);
14594 Error(ERR_INFO, "number of random calls is %d", game.num_random_calls);
14595 Error(ERR_EXIT, "this should not happen -- please debug");
14599 void FreeEngineSnapshotSingle()
14601 FreeSnapshotSingle();
14603 setString(&snapshot_level_identifier, NULL);
14604 snapshot_level_nr = -1;
14607 void FreeEngineSnapshotList()
14609 FreeSnapshotList();
14612 ListNode *SaveEngineSnapshotBuffers()
14614 ListNode *buffers = NULL;
14616 /* do not save snapshots from editor */
14617 if (level_editor_test_game)
14620 /* copy some special values to a structure better suited for the snapshot */
14622 if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
14623 SaveEngineSnapshotValues_RND();
14624 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
14625 SaveEngineSnapshotValues_EM();
14626 if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
14627 SaveEngineSnapshotValues_SP(&buffers);
14629 /* save values stored in special snapshot structure */
14631 if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
14632 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
14633 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
14634 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
14635 if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
14636 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_sp));
14638 /* save further RND engine values */
14640 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(stored_player));
14641 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(game));
14642 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(tape));
14644 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ZX));
14645 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ZY));
14646 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExitX));
14647 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExitY));
14649 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(FrameCounter));
14650 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeFrames));
14651 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimePlayed));
14652 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeLeft));
14653 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TapeTime));
14655 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovDir));
14656 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovPos));
14657 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenGfxPos));
14659 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScrollStepSize));
14661 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AllPlayersGone));
14663 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt));
14664 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2));
14666 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Feld));
14667 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovPos));
14668 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDir));
14669 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDelay));
14670 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeDelay));
14671 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangePage));
14672 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CustomValue));
14673 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store));
14674 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store2));
14675 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(StorePlayer));
14676 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Back));
14677 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaNr));
14678 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustMoving));
14679 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustFalling));
14680 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckCollision));
14681 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckImpact));
14682 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Stop));
14683 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Pushed));
14685 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeCount));
14686 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeEvent));
14688 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodePhase));
14689 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeDelay));
14690 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeField));
14692 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(RunnerVisit));
14693 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(PlayerVisit));
14695 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxFrame));
14696 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxRandom));
14697 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxElement));
14698 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxAction));
14699 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxDir));
14701 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_x));
14702 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_y));
14704 /* save level identification information */
14706 setString(&snapshot_level_identifier, leveldir_current->identifier);
14707 snapshot_level_nr = level_nr;
14710 ListNode *node = engine_snapshot_list_rnd;
14713 while (node != NULL)
14715 num_bytes += ((struct EngineSnapshotNodeInfo *)node->content)->size;
14720 printf("::: size of engine snapshot: %d bytes\n", num_bytes);
14726 void SaveEngineSnapshotSingle()
14728 ListNode *buffers = SaveEngineSnapshotBuffers();
14730 /* finally save all snapshot buffers to single snapshot */
14731 SaveSnapshotSingle(buffers);
14734 void SaveEngineSnapshotToList()
14736 ListNode *buffers = SaveEngineSnapshotBuffers();
14738 /* finally save all snapshot buffers to snapshot list */
14739 SaveSnapshotToList(buffers);
14742 void LoadEngineSnapshotValues()
14744 /* restore special values from snapshot structure */
14746 if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
14747 LoadEngineSnapshotValues_RND();
14748 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
14749 LoadEngineSnapshotValues_EM();
14750 if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
14751 LoadEngineSnapshotValues_SP();
14754 void LoadEngineSnapshotSingle()
14756 LoadSnapshotSingle();
14758 LoadEngineSnapshotValues();
14761 void LoadEngineSnapshot_Undo()
14763 LoadSnapshotFromList_Older();
14765 LoadEngineSnapshotValues();
14768 void LoadEngineSnapshot_Redo()
14770 LoadSnapshotFromList_Newer();
14772 LoadEngineSnapshotValues();
14775 boolean CheckEngineSnapshot()
14777 return (strEqual(snapshot_level_identifier, leveldir_current->identifier) &&
14778 snapshot_level_nr == level_nr);
14782 /* ---------- new game button stuff ---------------------------------------- */
14790 } gamebutton_info[NUM_GAME_BUTTONS] =
14793 IMG_GAME_BUTTON_GFX_STOP, &game.button.stop,
14794 GAME_CTRL_ID_STOP, "stop game"
14797 IMG_GAME_BUTTON_GFX_PAUSE, &game.button.pause,
14798 GAME_CTRL_ID_PAUSE, "pause game"
14801 IMG_GAME_BUTTON_GFX_PLAY, &game.button.play,
14802 GAME_CTRL_ID_PLAY, "play game"
14805 IMG_GAME_BUTTON_GFX_UNDO, &game.button.undo,
14806 GAME_CTRL_ID_UNDO, "undo step"
14809 IMG_GAME_BUTTON_GFX_REDO, &game.button.redo,
14810 GAME_CTRL_ID_REDO, "redo step"
14813 IMG_GAME_BUTTON_GFX_SAVE, &game.button.save,
14814 GAME_CTRL_ID_SAVE, "save game"
14817 IMG_GAME_BUTTON_GFX_LOAD, &game.button.load,
14818 GAME_CTRL_ID_LOAD, "load game"
14821 IMG_GAME_BUTTON_GFX_SOUND_MUSIC, &game.button.sound_music,
14822 SOUND_CTRL_ID_MUSIC, "background music on/off"
14825 IMG_GAME_BUTTON_GFX_SOUND_LOOPS, &game.button.sound_loops,
14826 SOUND_CTRL_ID_LOOPS, "sound loops on/off"
14829 IMG_GAME_BUTTON_GFX_SOUND_SIMPLE, &game.button.sound_simple,
14830 SOUND_CTRL_ID_SIMPLE, "normal sounds on/off"
14834 void CreateGameButtons()
14838 for (i = 0; i < NUM_GAME_BUTTONS; i++)
14840 struct GraphicInfo *gfx = &graphic_info[gamebutton_info[i].graphic];
14841 struct XY *pos = gamebutton_info[i].pos;
14842 struct GadgetInfo *gi;
14845 unsigned int event_mask;
14846 int base_x = (tape.show_game_buttons ? VX : DX);
14847 int base_y = (tape.show_game_buttons ? VY : DY);
14848 int gd_x = gfx->src_x;
14849 int gd_y = gfx->src_y;
14850 int gd_xp = gfx->src_x + gfx->pressed_xoffset;
14851 int gd_yp = gfx->src_y + gfx->pressed_yoffset;
14852 int gd_xa = gfx->src_x + gfx->active_xoffset;
14853 int gd_ya = gfx->src_y + gfx->active_yoffset;
14854 int gd_xap = gfx->src_x + gfx->active_xoffset + gfx->pressed_xoffset;
14855 int gd_yap = gfx->src_y + gfx->active_yoffset + gfx->pressed_yoffset;
14858 if (gfx->bitmap == NULL)
14860 game_gadget[id] = NULL;
14865 if (id == GAME_CTRL_ID_STOP ||
14866 id == GAME_CTRL_ID_PAUSE ||
14867 id == GAME_CTRL_ID_PLAY ||
14868 id == GAME_CTRL_ID_SAVE ||
14869 id == GAME_CTRL_ID_LOAD)
14871 button_type = GD_TYPE_NORMAL_BUTTON;
14873 event_mask = GD_EVENT_RELEASED;
14875 else if (id == GAME_CTRL_ID_UNDO ||
14876 id == GAME_CTRL_ID_REDO)
14878 button_type = GD_TYPE_NORMAL_BUTTON;
14880 event_mask = GD_EVENT_PRESSED | GD_EVENT_REPEATED;
14884 button_type = GD_TYPE_CHECK_BUTTON;
14886 ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
14887 (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
14888 (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
14889 event_mask = GD_EVENT_PRESSED;
14892 gi = CreateGadget(GDI_CUSTOM_ID, id,
14893 GDI_INFO_TEXT, gamebutton_info[i].infotext,
14894 GDI_X, base_x + GDI_ACTIVE_POS(pos->x),
14895 GDI_Y, base_y + GDI_ACTIVE_POS(pos->y),
14896 GDI_WIDTH, gfx->width,
14897 GDI_HEIGHT, gfx->height,
14898 GDI_TYPE, button_type,
14899 GDI_STATE, GD_BUTTON_UNPRESSED,
14900 GDI_CHECKED, checked,
14901 GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
14902 GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
14903 GDI_ALT_DESIGN_UNPRESSED, gfx->bitmap, gd_xa, gd_ya,
14904 GDI_ALT_DESIGN_PRESSED, gfx->bitmap, gd_xap, gd_yap,
14905 GDI_DIRECT_DRAW, FALSE,
14906 GDI_EVENT_MASK, event_mask,
14907 GDI_CALLBACK_ACTION, HandleGameButtons,
14911 Error(ERR_EXIT, "cannot create gadget");
14913 game_gadget[id] = gi;
14917 void FreeGameButtons()
14921 for (i = 0; i < NUM_GAME_BUTTONS; i++)
14922 FreeGadget(game_gadget[i]);
14925 void MapStopPlayButtons()
14927 UnmapGadget(game_gadget[GAME_CTRL_ID_UNDO]);
14928 UnmapGadget(game_gadget[GAME_CTRL_ID_REDO]);
14930 MapGadget(game_gadget[GAME_CTRL_ID_STOP]);
14931 MapGadget(game_gadget[GAME_CTRL_ID_PLAY]);
14934 void MapUndoRedoButtons()
14936 UnmapGadget(game_gadget[GAME_CTRL_ID_STOP]);
14937 UnmapGadget(game_gadget[GAME_CTRL_ID_PLAY]);
14939 MapGadget(game_gadget[GAME_CTRL_ID_UNDO]);
14940 MapGadget(game_gadget[GAME_CTRL_ID_REDO]);
14943 void MapGameButtons()
14947 for (i = 0; i < NUM_GAME_BUTTONS; i++)
14948 if (i != GAME_CTRL_ID_UNDO &&
14949 i != GAME_CTRL_ID_REDO)
14950 MapGadget(game_gadget[i]);
14953 void UnmapGameButtons()
14957 for (i = 0; i < NUM_GAME_BUTTONS; i++)
14958 UnmapGadget(game_gadget[i]);
14961 void RedrawGameButtons()
14965 for (i = 0; i < NUM_GAME_BUTTONS; i++)
14966 RedrawGadget(game_gadget[i]);
14968 // RedrawGadget() may have set REDRAW_ALL if buttons are defined off-area
14969 redraw_mask &= ~REDRAW_ALL;
14972 void GameUndoRedoExt()
14974 ClearPlayerAction();
14976 tape.pausing = TRUE;
14979 UpdateAndDisplayGameControlValues();
14981 DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
14982 DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
14989 if (!CheckEngineSnapshot())
14992 LoadEngineSnapshot_Undo();
14999 if (!CheckEngineSnapshot())
15002 LoadEngineSnapshot_Redo();
15007 static void HandleGameButtonsExt(int id)
15009 boolean handle_game_buttons =
15010 (game_status == GAME_MODE_PLAYING ||
15011 (game_status == GAME_MODE_MAIN && tape.show_game_buttons));
15013 if (!handle_game_buttons)
15018 case GAME_CTRL_ID_STOP:
15019 if (game_status == GAME_MODE_MAIN)
15025 RequestQuitGame(TRUE);
15029 case GAME_CTRL_ID_PAUSE:
15030 if (options.network && game_status == GAME_MODE_PLAYING)
15032 #if defined(NETWORK_AVALIABLE)
15034 SendToServer_ContinuePlaying();
15036 SendToServer_PausePlaying();
15040 TapeTogglePause(TAPE_TOGGLE_MANUAL);
15043 case GAME_CTRL_ID_PLAY:
15044 if (game_status == GAME_MODE_MAIN)
15046 StartGameActions(options.network, setup.autorecord, level.random_seed);
15048 else if (tape.pausing)
15050 #if defined(NETWORK_AVALIABLE)
15051 if (options.network)
15052 SendToServer_ContinuePlaying();
15056 tape.pausing = FALSE;
15057 DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF, 0);
15062 case GAME_CTRL_ID_UNDO:
15066 case GAME_CTRL_ID_REDO:
15070 case GAME_CTRL_ID_SAVE:
15074 case GAME_CTRL_ID_LOAD:
15078 case SOUND_CTRL_ID_MUSIC:
15079 if (setup.sound_music)
15081 setup.sound_music = FALSE;
15085 else if (audio.music_available)
15087 setup.sound = setup.sound_music = TRUE;
15089 SetAudioMode(setup.sound);
15095 case SOUND_CTRL_ID_LOOPS:
15096 if (setup.sound_loops)
15097 setup.sound_loops = FALSE;
15098 else if (audio.loops_available)
15100 setup.sound = setup.sound_loops = TRUE;
15102 SetAudioMode(setup.sound);
15106 case SOUND_CTRL_ID_SIMPLE:
15107 if (setup.sound_simple)
15108 setup.sound_simple = FALSE;
15109 else if (audio.sound_available)
15111 setup.sound = setup.sound_simple = TRUE;
15113 SetAudioMode(setup.sound);
15122 static void HandleGameButtons(struct GadgetInfo *gi)
15124 HandleGameButtonsExt(gi->custom_id);
15127 void HandleSoundButtonKeys(Key key)
15130 if (key == setup.shortcut.sound_simple)
15131 ClickOnGadget(game_gadget[SOUND_CTRL_ID_SIMPLE], MB_LEFTBUTTON);
15132 else if (key == setup.shortcut.sound_loops)
15133 ClickOnGadget(game_gadget[SOUND_CTRL_ID_LOOPS], MB_LEFTBUTTON);
15134 else if (key == setup.shortcut.sound_music)
15135 ClickOnGadget(game_gadget[SOUND_CTRL_ID_MUSIC], MB_LEFTBUTTON);