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 /* ---------- initialize game engine snapshots ---------------------------- */
3039 for (i = 0; i < MAX_PLAYERS; i++)
3040 game.snapshot.last_action[i] = 0;
3041 game.snapshot.changed_action = FALSE;
3042 game.snapshot.mode =
3043 (strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_STEP) ?
3044 SNAPSHOT_MODE_EVERY_STEP :
3045 strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_MOVE) ?
3046 SNAPSHOT_MODE_EVERY_MOVE : SNAPSHOT_MODE_OFF);
3048 FreeEngineSnapshotList();
3051 int get_num_special_action(int element, int action_first, int action_last)
3053 int num_special_action = 0;
3056 for (i = action_first; i <= action_last; i++)
3058 boolean found = FALSE;
3060 for (j = 0; j < NUM_DIRECTIONS; j++)
3061 if (el_act_dir2img(element, i, j) !=
3062 el_act_dir2img(element, ACTION_DEFAULT, j))
3066 num_special_action++;
3071 return num_special_action;
3076 =============================================================================
3078 -----------------------------------------------------------------------------
3079 initialize and start new game
3080 =============================================================================
3085 int full_lev_fieldx = lev_fieldx + (BorderElement != EL_EMPTY ? 2 : 0);
3086 int full_lev_fieldy = lev_fieldy + (BorderElement != EL_EMPTY ? 2 : 0);
3088 boolean emulate_bd = TRUE; /* unless non-BOULDERDASH elements found */
3089 boolean emulate_sb = TRUE; /* unless non-SOKOBAN elements found */
3090 boolean emulate_sp = TRUE; /* unless non-SUPAPLEX elements found */
3091 int initial_move_dir = MV_DOWN;
3094 game_status = GAME_MODE_PLAYING;
3098 if (!game.restart_level)
3099 CloseDoor(DOOR_CLOSE_1);
3101 if (level_editor_test_game)
3102 FadeSkipNextFadeIn();
3104 FadeSetEnterScreen();
3106 FadeOut(REDRAW_FIELD);
3108 /* needed if different viewport properties defined for playing */
3109 ChangeViewportPropertiesIfNeeded();
3111 DrawCompleteVideoDisplay();
3114 InitGameControlValues();
3116 /* don't play tapes over network */
3117 network_playing = (options.network && !tape.playing);
3119 for (i = 0; i < MAX_PLAYERS; i++)
3121 struct PlayerInfo *player = &stored_player[i];
3123 player->index_nr = i;
3124 player->index_bit = (1 << i);
3125 player->element_nr = EL_PLAYER_1 + i;
3127 player->present = FALSE;
3128 player->active = FALSE;
3129 player->mapped = FALSE;
3131 player->killed = FALSE;
3132 player->reanimated = FALSE;
3135 player->effective_action = 0;
3136 player->programmed_action = 0;
3139 player->score_final = 0;
3141 player->gems_still_needed = level.gems_needed;
3142 player->sokobanfields_still_needed = 0;
3143 player->lights_still_needed = 0;
3144 player->friends_still_needed = 0;
3146 for (j = 0; j < MAX_NUM_KEYS; j++)
3147 player->key[j] = FALSE;
3149 player->num_white_keys = 0;
3151 player->dynabomb_count = 0;
3152 player->dynabomb_size = 1;
3153 player->dynabombs_left = 0;
3154 player->dynabomb_xl = FALSE;
3156 player->MovDir = initial_move_dir;
3159 player->GfxDir = initial_move_dir;
3160 player->GfxAction = ACTION_DEFAULT;
3162 player->StepFrame = 0;
3164 player->initial_element = player->element_nr;
3165 player->artwork_element =
3166 (level.use_artwork_element[i] ? level.artwork_element[i] :
3167 player->element_nr);
3168 player->use_murphy = FALSE;
3170 player->block_last_field = FALSE; /* initialized in InitPlayerField() */
3171 player->block_delay_adjustment = 0; /* initialized in InitPlayerField() */
3173 player->gravity = level.initial_player_gravity[i];
3175 player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
3177 player->actual_frame_counter = 0;
3179 player->step_counter = 0;
3181 player->last_move_dir = initial_move_dir;
3183 player->is_active = FALSE;
3185 player->is_waiting = FALSE;
3186 player->is_moving = FALSE;
3187 player->is_auto_moving = FALSE;
3188 player->is_digging = FALSE;
3189 player->is_snapping = FALSE;
3190 player->is_collecting = FALSE;
3191 player->is_pushing = FALSE;
3192 player->is_switching = FALSE;
3193 player->is_dropping = FALSE;
3194 player->is_dropping_pressed = FALSE;
3196 player->is_bored = FALSE;
3197 player->is_sleeping = FALSE;
3199 player->frame_counter_bored = -1;
3200 player->frame_counter_sleeping = -1;
3202 player->anim_delay_counter = 0;
3203 player->post_delay_counter = 0;
3205 player->dir_waiting = initial_move_dir;
3206 player->action_waiting = ACTION_DEFAULT;
3207 player->last_action_waiting = ACTION_DEFAULT;
3208 player->special_action_bored = ACTION_DEFAULT;
3209 player->special_action_sleeping = ACTION_DEFAULT;
3211 player->switch_x = -1;
3212 player->switch_y = -1;
3214 player->drop_x = -1;
3215 player->drop_y = -1;
3217 player->show_envelope = 0;
3219 SetPlayerMoveSpeed(player, level.initial_player_stepsize[i], TRUE);
3221 player->push_delay = -1; /* initialized when pushing starts */
3222 player->push_delay_value = game.initial_push_delay_value;
3224 player->drop_delay = 0;
3225 player->drop_pressed_delay = 0;
3227 player->last_jx = -1;
3228 player->last_jy = -1;
3232 player->shield_normal_time_left = 0;
3233 player->shield_deadly_time_left = 0;
3235 player->inventory_infinite_element = EL_UNDEFINED;
3236 player->inventory_size = 0;
3238 if (level.use_initial_inventory[i])
3240 for (j = 0; j < level.initial_inventory_size[i]; j++)
3242 int element = level.initial_inventory_content[i][j];
3243 int collect_count = element_info[element].collect_count_initial;
3246 if (!IS_CUSTOM_ELEMENT(element))
3249 if (collect_count == 0)
3250 player->inventory_infinite_element = element;
3252 for (k = 0; k < collect_count; k++)
3253 if (player->inventory_size < MAX_INVENTORY_SIZE)
3254 player->inventory_element[player->inventory_size++] = element;
3258 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
3259 SnapField(player, 0, 0);
3261 player->LevelSolved = FALSE;
3262 player->GameOver = FALSE;
3264 player->LevelSolved_GameWon = FALSE;
3265 player->LevelSolved_GameEnd = FALSE;
3266 player->LevelSolved_PanelOff = FALSE;
3267 player->LevelSolved_SaveTape = FALSE;
3268 player->LevelSolved_SaveScore = FALSE;
3269 player->LevelSolved_CountingTime = 0;
3270 player->LevelSolved_CountingScore = 0;
3272 map_player_action[i] = i;
3275 network_player_action_received = FALSE;
3277 #if defined(NETWORK_AVALIABLE)
3278 /* initial null action */
3279 if (network_playing)
3280 SendToServer_MovePlayer(MV_NONE);
3289 TimeLeft = level.time;
3292 ScreenMovDir = MV_NONE;
3296 ScrollStepSize = 0; /* will be correctly initialized by ScrollScreen() */
3298 AllPlayersGone = FALSE;
3300 game.no_time_limit = (level.time == 0);
3302 game.yamyam_content_nr = 0;
3303 game.robot_wheel_active = FALSE;
3304 game.magic_wall_active = FALSE;
3305 game.magic_wall_time_left = 0;
3306 game.light_time_left = 0;
3307 game.timegate_time_left = 0;
3308 game.switchgate_pos = 0;
3309 game.wind_direction = level.wind_direction_initial;
3311 game.lenses_time_left = 0;
3312 game.magnify_time_left = 0;
3314 game.ball_state = level.ball_state_initial;
3315 game.ball_content_nr = 0;
3317 game.envelope_active = FALSE;
3319 /* set focus to local player for network games, else to all players */
3320 game.centered_player_nr = (network_playing ? local_player->index_nr : -1);
3321 game.centered_player_nr_next = game.centered_player_nr;
3322 game.set_centered_player = FALSE;
3324 if (network_playing && tape.recording)
3326 /* store client dependent player focus when recording network games */
3327 tape.centered_player_nr_next = game.centered_player_nr_next;
3328 tape.set_centered_player = TRUE;
3331 for (i = 0; i < NUM_BELTS; i++)
3333 game.belt_dir[i] = MV_NONE;
3334 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
3337 for (i = 0; i < MAX_NUM_AMOEBA; i++)
3338 AmoebaCnt[i] = AmoebaCnt2[i] = 0;
3340 #if DEBUG_INIT_PLAYER
3343 printf("Player status at level initialization:\n");
3347 SCAN_PLAYFIELD(x, y)
3349 Feld[x][y] = level.field[x][y];
3350 MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
3351 ChangeDelay[x][y] = 0;
3352 ChangePage[x][y] = -1;
3353 CustomValue[x][y] = 0; /* initialized in InitField() */
3354 Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
3356 WasJustMoving[x][y] = 0;
3357 WasJustFalling[x][y] = 0;
3358 CheckCollision[x][y] = 0;
3359 CheckImpact[x][y] = 0;
3361 Pushed[x][y] = FALSE;
3363 ChangeCount[x][y] = 0;
3364 ChangeEvent[x][y] = -1;
3366 ExplodePhase[x][y] = 0;
3367 ExplodeDelay[x][y] = 0;
3368 ExplodeField[x][y] = EX_TYPE_NONE;
3370 RunnerVisit[x][y] = 0;
3371 PlayerVisit[x][y] = 0;
3374 GfxRandom[x][y] = INIT_GFX_RANDOM();
3375 GfxElement[x][y] = EL_UNDEFINED;
3376 GfxAction[x][y] = ACTION_DEFAULT;
3377 GfxDir[x][y] = MV_NONE;
3378 GfxRedraw[x][y] = GFX_REDRAW_NONE;
3381 SCAN_PLAYFIELD(x, y)
3383 if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
3385 if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
3387 if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
3390 InitField(x, y, TRUE);
3392 ResetGfxAnimation(x, y);
3397 for (i = 0; i < MAX_PLAYERS; i++)
3399 struct PlayerInfo *player = &stored_player[i];
3401 /* set number of special actions for bored and sleeping animation */
3402 player->num_special_action_bored =
3403 get_num_special_action(player->artwork_element,
3404 ACTION_BORING_1, ACTION_BORING_LAST);
3405 player->num_special_action_sleeping =
3406 get_num_special_action(player->artwork_element,
3407 ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
3410 game.emulation = (emulate_bd ? EMU_BOULDERDASH :
3411 emulate_sb ? EMU_SOKOBAN :
3412 emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
3414 /* initialize type of slippery elements */
3415 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3417 if (!IS_CUSTOM_ELEMENT(i))
3419 /* default: elements slip down either to the left or right randomly */
3420 element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
3422 /* SP style elements prefer to slip down on the left side */
3423 if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
3424 element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3426 /* BD style elements prefer to slip down on the left side */
3427 if (game.emulation == EMU_BOULDERDASH)
3428 element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3432 /* initialize explosion and ignition delay */
3433 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3435 if (!IS_CUSTOM_ELEMENT(i))
3438 int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
3439 game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
3440 game.emulation == EMU_SUPAPLEX ? 3 : 2);
3441 int last_phase = (num_phase + 1) * delay;
3442 int half_phase = (num_phase / 2) * delay;
3444 element_info[i].explosion_delay = last_phase - 1;
3445 element_info[i].ignition_delay = half_phase;
3447 if (i == EL_BLACK_ORB)
3448 element_info[i].ignition_delay = 1;
3452 /* correct non-moving belts to start moving left */
3453 for (i = 0; i < NUM_BELTS; i++)
3454 if (game.belt_dir[i] == MV_NONE)
3455 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
3457 #if USE_NEW_PLAYER_ASSIGNMENTS
3458 /* !!! SAME AS init.c:InitPlayerInfo() -- FIX THIS !!! */
3459 /* choose default local player */
3460 local_player = &stored_player[0];
3462 for (i = 0; i < MAX_PLAYERS; i++)
3463 stored_player[i].connected = FALSE;
3465 local_player->connected = TRUE;
3466 /* !!! SAME AS init.c:InitPlayerInfo() -- FIX THIS !!! */
3470 for (i = 0; i < MAX_PLAYERS; i++)
3471 stored_player[i].connected = tape.player_participates[i];
3473 else if (game.team_mode && !options.network)
3475 /* try to guess locally connected team mode players (needed for correct
3476 assignment of player figures from level to locally playing players) */
3478 for (i = 0; i < MAX_PLAYERS; i++)
3479 if (setup.input[i].use_joystick ||
3480 setup.input[i].key.left != KSYM_UNDEFINED)
3481 stored_player[i].connected = TRUE;
3484 #if DEBUG_INIT_PLAYER
3487 printf("Player status after level initialization:\n");
3489 for (i = 0; i < MAX_PLAYERS; i++)
3491 struct PlayerInfo *player = &stored_player[i];
3493 printf("- player %d: present == %d, connected == %d, active == %d",
3499 if (local_player == player)
3500 printf(" (local player)");
3507 #if DEBUG_INIT_PLAYER
3509 printf("Reassigning players ...\n");
3512 /* check if any connected player was not found in playfield */
3513 for (i = 0; i < MAX_PLAYERS; i++)
3515 struct PlayerInfo *player = &stored_player[i];
3517 if (player->connected && !player->present)
3519 struct PlayerInfo *field_player = NULL;
3521 #if DEBUG_INIT_PLAYER
3523 printf("- looking for field player for player %d ...\n", i + 1);
3526 /* assign first free player found that is present in the playfield */
3528 /* first try: look for unmapped playfield player that is not connected */
3529 for (j = 0; j < MAX_PLAYERS; j++)
3530 if (field_player == NULL &&
3531 stored_player[j].present &&
3532 !stored_player[j].mapped &&
3533 !stored_player[j].connected)
3534 field_player = &stored_player[j];
3536 /* second try: look for *any* unmapped playfield player */
3537 for (j = 0; j < MAX_PLAYERS; j++)
3538 if (field_player == NULL &&
3539 stored_player[j].present &&
3540 !stored_player[j].mapped)
3541 field_player = &stored_player[j];
3543 if (field_player != NULL)
3545 int jx = field_player->jx, jy = field_player->jy;
3547 #if DEBUG_INIT_PLAYER
3549 printf("- found player %d\n", field_player->index_nr + 1);
3552 player->present = FALSE;
3553 player->active = FALSE;
3555 field_player->present = TRUE;
3556 field_player->active = TRUE;
3559 player->initial_element = field_player->initial_element;
3560 player->artwork_element = field_player->artwork_element;
3562 player->block_last_field = field_player->block_last_field;
3563 player->block_delay_adjustment = field_player->block_delay_adjustment;
3566 StorePlayer[jx][jy] = field_player->element_nr;
3568 field_player->jx = field_player->last_jx = jx;
3569 field_player->jy = field_player->last_jy = jy;
3571 if (local_player == player)
3572 local_player = field_player;
3574 map_player_action[field_player->index_nr] = i;
3576 field_player->mapped = TRUE;
3578 #if DEBUG_INIT_PLAYER
3580 printf("- map_player_action[%d] == %d\n",
3581 field_player->index_nr + 1, i + 1);
3586 if (player->connected && player->present)
3587 player->mapped = TRUE;
3590 #if DEBUG_INIT_PLAYER
3593 printf("Player status after player assignment (first stage):\n");
3595 for (i = 0; i < MAX_PLAYERS; i++)
3597 struct PlayerInfo *player = &stored_player[i];
3599 printf("- player %d: present == %d, connected == %d, active == %d",
3605 if (local_player == player)
3606 printf(" (local player)");
3615 /* check if any connected player was not found in playfield */
3616 for (i = 0; i < MAX_PLAYERS; i++)
3618 struct PlayerInfo *player = &stored_player[i];
3620 if (player->connected && !player->present)
3622 for (j = 0; j < MAX_PLAYERS; j++)
3624 struct PlayerInfo *field_player = &stored_player[j];
3625 int jx = field_player->jx, jy = field_player->jy;
3627 /* assign first free player found that is present in the playfield */
3628 if (field_player->present && !field_player->connected)
3630 player->present = TRUE;
3631 player->active = TRUE;
3633 field_player->present = FALSE;
3634 field_player->active = FALSE;
3636 player->initial_element = field_player->initial_element;
3637 player->artwork_element = field_player->artwork_element;
3639 player->block_last_field = field_player->block_last_field;
3640 player->block_delay_adjustment = field_player->block_delay_adjustment;
3642 StorePlayer[jx][jy] = player->element_nr;
3644 player->jx = player->last_jx = jx;
3645 player->jy = player->last_jy = jy;
3655 printf("::: local_player->present == %d\n", local_player->present);
3660 /* when playing a tape, eliminate all players who do not participate */
3662 #if USE_NEW_PLAYER_ASSIGNMENTS
3664 if (!game.team_mode)
3666 for (i = 0; i < MAX_PLAYERS; i++)
3668 if (stored_player[i].active &&
3669 !tape.player_participates[map_player_action[i]])
3671 struct PlayerInfo *player = &stored_player[i];
3672 int jx = player->jx, jy = player->jy;
3674 #if DEBUG_INIT_PLAYER
3676 printf("Removing player %d at (%d, %d)\n", i + 1, jx, jy);
3679 player->active = FALSE;
3680 StorePlayer[jx][jy] = 0;
3681 Feld[jx][jy] = EL_EMPTY;
3688 for (i = 0; i < MAX_PLAYERS; i++)
3690 if (stored_player[i].active &&
3691 !tape.player_participates[i])
3693 struct PlayerInfo *player = &stored_player[i];
3694 int jx = player->jx, jy = player->jy;
3696 player->active = FALSE;
3697 StorePlayer[jx][jy] = 0;
3698 Feld[jx][jy] = EL_EMPTY;
3703 else if (!options.network && !game.team_mode) /* && !tape.playing */
3705 /* when in single player mode, eliminate all but the first active player */
3707 for (i = 0; i < MAX_PLAYERS; i++)
3709 if (stored_player[i].active)
3711 for (j = i + 1; j < MAX_PLAYERS; j++)
3713 if (stored_player[j].active)
3715 struct PlayerInfo *player = &stored_player[j];
3716 int jx = player->jx, jy = player->jy;
3718 player->active = FALSE;
3719 player->present = FALSE;
3721 StorePlayer[jx][jy] = 0;
3722 Feld[jx][jy] = EL_EMPTY;
3729 /* when recording the game, store which players take part in the game */
3732 #if USE_NEW_PLAYER_ASSIGNMENTS
3733 for (i = 0; i < MAX_PLAYERS; i++)
3734 if (stored_player[i].connected)
3735 tape.player_participates[i] = TRUE;
3737 for (i = 0; i < MAX_PLAYERS; i++)
3738 if (stored_player[i].active)
3739 tape.player_participates[i] = TRUE;
3743 #if DEBUG_INIT_PLAYER
3746 printf("Player status after player assignment (final stage):\n");
3748 for (i = 0; i < MAX_PLAYERS; i++)
3750 struct PlayerInfo *player = &stored_player[i];
3752 printf("- player %d: present == %d, connected == %d, active == %d",
3758 if (local_player == player)
3759 printf(" (local player)");
3766 if (BorderElement == EL_EMPTY)
3769 SBX_Right = lev_fieldx - SCR_FIELDX;
3771 SBY_Lower = lev_fieldy - SCR_FIELDY;
3776 SBX_Right = lev_fieldx - SCR_FIELDX + 1;
3778 SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
3781 if (full_lev_fieldx <= SCR_FIELDX)
3782 SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
3783 if (full_lev_fieldy <= SCR_FIELDY)
3784 SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
3786 if (EVEN(SCR_FIELDX) && full_lev_fieldx > SCR_FIELDX)
3788 if (EVEN(SCR_FIELDY) && full_lev_fieldy > SCR_FIELDY)
3791 /* if local player not found, look for custom element that might create
3792 the player (make some assumptions about the right custom element) */
3793 if (!local_player->present)
3795 int start_x = 0, start_y = 0;
3796 int found_rating = 0;
3797 int found_element = EL_UNDEFINED;
3798 int player_nr = local_player->index_nr;
3800 SCAN_PLAYFIELD(x, y)
3802 int element = Feld[x][y];
3807 if (level.use_start_element[player_nr] &&
3808 level.start_element[player_nr] == element &&
3815 found_element = element;
3818 if (!IS_CUSTOM_ELEMENT(element))
3821 if (CAN_CHANGE(element))
3823 for (i = 0; i < element_info[element].num_change_pages; i++)
3825 /* check for player created from custom element as single target */
3826 content = element_info[element].change_page[i].target_element;
3827 is_player = ELEM_IS_PLAYER(content);
3829 if (is_player && (found_rating < 3 ||
3830 (found_rating == 3 && element < found_element)))
3836 found_element = element;
3841 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
3843 /* check for player created from custom element as explosion content */
3844 content = element_info[element].content.e[xx][yy];
3845 is_player = ELEM_IS_PLAYER(content);
3847 if (is_player && (found_rating < 2 ||
3848 (found_rating == 2 && element < found_element)))
3850 start_x = x + xx - 1;
3851 start_y = y + yy - 1;
3854 found_element = element;
3857 if (!CAN_CHANGE(element))
3860 for (i = 0; i < element_info[element].num_change_pages; i++)
3862 /* check for player created from custom element as extended target */
3864 element_info[element].change_page[i].target_content.e[xx][yy];
3866 is_player = ELEM_IS_PLAYER(content);
3868 if (is_player && (found_rating < 1 ||
3869 (found_rating == 1 && element < found_element)))
3871 start_x = x + xx - 1;
3872 start_y = y + yy - 1;
3875 found_element = element;
3881 scroll_x = (start_x < SBX_Left + MIDPOSX ? SBX_Left :
3882 start_x > SBX_Right + MIDPOSX ? SBX_Right :
3885 scroll_y = (start_y < SBY_Upper + MIDPOSY ? SBY_Upper :
3886 start_y > SBY_Lower + MIDPOSY ? SBY_Lower :
3891 scroll_x = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
3892 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
3893 local_player->jx - MIDPOSX);
3895 scroll_y = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
3896 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
3897 local_player->jy - MIDPOSY);
3900 /* !!! FIX THIS (START) !!! */
3901 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
3903 InitGameEngine_EM();
3905 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
3907 InitGameEngine_SP();
3911 DrawLevel(REDRAW_FIELD);
3914 /* after drawing the level, correct some elements */
3915 if (game.timegate_time_left == 0)
3916 CloseAllOpenTimegates();
3919 /* blit playfield from scroll buffer to normal back buffer for fading in */
3920 BlitScreenToBitmap(backbuffer);
3922 redraw_mask |= REDRAW_FROM_BACKBUFFER;
3923 /* !!! FIX THIS (END) !!! */
3925 FadeIn(REDRAW_FIELD);
3928 // full screen redraw is required at this point in the following cases:
3929 // - special editor door undrawn when game was started from level editor
3930 // - drawing area (playfield) was changed and has to be removed completely
3931 redraw_mask = REDRAW_ALL;
3935 if (!game.restart_level)
3937 /* copy default game door content to main double buffer */
3939 /* !!! CHECK AGAIN !!! */
3940 SetPanelBackground();
3941 // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
3942 DrawBackground(DX, DY, DXSIZE, DYSIZE);
3945 SetPanelBackground();
3946 SetDrawBackgroundMask(REDRAW_DOOR_1);
3948 UpdateAndDisplayGameControlValues();
3950 if (!game.restart_level)
3954 game_gadget[SOUND_CTRL_ID_MUSIC]->checked = setup.sound_music;
3955 game_gadget[SOUND_CTRL_ID_LOOPS]->checked = setup.sound_loops;
3956 game_gadget[SOUND_CTRL_ID_SIMPLE]->checked = setup.sound_simple;
3960 /* copy actual game door content to door double buffer for OpenDoor() */
3961 BlitBitmap(drawto, bitmap_db_door_1, DX, DY, DXSIZE, DYSIZE, 0, 0);
3963 OpenDoor(DOOR_OPEN_ALL);
3965 PlaySound(SND_GAME_STARTING);
3967 if (setup.sound_music)
3970 KeyboardAutoRepeatOffUnlessAutoplay();
3972 #if DEBUG_INIT_PLAYER
3975 printf("Player status (final):\n");
3977 for (i = 0; i < MAX_PLAYERS; i++)
3979 struct PlayerInfo *player = &stored_player[i];
3981 printf("- player %d: present == %d, connected == %d, active == %d",
3987 if (local_player == player)
3988 printf(" (local player)");
4001 if (!game.restart_level && !tape.playing)
4003 LevelStats_incPlayed(level_nr);
4005 SaveLevelSetup_SeriesInfo();
4008 game.restart_level = FALSE;
4010 SaveEngineSnapshotToList();
4013 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y)
4015 /* this is used for non-R'n'D game engines to update certain engine values */
4017 /* needed to determine if sounds are played within the visible screen area */
4018 scroll_x = actual_scroll_x;
4019 scroll_y = actual_scroll_y;
4022 void InitMovDir(int x, int y)
4024 int i, element = Feld[x][y];
4025 static int xy[4][2] =
4032 static int direction[3][4] =
4034 { MV_RIGHT, MV_UP, MV_LEFT, MV_DOWN },
4035 { MV_LEFT, MV_DOWN, MV_RIGHT, MV_UP },
4036 { MV_LEFT, MV_RIGHT, MV_UP, MV_DOWN }
4045 Feld[x][y] = EL_BUG;
4046 MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
4049 case EL_SPACESHIP_RIGHT:
4050 case EL_SPACESHIP_UP:
4051 case EL_SPACESHIP_LEFT:
4052 case EL_SPACESHIP_DOWN:
4053 Feld[x][y] = EL_SPACESHIP;
4054 MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
4057 case EL_BD_BUTTERFLY_RIGHT:
4058 case EL_BD_BUTTERFLY_UP:
4059 case EL_BD_BUTTERFLY_LEFT:
4060 case EL_BD_BUTTERFLY_DOWN:
4061 Feld[x][y] = EL_BD_BUTTERFLY;
4062 MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
4065 case EL_BD_FIREFLY_RIGHT:
4066 case EL_BD_FIREFLY_UP:
4067 case EL_BD_FIREFLY_LEFT:
4068 case EL_BD_FIREFLY_DOWN:
4069 Feld[x][y] = EL_BD_FIREFLY;
4070 MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
4073 case EL_PACMAN_RIGHT:
4075 case EL_PACMAN_LEFT:
4076 case EL_PACMAN_DOWN:
4077 Feld[x][y] = EL_PACMAN;
4078 MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
4081 case EL_YAMYAM_LEFT:
4082 case EL_YAMYAM_RIGHT:
4084 case EL_YAMYAM_DOWN:
4085 Feld[x][y] = EL_YAMYAM;
4086 MovDir[x][y] = direction[2][element - EL_YAMYAM_LEFT];
4089 case EL_SP_SNIKSNAK:
4090 MovDir[x][y] = MV_UP;
4093 case EL_SP_ELECTRON:
4094 MovDir[x][y] = MV_LEFT;
4101 Feld[x][y] = EL_MOLE;
4102 MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
4106 if (IS_CUSTOM_ELEMENT(element))
4108 struct ElementInfo *ei = &element_info[element];
4109 int move_direction_initial = ei->move_direction_initial;
4110 int move_pattern = ei->move_pattern;
4112 if (move_direction_initial == MV_START_PREVIOUS)
4114 if (MovDir[x][y] != MV_NONE)
4117 move_direction_initial = MV_START_AUTOMATIC;
4120 if (move_direction_initial == MV_START_RANDOM)
4121 MovDir[x][y] = 1 << RND(4);
4122 else if (move_direction_initial & MV_ANY_DIRECTION)
4123 MovDir[x][y] = move_direction_initial;
4124 else if (move_pattern == MV_ALL_DIRECTIONS ||
4125 move_pattern == MV_TURNING_LEFT ||
4126 move_pattern == MV_TURNING_RIGHT ||
4127 move_pattern == MV_TURNING_LEFT_RIGHT ||
4128 move_pattern == MV_TURNING_RIGHT_LEFT ||
4129 move_pattern == MV_TURNING_RANDOM)
4130 MovDir[x][y] = 1 << RND(4);
4131 else if (move_pattern == MV_HORIZONTAL)
4132 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
4133 else if (move_pattern == MV_VERTICAL)
4134 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
4135 else if (move_pattern & MV_ANY_DIRECTION)
4136 MovDir[x][y] = element_info[element].move_pattern;
4137 else if (move_pattern == MV_ALONG_LEFT_SIDE ||
4138 move_pattern == MV_ALONG_RIGHT_SIDE)
4140 /* use random direction as default start direction */
4141 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
4142 MovDir[x][y] = 1 << RND(4);
4144 for (i = 0; i < NUM_DIRECTIONS; i++)
4146 int x1 = x + xy[i][0];
4147 int y1 = y + xy[i][1];
4149 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4151 if (move_pattern == MV_ALONG_RIGHT_SIDE)
4152 MovDir[x][y] = direction[0][i];
4154 MovDir[x][y] = direction[1][i];
4163 MovDir[x][y] = 1 << RND(4);
4165 if (element != EL_BUG &&
4166 element != EL_SPACESHIP &&
4167 element != EL_BD_BUTTERFLY &&
4168 element != EL_BD_FIREFLY)
4171 for (i = 0; i < NUM_DIRECTIONS; i++)
4173 int x1 = x + xy[i][0];
4174 int y1 = y + xy[i][1];
4176 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4178 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
4180 MovDir[x][y] = direction[0][i];
4183 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
4184 element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4186 MovDir[x][y] = direction[1][i];
4195 GfxDir[x][y] = MovDir[x][y];
4198 void InitAmoebaNr(int x, int y)
4201 int group_nr = AmoebeNachbarNr(x, y);
4205 for (i = 1; i < MAX_NUM_AMOEBA; i++)
4207 if (AmoebaCnt[i] == 0)
4215 AmoebaNr[x][y] = group_nr;
4216 AmoebaCnt[group_nr]++;
4217 AmoebaCnt2[group_nr]++;
4220 static void PlayerWins(struct PlayerInfo *player)
4222 player->LevelSolved = TRUE;
4223 player->GameOver = TRUE;
4225 player->score_final = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
4226 level.native_em_level->lev->score : player->score);
4228 player->LevelSolved_CountingTime = (game.no_time_limit ? TimePlayed :
4230 player->LevelSolved_CountingScore = player->score_final;
4235 static int time, time_final;
4236 static int score, score_final;
4237 static int game_over_delay_1 = 0;
4238 static int game_over_delay_2 = 0;
4239 int game_over_delay_value_1 = 50;
4240 int game_over_delay_value_2 = 50;
4242 if (!local_player->LevelSolved_GameWon)
4246 /* do not start end game actions before the player stops moving (to exit) */
4247 if (local_player->MovPos)
4250 local_player->LevelSolved_GameWon = TRUE;
4251 local_player->LevelSolved_SaveTape = tape.recording;
4252 local_player->LevelSolved_SaveScore = !tape.playing;
4256 LevelStats_incSolved(level_nr);
4258 SaveLevelSetup_SeriesInfo();
4261 if (tape.auto_play) /* tape might already be stopped here */
4262 tape.auto_play_level_solved = TRUE;
4266 game_over_delay_1 = game_over_delay_value_1;
4267 game_over_delay_2 = game_over_delay_value_2;
4269 time = time_final = (game.no_time_limit ? TimePlayed : TimeLeft);
4270 score = score_final = local_player->score_final;
4275 score_final += TimeLeft * level.score[SC_TIME_BONUS];
4277 else if (game.no_time_limit && TimePlayed < 999)
4280 score_final += (999 - TimePlayed) * level.score[SC_TIME_BONUS];
4283 local_player->score_final = score_final;
4285 if (level_editor_test_game)
4288 score = score_final;
4290 local_player->LevelSolved_CountingTime = time;
4291 local_player->LevelSolved_CountingScore = score;
4293 game_panel_controls[GAME_PANEL_TIME].value = time;
4294 game_panel_controls[GAME_PANEL_SCORE].value = score;
4296 DisplayGameControlValues();
4299 if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
4301 if (ExitX >= 0 && ExitY >= 0) /* local player has left the level */
4303 /* close exit door after last player */
4304 if ((AllPlayersGone &&
4305 (Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
4306 Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN ||
4307 Feld[ExitX][ExitY] == EL_STEEL_EXIT_OPEN)) ||
4308 Feld[ExitX][ExitY] == EL_EM_EXIT_OPEN ||
4309 Feld[ExitX][ExitY] == EL_EM_STEEL_EXIT_OPEN)
4311 int element = Feld[ExitX][ExitY];
4313 Feld[ExitX][ExitY] =
4314 (element == EL_EXIT_OPEN ? EL_EXIT_CLOSING :
4315 element == EL_EM_EXIT_OPEN ? EL_EM_EXIT_CLOSING :
4316 element == EL_SP_EXIT_OPEN ? EL_SP_EXIT_CLOSING:
4317 element == EL_STEEL_EXIT_OPEN ? EL_STEEL_EXIT_CLOSING:
4318 EL_EM_STEEL_EXIT_CLOSING);
4320 PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING);
4323 /* player disappears */
4324 DrawLevelField(ExitX, ExitY);
4327 for (i = 0; i < MAX_PLAYERS; i++)
4329 struct PlayerInfo *player = &stored_player[i];
4331 if (player->present)
4333 RemovePlayer(player);
4335 /* player disappears */
4336 DrawLevelField(player->jx, player->jy);
4341 PlaySound(SND_GAME_WINNING);
4344 if (game_over_delay_1 > 0)
4346 game_over_delay_1--;
4351 if (time != time_final)
4353 int time_to_go = ABS(time_final - time);
4354 int time_count_dir = (time < time_final ? +1 : -1);
4355 int time_count_steps = (time_to_go > 100 && time_to_go % 10 == 0 ? 10 : 1);
4357 time += time_count_steps * time_count_dir;
4358 score += time_count_steps * level.score[SC_TIME_BONUS];
4360 local_player->LevelSolved_CountingTime = time;
4361 local_player->LevelSolved_CountingScore = score;
4363 game_panel_controls[GAME_PANEL_TIME].value = time;
4364 game_panel_controls[GAME_PANEL_SCORE].value = score;
4366 DisplayGameControlValues();
4368 if (time == time_final)
4369 StopSound(SND_GAME_LEVELTIME_BONUS);
4370 else if (setup.sound_loops)
4371 PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4373 PlaySound(SND_GAME_LEVELTIME_BONUS);
4378 local_player->LevelSolved_PanelOff = TRUE;
4380 if (game_over_delay_2 > 0)
4382 game_over_delay_2--;
4393 boolean raise_level = FALSE;
4395 local_player->LevelSolved_GameEnd = TRUE;
4397 CloseDoor(DOOR_CLOSE_1);
4399 if (local_player->LevelSolved_SaveTape)
4401 SaveTapeChecked(tape.level_nr); /* ask to save tape */
4404 if (level_editor_test_game)
4406 game_status = GAME_MODE_MAIN;
4408 DrawAndFadeInMainMenu(REDRAW_FIELD);
4413 if (!local_player->LevelSolved_SaveScore)
4415 FadeOut(REDRAW_FIELD);
4417 game_status = GAME_MODE_MAIN;
4419 DrawAndFadeInMainMenu(REDRAW_FIELD);
4424 if (level_nr == leveldir_current->handicap_level)
4426 leveldir_current->handicap_level++;
4428 SaveLevelSetup_SeriesInfo();
4431 if (level_nr < leveldir_current->last_level)
4432 raise_level = TRUE; /* advance to next level */
4434 if ((hi_pos = NewHiScore()) >= 0)
4436 game_status = GAME_MODE_SCORES;
4438 DrawHallOfFame(hi_pos);
4448 FadeOut(REDRAW_FIELD);
4450 game_status = GAME_MODE_MAIN;
4458 DrawAndFadeInMainMenu(REDRAW_FIELD);
4467 LoadScore(level_nr);
4469 if (strEqual(setup.player_name, EMPTY_PLAYER_NAME) ||
4470 local_player->score_final < highscore[MAX_SCORE_ENTRIES - 1].Score)
4473 for (k = 0; k < MAX_SCORE_ENTRIES; k++)
4475 if (local_player->score_final > highscore[k].Score)
4477 /* player has made it to the hall of fame */
4479 if (k < MAX_SCORE_ENTRIES - 1)
4481 int m = MAX_SCORE_ENTRIES - 1;
4484 for (l = k; l < MAX_SCORE_ENTRIES; l++)
4485 if (strEqual(setup.player_name, highscore[l].Name))
4487 if (m == k) /* player's new highscore overwrites his old one */
4491 for (l = m; l > k; l--)
4493 strcpy(highscore[l].Name, highscore[l - 1].Name);
4494 highscore[l].Score = highscore[l - 1].Score;
4501 strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
4502 highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
4503 highscore[k].Score = local_player->score_final;
4509 else if (!strncmp(setup.player_name, highscore[k].Name,
4510 MAX_PLAYER_NAME_LEN))
4511 break; /* player already there with a higher score */
4517 SaveScore(level_nr);
4522 inline static int getElementMoveStepsizeExt(int x, int y, int direction)
4524 int element = Feld[x][y];
4525 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
4526 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
4527 int horiz_move = (dx != 0);
4528 int sign = (horiz_move ? dx : dy);
4529 int step = sign * element_info[element].move_stepsize;
4531 /* special values for move stepsize for spring and things on conveyor belt */
4534 if (CAN_FALL(element) &&
4535 y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
4536 step = sign * MOVE_STEPSIZE_NORMAL / 2;
4537 else if (element == EL_SPRING)
4538 step = sign * MOVE_STEPSIZE_NORMAL * 2;
4544 inline static int getElementMoveStepsize(int x, int y)
4546 return getElementMoveStepsizeExt(x, y, MovDir[x][y]);
4549 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
4551 if (player->GfxAction != action || player->GfxDir != dir)
4553 player->GfxAction = action;
4554 player->GfxDir = dir;
4556 player->StepFrame = 0;
4560 static void ResetGfxFrame(int x, int y, boolean redraw)
4562 int element = Feld[x][y];
4563 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
4564 int last_gfx_frame = GfxFrame[x][y];
4566 if (graphic_info[graphic].anim_global_sync)
4567 GfxFrame[x][y] = FrameCounter;
4568 else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
4569 GfxFrame[x][y] = CustomValue[x][y];
4570 else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
4571 GfxFrame[x][y] = element_info[element].collect_score;
4572 else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
4573 GfxFrame[x][y] = ChangeDelay[x][y];
4575 if (redraw && GfxFrame[x][y] != last_gfx_frame)
4576 DrawLevelGraphicAnimation(x, y, graphic);
4579 static void ResetGfxAnimation(int x, int y)
4581 GfxAction[x][y] = ACTION_DEFAULT;
4582 GfxDir[x][y] = MovDir[x][y];
4585 ResetGfxFrame(x, y, FALSE);
4588 static void ResetRandomAnimationValue(int x, int y)
4590 GfxRandom[x][y] = INIT_GFX_RANDOM();
4593 void InitMovingField(int x, int y, int direction)
4595 int element = Feld[x][y];
4596 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
4597 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
4600 boolean is_moving_before, is_moving_after;
4602 /* check if element was/is moving or being moved before/after mode change */
4603 is_moving_before = (WasJustMoving[x][y] != 0);
4604 is_moving_after = (getElementMoveStepsizeExt(x, y, direction) != 0);
4606 /* reset animation only for moving elements which change direction of moving
4607 or which just started or stopped moving
4608 (else CEs with property "can move" / "not moving" are reset each frame) */
4609 if (is_moving_before != is_moving_after ||
4610 direction != MovDir[x][y])
4611 ResetGfxAnimation(x, y);
4613 MovDir[x][y] = direction;
4614 GfxDir[x][y] = direction;
4616 GfxAction[x][y] = (!is_moving_after ? ACTION_WAITING :
4617 direction == MV_DOWN && CAN_FALL(element) ?
4618 ACTION_FALLING : ACTION_MOVING);
4620 /* this is needed for CEs with property "can move" / "not moving" */
4622 if (is_moving_after)
4624 if (Feld[newx][newy] == EL_EMPTY)
4625 Feld[newx][newy] = EL_BLOCKED;
4627 MovDir[newx][newy] = MovDir[x][y];
4629 CustomValue[newx][newy] = CustomValue[x][y];
4631 GfxFrame[newx][newy] = GfxFrame[x][y];
4632 GfxRandom[newx][newy] = GfxRandom[x][y];
4633 GfxAction[newx][newy] = GfxAction[x][y];
4634 GfxDir[newx][newy] = GfxDir[x][y];
4638 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
4640 int direction = MovDir[x][y];
4641 int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0);
4642 int newy = y + (direction & MV_UP ? -1 : direction & MV_DOWN ? +1 : 0);
4648 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
4650 int oldx = x, oldy = y;
4651 int direction = MovDir[x][y];
4653 if (direction == MV_LEFT)
4655 else if (direction == MV_RIGHT)
4657 else if (direction == MV_UP)
4659 else if (direction == MV_DOWN)
4662 *comes_from_x = oldx;
4663 *comes_from_y = oldy;
4666 int MovingOrBlocked2Element(int x, int y)
4668 int element = Feld[x][y];
4670 if (element == EL_BLOCKED)
4674 Blocked2Moving(x, y, &oldx, &oldy);
4675 return Feld[oldx][oldy];
4681 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
4683 /* like MovingOrBlocked2Element(), but if element is moving
4684 and (x,y) is the field the moving element is just leaving,
4685 return EL_BLOCKED instead of the element value */
4686 int element = Feld[x][y];
4688 if (IS_MOVING(x, y))
4690 if (element == EL_BLOCKED)
4694 Blocked2Moving(x, y, &oldx, &oldy);
4695 return Feld[oldx][oldy];
4704 static void RemoveField(int x, int y)
4706 Feld[x][y] = EL_EMPTY;
4712 CustomValue[x][y] = 0;
4715 ChangeDelay[x][y] = 0;
4716 ChangePage[x][y] = -1;
4717 Pushed[x][y] = FALSE;
4719 GfxElement[x][y] = EL_UNDEFINED;
4720 GfxAction[x][y] = ACTION_DEFAULT;
4721 GfxDir[x][y] = MV_NONE;
4724 void RemoveMovingField(int x, int y)
4726 int oldx = x, oldy = y, newx = x, newy = y;
4727 int element = Feld[x][y];
4728 int next_element = EL_UNDEFINED;
4730 if (element != EL_BLOCKED && !IS_MOVING(x, y))
4733 if (IS_MOVING(x, y))
4735 Moving2Blocked(x, y, &newx, &newy);
4737 if (Feld[newx][newy] != EL_BLOCKED)
4739 /* element is moving, but target field is not free (blocked), but
4740 already occupied by something different (example: acid pool);
4741 in this case, only remove the moving field, but not the target */
4743 RemoveField(oldx, oldy);
4745 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
4747 TEST_DrawLevelField(oldx, oldy);
4752 else if (element == EL_BLOCKED)
4754 Blocked2Moving(x, y, &oldx, &oldy);
4755 if (!IS_MOVING(oldx, oldy))
4759 if (element == EL_BLOCKED &&
4760 (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
4761 Feld[oldx][oldy] == EL_QUICKSAND_FAST_EMPTYING ||
4762 Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
4763 Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
4764 Feld[oldx][oldy] == EL_DC_MAGIC_WALL_EMPTYING ||
4765 Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
4766 next_element = get_next_element(Feld[oldx][oldy]);
4768 RemoveField(oldx, oldy);
4769 RemoveField(newx, newy);
4771 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
4773 if (next_element != EL_UNDEFINED)
4774 Feld[oldx][oldy] = next_element;
4776 TEST_DrawLevelField(oldx, oldy);
4777 TEST_DrawLevelField(newx, newy);
4780 void DrawDynamite(int x, int y)
4782 int sx = SCREENX(x), sy = SCREENY(y);
4783 int graphic = el2img(Feld[x][y]);
4786 if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
4789 if (IS_WALKABLE_INSIDE(Back[x][y]))
4793 DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
4794 else if (Store[x][y])
4795 DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
4797 frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
4799 if (Back[x][y] || Store[x][y])
4800 DrawGraphicThruMask(sx, sy, graphic, frame);
4802 DrawGraphic(sx, sy, graphic, frame);
4805 void CheckDynamite(int x, int y)
4807 if (MovDelay[x][y] != 0) /* dynamite is still waiting to explode */
4811 if (MovDelay[x][y] != 0)
4814 PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
4820 StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
4825 static void setMinimalPlayerBoundaries(int *sx1, int *sy1, int *sx2, int *sy2)
4827 boolean num_checked_players = 0;
4830 for (i = 0; i < MAX_PLAYERS; i++)
4832 if (stored_player[i].active)
4834 int sx = stored_player[i].jx;
4835 int sy = stored_player[i].jy;
4837 if (num_checked_players == 0)
4844 *sx1 = MIN(*sx1, sx);
4845 *sy1 = MIN(*sy1, sy);
4846 *sx2 = MAX(*sx2, sx);
4847 *sy2 = MAX(*sy2, sy);
4850 num_checked_players++;
4855 static boolean checkIfAllPlayersFitToScreen_RND()
4857 int sx1 = 0, sy1 = 0, sx2 = 0, sy2 = 0;
4859 setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
4861 return (sx2 - sx1 < SCR_FIELDX &&
4862 sy2 - sy1 < SCR_FIELDY);
4865 static void setScreenCenteredToAllPlayers(int *sx, int *sy)
4867 int sx1 = scroll_x, sy1 = scroll_y, sx2 = scroll_x, sy2 = scroll_y;
4869 setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
4871 *sx = (sx1 + sx2) / 2;
4872 *sy = (sy1 + sy2) / 2;
4875 void DrawRelocateScreen(int old_x, int old_y, int x, int y, int move_dir,
4876 boolean center_screen, boolean quick_relocation)
4878 boolean ffwd_delay = (tape.playing && tape.fast_forward);
4879 boolean no_delay = (tape.warp_forward);
4880 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
4881 int wait_delay_value = (no_delay ? 0 : frame_delay_value);
4883 if (quick_relocation)
4885 if (!IN_VIS_FIELD(SCREENX(x), SCREENY(y)) || center_screen)
4887 if (!level.shifted_relocation || center_screen)
4889 /* quick relocation (without scrolling), with centering of screen */
4891 scroll_x = (x < SBX_Left + MIDPOSX ? SBX_Left :
4892 x > SBX_Right + MIDPOSX ? SBX_Right :
4895 scroll_y = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
4896 y > SBY_Lower + MIDPOSY ? SBY_Lower :
4901 /* quick relocation (without scrolling), but do not center screen */
4903 int center_scroll_x = (old_x < SBX_Left + MIDPOSX ? SBX_Left :
4904 old_x > SBX_Right + MIDPOSX ? SBX_Right :
4907 int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
4908 old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
4911 int offset_x = x + (scroll_x - center_scroll_x);
4912 int offset_y = y + (scroll_y - center_scroll_y);
4914 scroll_x = (offset_x < SBX_Left + MIDPOSX ? SBX_Left :
4915 offset_x > SBX_Right + MIDPOSX ? SBX_Right :
4916 offset_x - MIDPOSX);
4918 scroll_y = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
4919 offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
4920 offset_y - MIDPOSY);
4925 if (!level.shifted_relocation || center_screen)
4927 /* quick relocation (without scrolling), with centering of screen */
4929 scroll_x = (x < SBX_Left + MIDPOSX ? SBX_Left :
4930 x > SBX_Right + MIDPOSX ? SBX_Right :
4933 scroll_y = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
4934 y > SBY_Lower + MIDPOSY ? SBY_Lower :
4939 /* quick relocation (without scrolling), but do not center screen */
4941 int center_scroll_x = (old_x < SBX_Left + MIDPOSX ? SBX_Left :
4942 old_x > SBX_Right + MIDPOSX ? SBX_Right :
4945 int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
4946 old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
4949 int offset_x = x + (scroll_x - center_scroll_x);
4950 int offset_y = y + (scroll_y - center_scroll_y);
4952 scroll_x = (offset_x < SBX_Left + MIDPOSX ? SBX_Left :
4953 offset_x > SBX_Right + MIDPOSX ? SBX_Right :
4954 offset_x - MIDPOSX);
4956 scroll_y = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
4957 offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
4958 offset_y - MIDPOSY);
4962 RedrawPlayfield(TRUE, 0,0,0,0);
4966 int scroll_xx, scroll_yy;
4968 if (!level.shifted_relocation || center_screen)
4970 /* visible relocation (with scrolling), with centering of screen */
4972 scroll_xx = (x < SBX_Left + MIDPOSX ? SBX_Left :
4973 x > SBX_Right + MIDPOSX ? SBX_Right :
4976 scroll_yy = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
4977 y > SBY_Lower + MIDPOSY ? SBY_Lower :
4982 /* visible relocation (with scrolling), but do not center screen */
4984 int center_scroll_x = (old_x < SBX_Left + MIDPOSX ? SBX_Left :
4985 old_x > SBX_Right + MIDPOSX ? SBX_Right :
4988 int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
4989 old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
4992 int offset_x = x + (scroll_x - center_scroll_x);
4993 int offset_y = y + (scroll_y - center_scroll_y);
4995 scroll_xx = (offset_x < SBX_Left + MIDPOSX ? SBX_Left :
4996 offset_x > SBX_Right + MIDPOSX ? SBX_Right :
4997 offset_x - MIDPOSX);
4999 scroll_yy = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5000 offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5001 offset_y - MIDPOSY);
5005 ScrollScreen(NULL, SCROLL_GO_ON); /* scroll last frame to full tile */
5007 while (scroll_x != scroll_xx || scroll_y != scroll_yy)
5010 int fx = FX, fy = FY;
5012 dx = (scroll_xx < scroll_x ? +1 : scroll_xx > scroll_x ? -1 : 0);
5013 dy = (scroll_yy < scroll_y ? +1 : scroll_yy > scroll_y ? -1 : 0);
5015 if (dx == 0 && dy == 0) /* no scrolling needed at all */
5021 fx += dx * TILEX / 2;
5022 fy += dy * TILEY / 2;
5024 ScrollLevel(dx, dy);
5027 /* scroll in two steps of half tile size to make things smoother */
5028 BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
5029 Delay(wait_delay_value);
5031 /* scroll second step to align at full tile size */
5033 Delay(wait_delay_value);
5038 Delay(wait_delay_value);
5042 void RelocatePlayer(int jx, int jy, int el_player_raw)
5044 int el_player = GET_PLAYER_ELEMENT(el_player_raw);
5045 int player_nr = GET_PLAYER_NR(el_player);
5046 struct PlayerInfo *player = &stored_player[player_nr];
5047 boolean ffwd_delay = (tape.playing && tape.fast_forward);
5048 boolean no_delay = (tape.warp_forward);
5049 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5050 int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5051 int old_jx = player->jx;
5052 int old_jy = player->jy;
5053 int old_element = Feld[old_jx][old_jy];
5054 int element = Feld[jx][jy];
5055 boolean player_relocated = (old_jx != jx || old_jy != jy);
5057 int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
5058 int move_dir_vert = (jy < old_jy ? MV_UP : jy > old_jy ? MV_DOWN : 0);
5059 int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
5060 int enter_side_vert = MV_DIR_OPPOSITE(move_dir_vert);
5061 int leave_side_horiz = move_dir_horiz;
5062 int leave_side_vert = move_dir_vert;
5063 int enter_side = enter_side_horiz | enter_side_vert;
5064 int leave_side = leave_side_horiz | leave_side_vert;
5066 if (player->GameOver) /* do not reanimate dead player */
5069 if (!player_relocated) /* no need to relocate the player */
5072 if (IS_PLAYER(jx, jy)) /* player already placed at new position */
5074 RemoveField(jx, jy); /* temporarily remove newly placed player */
5075 DrawLevelField(jx, jy);
5078 if (player->present)
5080 while (player->MovPos)
5082 ScrollPlayer(player, SCROLL_GO_ON);
5083 ScrollScreen(NULL, SCROLL_GO_ON);
5085 AdvanceFrameAndPlayerCounters(player->index_nr);
5090 Delay(wait_delay_value);
5093 DrawPlayer(player); /* needed here only to cleanup last field */
5094 DrawLevelField(player->jx, player->jy); /* remove player graphic */
5096 player->is_moving = FALSE;
5099 if (IS_CUSTOM_ELEMENT(old_element))
5100 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
5102 player->index_bit, leave_side);
5104 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
5106 player->index_bit, leave_side);
5108 Feld[jx][jy] = el_player;
5109 InitPlayerField(jx, jy, el_player, TRUE);
5111 /* "InitPlayerField()" above sets Feld[jx][jy] to EL_EMPTY, but it may be
5112 possible that the relocation target field did not contain a player element,
5113 but a walkable element, to which the new player was relocated -- in this
5114 case, restore that (already initialized!) element on the player field */
5115 if (!ELEM_IS_PLAYER(element)) /* player may be set on walkable element */
5117 Feld[jx][jy] = element; /* restore previously existing element */
5120 /* only visually relocate centered player */
5121 DrawRelocateScreen(old_jx, old_jy, player->jx, player->jy, player->MovDir,
5122 FALSE, level.instant_relocation);
5124 TestIfPlayerTouchesBadThing(jx, jy);
5125 TestIfPlayerTouchesCustomElement(jx, jy);
5127 if (IS_CUSTOM_ELEMENT(element))
5128 CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
5129 player->index_bit, enter_side);
5131 CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
5132 player->index_bit, enter_side);
5134 if (player->is_switching)
5136 /* ensure that relocation while still switching an element does not cause
5137 a new element to be treated as also switched directly after relocation
5138 (this is important for teleporter switches that teleport the player to
5139 a place where another teleporter switch is in the same direction, which
5140 would then incorrectly be treated as immediately switched before the
5141 direction key that caused the switch was released) */
5143 player->switch_x += jx - old_jx;
5144 player->switch_y += jy - old_jy;
5148 void Explode(int ex, int ey, int phase, int mode)
5154 /* !!! eliminate this variable !!! */
5155 int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
5157 if (game.explosions_delayed)
5159 ExplodeField[ex][ey] = mode;
5163 if (phase == EX_PHASE_START) /* initialize 'Store[][]' field */
5165 int center_element = Feld[ex][ey];
5166 int artwork_element, explosion_element; /* set these values later */
5168 /* remove things displayed in background while burning dynamite */
5169 if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
5172 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
5174 /* put moving element to center field (and let it explode there) */
5175 center_element = MovingOrBlocked2Element(ex, ey);
5176 RemoveMovingField(ex, ey);
5177 Feld[ex][ey] = center_element;
5180 /* now "center_element" is finally determined -- set related values now */
5181 artwork_element = center_element; /* for custom player artwork */
5182 explosion_element = center_element; /* for custom player artwork */
5184 if (IS_PLAYER(ex, ey))
5186 int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
5188 artwork_element = stored_player[player_nr].artwork_element;
5190 if (level.use_explosion_element[player_nr])
5192 explosion_element = level.explosion_element[player_nr];
5193 artwork_element = explosion_element;
5197 if (mode == EX_TYPE_NORMAL ||
5198 mode == EX_TYPE_CENTER ||
5199 mode == EX_TYPE_CROSS)
5200 PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
5202 last_phase = element_info[explosion_element].explosion_delay + 1;
5204 for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
5206 int xx = x - ex + 1;
5207 int yy = y - ey + 1;
5210 if (!IN_LEV_FIELD(x, y) ||
5211 (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
5212 (mode == EX_TYPE_CROSS && (x != ex && y != ey)))
5215 element = Feld[x][y];
5217 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
5219 element = MovingOrBlocked2Element(x, y);
5221 if (!IS_EXPLOSION_PROOF(element))
5222 RemoveMovingField(x, y);
5225 /* indestructible elements can only explode in center (but not flames) */
5226 if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
5227 mode == EX_TYPE_BORDER)) ||
5228 element == EL_FLAMES)
5231 /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
5232 behaviour, for example when touching a yamyam that explodes to rocks
5233 with active deadly shield, a rock is created under the player !!! */
5234 /* (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8) */
5236 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
5237 (game.engine_version < VERSION_IDENT(3,1,0,0) ||
5238 (x == ex && y == ey && mode != EX_TYPE_BORDER)))
5240 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
5243 if (IS_ACTIVE_BOMB(element))
5245 /* re-activate things under the bomb like gate or penguin */
5246 Feld[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
5253 /* save walkable background elements while explosion on same tile */
5254 if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
5255 (x != ex || y != ey || mode == EX_TYPE_BORDER))
5256 Back[x][y] = element;
5258 /* ignite explodable elements reached by other explosion */
5259 if (element == EL_EXPLOSION)
5260 element = Store2[x][y];
5262 if (AmoebaNr[x][y] &&
5263 (element == EL_AMOEBA_FULL ||
5264 element == EL_BD_AMOEBA ||
5265 element == EL_AMOEBA_GROWING))
5267 AmoebaCnt[AmoebaNr[x][y]]--;
5268 AmoebaCnt2[AmoebaNr[x][y]]--;
5273 if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
5275 int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
5277 Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
5279 if (PLAYERINFO(ex, ey)->use_murphy)
5280 Store[x][y] = EL_EMPTY;
5283 /* !!! check this case -- currently needed for rnd_rado_negundo_v,
5284 !!! levels 015 018 019 020 021 022 023 026 027 028 !!! */
5285 else if (ELEM_IS_PLAYER(center_element))
5286 Store[x][y] = EL_EMPTY;
5287 else if (center_element == EL_YAMYAM)
5288 Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
5289 else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
5290 Store[x][y] = element_info[center_element].content.e[xx][yy];
5292 /* needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
5293 (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
5294 otherwise) -- FIX THIS !!! */
5295 else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
5296 Store[x][y] = element_info[element].content.e[1][1];
5298 else if (!CAN_EXPLODE(element))
5299 Store[x][y] = element_info[element].content.e[1][1];
5302 Store[x][y] = EL_EMPTY;
5304 if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
5305 center_element == EL_AMOEBA_TO_DIAMOND)
5306 Store2[x][y] = element;
5308 Feld[x][y] = EL_EXPLOSION;
5309 GfxElement[x][y] = artwork_element;
5311 ExplodePhase[x][y] = 1;
5312 ExplodeDelay[x][y] = last_phase;
5317 if (center_element == EL_YAMYAM)
5318 game.yamyam_content_nr =
5319 (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
5331 GfxFrame[x][y] = 0; /* restart explosion animation */
5333 last_phase = ExplodeDelay[x][y];
5335 ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
5337 /* this can happen if the player leaves an explosion just in time */
5338 if (GfxElement[x][y] == EL_UNDEFINED)
5339 GfxElement[x][y] = EL_EMPTY;
5341 border_element = Store2[x][y];
5342 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5343 border_element = StorePlayer[x][y];
5345 if (phase == element_info[border_element].ignition_delay ||
5346 phase == last_phase)
5348 boolean border_explosion = FALSE;
5350 if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
5351 !PLAYER_EXPLOSION_PROTECTED(x, y))
5353 KillPlayerUnlessExplosionProtected(x, y);
5354 border_explosion = TRUE;
5356 else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
5358 Feld[x][y] = Store2[x][y];
5361 border_explosion = TRUE;
5363 else if (border_element == EL_AMOEBA_TO_DIAMOND)
5365 AmoebeUmwandeln(x, y);
5367 border_explosion = TRUE;
5370 /* if an element just explodes due to another explosion (chain-reaction),
5371 do not immediately end the new explosion when it was the last frame of
5372 the explosion (as it would be done in the following "if"-statement!) */
5373 if (border_explosion && phase == last_phase)
5377 if (phase == last_phase)
5381 element = Feld[x][y] = Store[x][y];
5382 Store[x][y] = Store2[x][y] = 0;
5383 GfxElement[x][y] = EL_UNDEFINED;
5385 /* player can escape from explosions and might therefore be still alive */
5386 if (element >= EL_PLAYER_IS_EXPLODING_1 &&
5387 element <= EL_PLAYER_IS_EXPLODING_4)
5389 int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
5390 int explosion_element = EL_PLAYER_1 + player_nr;
5391 int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
5392 int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
5394 if (level.use_explosion_element[player_nr])
5395 explosion_element = level.explosion_element[player_nr];
5397 Feld[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
5398 element_info[explosion_element].content.e[xx][yy]);
5401 /* restore probably existing indestructible background element */
5402 if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
5403 element = Feld[x][y] = Back[x][y];
5406 MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
5407 GfxDir[x][y] = MV_NONE;
5408 ChangeDelay[x][y] = 0;
5409 ChangePage[x][y] = -1;
5411 CustomValue[x][y] = 0;
5413 InitField_WithBug2(x, y, FALSE);
5415 TEST_DrawLevelField(x, y);
5417 TestIfElementTouchesCustomElement(x, y);
5419 if (GFX_CRUMBLED(element))
5420 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
5422 if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
5423 StorePlayer[x][y] = 0;
5425 if (ELEM_IS_PLAYER(element))
5426 RelocatePlayer(x, y, element);
5428 else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
5430 int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
5431 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5434 TEST_DrawLevelFieldCrumbled(x, y);
5436 if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
5438 DrawLevelElement(x, y, Back[x][y]);
5439 DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
5441 else if (IS_WALKABLE_UNDER(Back[x][y]))
5443 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5444 DrawLevelElementThruMask(x, y, Back[x][y]);
5446 else if (!IS_WALKABLE_INSIDE(Back[x][y]))
5447 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5451 void DynaExplode(int ex, int ey)
5454 int dynabomb_element = Feld[ex][ey];
5455 int dynabomb_size = 1;
5456 boolean dynabomb_xl = FALSE;
5457 struct PlayerInfo *player;
5458 static int xy[4][2] =
5466 if (IS_ACTIVE_BOMB(dynabomb_element))
5468 player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
5469 dynabomb_size = player->dynabomb_size;
5470 dynabomb_xl = player->dynabomb_xl;
5471 player->dynabombs_left++;
5474 Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
5476 for (i = 0; i < NUM_DIRECTIONS; i++)
5478 for (j = 1; j <= dynabomb_size; j++)
5480 int x = ex + j * xy[i][0];
5481 int y = ey + j * xy[i][1];
5484 if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
5487 element = Feld[x][y];
5489 /* do not restart explosions of fields with active bombs */
5490 if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
5493 Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
5495 if (element != EL_EMPTY && element != EL_EXPLOSION &&
5496 !IS_DIGGABLE(element) && !dynabomb_xl)
5502 void Bang(int x, int y)
5504 int element = MovingOrBlocked2Element(x, y);
5505 int explosion_type = EX_TYPE_NORMAL;
5507 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5509 struct PlayerInfo *player = PLAYERINFO(x, y);
5511 element = Feld[x][y] = player->initial_element;
5513 if (level.use_explosion_element[player->index_nr])
5515 int explosion_element = level.explosion_element[player->index_nr];
5517 if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
5518 explosion_type = EX_TYPE_CROSS;
5519 else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
5520 explosion_type = EX_TYPE_CENTER;
5528 case EL_BD_BUTTERFLY:
5531 case EL_DARK_YAMYAM:
5535 RaiseScoreElement(element);
5538 case EL_DYNABOMB_PLAYER_1_ACTIVE:
5539 case EL_DYNABOMB_PLAYER_2_ACTIVE:
5540 case EL_DYNABOMB_PLAYER_3_ACTIVE:
5541 case EL_DYNABOMB_PLAYER_4_ACTIVE:
5542 case EL_DYNABOMB_INCREASE_NUMBER:
5543 case EL_DYNABOMB_INCREASE_SIZE:
5544 case EL_DYNABOMB_INCREASE_POWER:
5545 explosion_type = EX_TYPE_DYNA;
5548 case EL_DC_LANDMINE:
5549 explosion_type = EX_TYPE_CENTER;
5554 case EL_LAMP_ACTIVE:
5555 case EL_AMOEBA_TO_DIAMOND:
5556 if (!IS_PLAYER(x, y)) /* penguin and player may be at same field */
5557 explosion_type = EX_TYPE_CENTER;
5561 if (element_info[element].explosion_type == EXPLODES_CROSS)
5562 explosion_type = EX_TYPE_CROSS;
5563 else if (element_info[element].explosion_type == EXPLODES_1X1)
5564 explosion_type = EX_TYPE_CENTER;
5568 if (explosion_type == EX_TYPE_DYNA)
5571 Explode(x, y, EX_PHASE_START, explosion_type);
5573 CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
5576 void SplashAcid(int x, int y)
5578 if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
5579 (!IN_LEV_FIELD(x - 1, y - 2) ||
5580 !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
5581 Feld[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
5583 if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
5584 (!IN_LEV_FIELD(x + 1, y - 2) ||
5585 !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
5586 Feld[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
5588 PlayLevelSound(x, y, SND_ACID_SPLASHING);
5591 static void InitBeltMovement()
5593 static int belt_base_element[4] =
5595 EL_CONVEYOR_BELT_1_LEFT,
5596 EL_CONVEYOR_BELT_2_LEFT,
5597 EL_CONVEYOR_BELT_3_LEFT,
5598 EL_CONVEYOR_BELT_4_LEFT
5600 static int belt_base_active_element[4] =
5602 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
5603 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
5604 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
5605 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
5610 /* set frame order for belt animation graphic according to belt direction */
5611 for (i = 0; i < NUM_BELTS; i++)
5615 for (j = 0; j < NUM_BELT_PARTS; j++)
5617 int element = belt_base_active_element[belt_nr] + j;
5618 int graphic_1 = el2img(element);
5619 int graphic_2 = el2panelimg(element);
5621 if (game.belt_dir[i] == MV_LEFT)
5623 graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
5624 graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
5628 graphic_info[graphic_1].anim_mode |= ANIM_REVERSE;
5629 graphic_info[graphic_2].anim_mode |= ANIM_REVERSE;
5634 SCAN_PLAYFIELD(x, y)
5636 int element = Feld[x][y];
5638 for (i = 0; i < NUM_BELTS; i++)
5640 if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
5642 int e_belt_nr = getBeltNrFromBeltElement(element);
5645 if (e_belt_nr == belt_nr)
5647 int belt_part = Feld[x][y] - belt_base_element[belt_nr];
5649 Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
5656 static void ToggleBeltSwitch(int x, int y)
5658 static int belt_base_element[4] =
5660 EL_CONVEYOR_BELT_1_LEFT,
5661 EL_CONVEYOR_BELT_2_LEFT,
5662 EL_CONVEYOR_BELT_3_LEFT,
5663 EL_CONVEYOR_BELT_4_LEFT
5665 static int belt_base_active_element[4] =
5667 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
5668 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
5669 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
5670 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
5672 static int belt_base_switch_element[4] =
5674 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
5675 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
5676 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
5677 EL_CONVEYOR_BELT_4_SWITCH_LEFT
5679 static int belt_move_dir[4] =
5687 int element = Feld[x][y];
5688 int belt_nr = getBeltNrFromBeltSwitchElement(element);
5689 int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
5690 int belt_dir = belt_move_dir[belt_dir_nr];
5693 if (!IS_BELT_SWITCH(element))
5696 game.belt_dir_nr[belt_nr] = belt_dir_nr;
5697 game.belt_dir[belt_nr] = belt_dir;
5699 if (belt_dir_nr == 3)
5702 /* set frame order for belt animation graphic according to belt direction */
5703 for (i = 0; i < NUM_BELT_PARTS; i++)
5705 int element = belt_base_active_element[belt_nr] + i;
5706 int graphic_1 = el2img(element);
5707 int graphic_2 = el2panelimg(element);
5709 if (belt_dir == MV_LEFT)
5711 graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
5712 graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
5716 graphic_info[graphic_1].anim_mode |= ANIM_REVERSE;
5717 graphic_info[graphic_2].anim_mode |= ANIM_REVERSE;
5721 SCAN_PLAYFIELD(xx, yy)
5723 int element = Feld[xx][yy];
5725 if (IS_BELT_SWITCH(element))
5727 int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
5729 if (e_belt_nr == belt_nr)
5731 Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
5732 TEST_DrawLevelField(xx, yy);
5735 else if (IS_BELT(element) && belt_dir != MV_NONE)
5737 int e_belt_nr = getBeltNrFromBeltElement(element);
5739 if (e_belt_nr == belt_nr)
5741 int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
5743 Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
5744 TEST_DrawLevelField(xx, yy);
5747 else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
5749 int e_belt_nr = getBeltNrFromBeltActiveElement(element);
5751 if (e_belt_nr == belt_nr)
5753 int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
5755 Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
5756 TEST_DrawLevelField(xx, yy);
5762 static void ToggleSwitchgateSwitch(int x, int y)
5766 game.switchgate_pos = !game.switchgate_pos;
5768 SCAN_PLAYFIELD(xx, yy)
5770 int element = Feld[xx][yy];
5772 if (element == EL_SWITCHGATE_SWITCH_UP)
5774 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_DOWN;
5775 TEST_DrawLevelField(xx, yy);
5777 else if (element == EL_SWITCHGATE_SWITCH_DOWN)
5779 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP;
5780 TEST_DrawLevelField(xx, yy);
5782 else if (element == EL_DC_SWITCHGATE_SWITCH_UP)
5784 Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_DOWN;
5785 TEST_DrawLevelField(xx, yy);
5787 else if (element == EL_DC_SWITCHGATE_SWITCH_DOWN)
5789 Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP;
5790 TEST_DrawLevelField(xx, yy);
5792 else if (element == EL_SWITCHGATE_OPEN ||
5793 element == EL_SWITCHGATE_OPENING)
5795 Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
5797 PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
5799 else if (element == EL_SWITCHGATE_CLOSED ||
5800 element == EL_SWITCHGATE_CLOSING)
5802 Feld[xx][yy] = EL_SWITCHGATE_OPENING;
5804 PlayLevelSoundAction(xx, yy, ACTION_OPENING);
5809 static int getInvisibleActiveFromInvisibleElement(int element)
5811 return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
5812 element == EL_INVISIBLE_WALL ? EL_INVISIBLE_WALL_ACTIVE :
5813 element == EL_INVISIBLE_SAND ? EL_INVISIBLE_SAND_ACTIVE :
5817 static int getInvisibleFromInvisibleActiveElement(int element)
5819 return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
5820 element == EL_INVISIBLE_WALL_ACTIVE ? EL_INVISIBLE_WALL :
5821 element == EL_INVISIBLE_SAND_ACTIVE ? EL_INVISIBLE_SAND :
5825 static void RedrawAllLightSwitchesAndInvisibleElements()
5829 SCAN_PLAYFIELD(x, y)
5831 int element = Feld[x][y];
5833 if (element == EL_LIGHT_SWITCH &&
5834 game.light_time_left > 0)
5836 Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
5837 TEST_DrawLevelField(x, y);
5839 else if (element == EL_LIGHT_SWITCH_ACTIVE &&
5840 game.light_time_left == 0)
5842 Feld[x][y] = EL_LIGHT_SWITCH;
5843 TEST_DrawLevelField(x, y);
5845 else if (element == EL_EMC_DRIPPER &&
5846 game.light_time_left > 0)
5848 Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
5849 TEST_DrawLevelField(x, y);
5851 else if (element == EL_EMC_DRIPPER_ACTIVE &&
5852 game.light_time_left == 0)
5854 Feld[x][y] = EL_EMC_DRIPPER;
5855 TEST_DrawLevelField(x, y);
5857 else if (element == EL_INVISIBLE_STEELWALL ||
5858 element == EL_INVISIBLE_WALL ||
5859 element == EL_INVISIBLE_SAND)
5861 if (game.light_time_left > 0)
5862 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
5864 TEST_DrawLevelField(x, y);
5866 /* uncrumble neighbour fields, if needed */
5867 if (element == EL_INVISIBLE_SAND)
5868 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
5870 else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
5871 element == EL_INVISIBLE_WALL_ACTIVE ||
5872 element == EL_INVISIBLE_SAND_ACTIVE)
5874 if (game.light_time_left == 0)
5875 Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
5877 TEST_DrawLevelField(x, y);
5879 /* re-crumble neighbour fields, if needed */
5880 if (element == EL_INVISIBLE_SAND)
5881 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
5886 static void RedrawAllInvisibleElementsForLenses()
5890 SCAN_PLAYFIELD(x, y)
5892 int element = Feld[x][y];
5894 if (element == EL_EMC_DRIPPER &&
5895 game.lenses_time_left > 0)
5897 Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
5898 TEST_DrawLevelField(x, y);
5900 else if (element == EL_EMC_DRIPPER_ACTIVE &&
5901 game.lenses_time_left == 0)
5903 Feld[x][y] = EL_EMC_DRIPPER;
5904 TEST_DrawLevelField(x, y);
5906 else if (element == EL_INVISIBLE_STEELWALL ||
5907 element == EL_INVISIBLE_WALL ||
5908 element == EL_INVISIBLE_SAND)
5910 if (game.lenses_time_left > 0)
5911 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
5913 TEST_DrawLevelField(x, y);
5915 /* uncrumble neighbour fields, if needed */
5916 if (element == EL_INVISIBLE_SAND)
5917 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
5919 else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
5920 element == EL_INVISIBLE_WALL_ACTIVE ||
5921 element == EL_INVISIBLE_SAND_ACTIVE)
5923 if (game.lenses_time_left == 0)
5924 Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
5926 TEST_DrawLevelField(x, y);
5928 /* re-crumble neighbour fields, if needed */
5929 if (element == EL_INVISIBLE_SAND)
5930 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
5935 static void RedrawAllInvisibleElementsForMagnifier()
5939 SCAN_PLAYFIELD(x, y)
5941 int element = Feld[x][y];
5943 if (element == EL_EMC_FAKE_GRASS &&
5944 game.magnify_time_left > 0)
5946 Feld[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
5947 TEST_DrawLevelField(x, y);
5949 else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
5950 game.magnify_time_left == 0)
5952 Feld[x][y] = EL_EMC_FAKE_GRASS;
5953 TEST_DrawLevelField(x, y);
5955 else if (IS_GATE_GRAY(element) &&
5956 game.magnify_time_left > 0)
5958 Feld[x][y] = (IS_RND_GATE_GRAY(element) ?
5959 element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
5960 IS_EM_GATE_GRAY(element) ?
5961 element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
5962 IS_EMC_GATE_GRAY(element) ?
5963 element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
5964 IS_DC_GATE_GRAY(element) ?
5965 EL_DC_GATE_WHITE_GRAY_ACTIVE :
5967 TEST_DrawLevelField(x, y);
5969 else if (IS_GATE_GRAY_ACTIVE(element) &&
5970 game.magnify_time_left == 0)
5972 Feld[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
5973 element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
5974 IS_EM_GATE_GRAY_ACTIVE(element) ?
5975 element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
5976 IS_EMC_GATE_GRAY_ACTIVE(element) ?
5977 element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
5978 IS_DC_GATE_GRAY_ACTIVE(element) ?
5979 EL_DC_GATE_WHITE_GRAY :
5981 TEST_DrawLevelField(x, y);
5986 static void ToggleLightSwitch(int x, int y)
5988 int element = Feld[x][y];
5990 game.light_time_left =
5991 (element == EL_LIGHT_SWITCH ?
5992 level.time_light * FRAMES_PER_SECOND : 0);
5994 RedrawAllLightSwitchesAndInvisibleElements();
5997 static void ActivateTimegateSwitch(int x, int y)
6001 game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
6003 SCAN_PLAYFIELD(xx, yy)
6005 int element = Feld[xx][yy];
6007 if (element == EL_TIMEGATE_CLOSED ||
6008 element == EL_TIMEGATE_CLOSING)
6010 Feld[xx][yy] = EL_TIMEGATE_OPENING;
6011 PlayLevelSound(xx, yy, SND_CLASS_TIMEGATE_OPENING);
6015 else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
6017 Feld[xx][yy] = EL_TIMEGATE_SWITCH;
6018 TEST_DrawLevelField(xx, yy);
6024 Feld[x][y] = (Feld[x][y] == EL_TIMEGATE_SWITCH ? EL_TIMEGATE_SWITCH_ACTIVE :
6025 EL_DC_TIMEGATE_SWITCH_ACTIVE);
6028 void Impact(int x, int y)
6030 boolean last_line = (y == lev_fieldy - 1);
6031 boolean object_hit = FALSE;
6032 boolean impact = (last_line || object_hit);
6033 int element = Feld[x][y];
6034 int smashed = EL_STEELWALL;
6036 if (!last_line) /* check if element below was hit */
6038 if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
6041 object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
6042 MovDir[x][y + 1] != MV_DOWN ||
6043 MovPos[x][y + 1] <= TILEY / 2));
6045 /* do not smash moving elements that left the smashed field in time */
6046 if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
6047 ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
6050 #if USE_QUICKSAND_IMPACT_BUGFIX
6051 if (Feld[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
6053 RemoveMovingField(x, y + 1);
6054 Feld[x][y + 1] = EL_QUICKSAND_EMPTY;
6055 Feld[x][y + 2] = EL_ROCK;
6056 TEST_DrawLevelField(x, y + 2);
6061 if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTYING && object_hit == FALSE)
6063 RemoveMovingField(x, y + 1);
6064 Feld[x][y + 1] = EL_QUICKSAND_FAST_EMPTY;
6065 Feld[x][y + 2] = EL_ROCK;
6066 TEST_DrawLevelField(x, y + 2);
6073 smashed = MovingOrBlocked2Element(x, y + 1);
6075 impact = (last_line || object_hit);
6078 if (!last_line && smashed == EL_ACID) /* element falls into acid */
6080 SplashAcid(x, y + 1);
6084 /* !!! not sufficient for all cases -- see EL_PEARL below !!! */
6085 /* only reset graphic animation if graphic really changes after impact */
6087 el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
6089 ResetGfxAnimation(x, y);
6090 TEST_DrawLevelField(x, y);
6093 if (impact && CAN_EXPLODE_IMPACT(element))
6098 else if (impact && element == EL_PEARL &&
6099 smashed != EL_DC_MAGIC_WALL && smashed != EL_DC_MAGIC_WALL_ACTIVE)
6101 ResetGfxAnimation(x, y);
6103 Feld[x][y] = EL_PEARL_BREAKING;
6104 PlayLevelSound(x, y, SND_PEARL_BREAKING);
6107 else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
6109 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6114 if (impact && element == EL_AMOEBA_DROP)
6116 if (object_hit && IS_PLAYER(x, y + 1))
6117 KillPlayerUnlessEnemyProtected(x, y + 1);
6118 else if (object_hit && smashed == EL_PENGUIN)
6122 Feld[x][y] = EL_AMOEBA_GROWING;
6123 Store[x][y] = EL_AMOEBA_WET;
6125 ResetRandomAnimationValue(x, y);
6130 if (object_hit) /* check which object was hit */
6132 if ((CAN_PASS_MAGIC_WALL(element) &&
6133 (smashed == EL_MAGIC_WALL ||
6134 smashed == EL_BD_MAGIC_WALL)) ||
6135 (CAN_PASS_DC_MAGIC_WALL(element) &&
6136 smashed == EL_DC_MAGIC_WALL))
6139 int activated_magic_wall =
6140 (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
6141 smashed == EL_BD_MAGIC_WALL ? EL_BD_MAGIC_WALL_ACTIVE :
6142 EL_DC_MAGIC_WALL_ACTIVE);
6144 /* activate magic wall / mill */
6145 SCAN_PLAYFIELD(xx, yy)
6147 if (Feld[xx][yy] == smashed)
6148 Feld[xx][yy] = activated_magic_wall;
6151 game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
6152 game.magic_wall_active = TRUE;
6154 PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
6155 SND_MAGIC_WALL_ACTIVATING :
6156 smashed == EL_BD_MAGIC_WALL ?
6157 SND_BD_MAGIC_WALL_ACTIVATING :
6158 SND_DC_MAGIC_WALL_ACTIVATING));
6161 if (IS_PLAYER(x, y + 1))
6163 if (CAN_SMASH_PLAYER(element))
6165 KillPlayerUnlessEnemyProtected(x, y + 1);
6169 else if (smashed == EL_PENGUIN)
6171 if (CAN_SMASH_PLAYER(element))
6177 else if (element == EL_BD_DIAMOND)
6179 if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
6185 else if (((element == EL_SP_INFOTRON ||
6186 element == EL_SP_ZONK) &&
6187 (smashed == EL_SP_SNIKSNAK ||
6188 smashed == EL_SP_ELECTRON ||
6189 smashed == EL_SP_DISK_ORANGE)) ||
6190 (element == EL_SP_INFOTRON &&
6191 smashed == EL_SP_DISK_YELLOW))
6196 else if (CAN_SMASH_EVERYTHING(element))
6198 if (IS_CLASSIC_ENEMY(smashed) ||
6199 CAN_EXPLODE_SMASHED(smashed))
6204 else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
6206 if (smashed == EL_LAMP ||
6207 smashed == EL_LAMP_ACTIVE)
6212 else if (smashed == EL_NUT)
6214 Feld[x][y + 1] = EL_NUT_BREAKING;
6215 PlayLevelSound(x, y, SND_NUT_BREAKING);
6216 RaiseScoreElement(EL_NUT);
6219 else if (smashed == EL_PEARL)
6221 ResetGfxAnimation(x, y);
6223 Feld[x][y + 1] = EL_PEARL_BREAKING;
6224 PlayLevelSound(x, y, SND_PEARL_BREAKING);
6227 else if (smashed == EL_DIAMOND)
6229 Feld[x][y + 1] = EL_DIAMOND_BREAKING;
6230 PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
6233 else if (IS_BELT_SWITCH(smashed))
6235 ToggleBeltSwitch(x, y + 1);
6237 else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
6238 smashed == EL_SWITCHGATE_SWITCH_DOWN ||
6239 smashed == EL_DC_SWITCHGATE_SWITCH_UP ||
6240 smashed == EL_DC_SWITCHGATE_SWITCH_DOWN)
6242 ToggleSwitchgateSwitch(x, y + 1);
6244 else if (smashed == EL_LIGHT_SWITCH ||
6245 smashed == EL_LIGHT_SWITCH_ACTIVE)
6247 ToggleLightSwitch(x, y + 1);
6251 CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6253 CheckElementChangeBySide(x, y + 1, smashed, element,
6254 CE_SWITCHED, CH_SIDE_TOP);
6255 CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
6261 CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6266 /* play sound of magic wall / mill */
6268 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
6269 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ||
6270 Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE))
6272 if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
6273 PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
6274 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
6275 PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
6276 else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
6277 PlayLevelSound(x, y, SND_DC_MAGIC_WALL_FILLING);
6282 /* play sound of object that hits the ground */
6283 if (last_line || object_hit)
6284 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6287 inline static void TurnRoundExt(int x, int y)
6299 { 0, 0 }, { 0, 0 }, { 0, 0 },
6304 int left, right, back;
6308 { MV_DOWN, MV_UP, MV_RIGHT },
6309 { MV_UP, MV_DOWN, MV_LEFT },
6311 { MV_LEFT, MV_RIGHT, MV_DOWN },
6315 { MV_RIGHT, MV_LEFT, MV_UP }
6318 int element = Feld[x][y];
6319 int move_pattern = element_info[element].move_pattern;
6321 int old_move_dir = MovDir[x][y];
6322 int left_dir = turn[old_move_dir].left;
6323 int right_dir = turn[old_move_dir].right;
6324 int back_dir = turn[old_move_dir].back;
6326 int left_dx = move_xy[left_dir].dx, left_dy = move_xy[left_dir].dy;
6327 int right_dx = move_xy[right_dir].dx, right_dy = move_xy[right_dir].dy;
6328 int move_dx = move_xy[old_move_dir].dx, move_dy = move_xy[old_move_dir].dy;
6329 int back_dx = move_xy[back_dir].dx, back_dy = move_xy[back_dir].dy;
6331 int left_x = x + left_dx, left_y = y + left_dy;
6332 int right_x = x + right_dx, right_y = y + right_dy;
6333 int move_x = x + move_dx, move_y = y + move_dy;
6337 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
6339 TestIfBadThingTouchesOtherBadThing(x, y);
6341 if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
6342 MovDir[x][y] = right_dir;
6343 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6344 MovDir[x][y] = left_dir;
6346 if (element == EL_BUG && MovDir[x][y] != old_move_dir)
6348 else if (element == EL_BD_BUTTERFLY) /* && MovDir[x][y] == left_dir) */
6351 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
6353 TestIfBadThingTouchesOtherBadThing(x, y);
6355 if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
6356 MovDir[x][y] = left_dir;
6357 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6358 MovDir[x][y] = right_dir;
6360 if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
6362 else if (element == EL_BD_FIREFLY) /* && MovDir[x][y] == right_dir) */
6365 else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
6367 TestIfBadThingTouchesOtherBadThing(x, y);
6369 if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
6370 MovDir[x][y] = left_dir;
6371 else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
6372 MovDir[x][y] = right_dir;
6374 if (MovDir[x][y] != old_move_dir)
6377 else if (element == EL_YAMYAM)
6379 boolean can_turn_left = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
6380 boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
6382 if (can_turn_left && can_turn_right)
6383 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6384 else if (can_turn_left)
6385 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6386 else if (can_turn_right)
6387 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6389 MovDir[x][y] = back_dir;
6391 MovDelay[x][y] = 16 + 16 * RND(3);
6393 else if (element == EL_DARK_YAMYAM)
6395 boolean can_turn_left = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6397 boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6400 if (can_turn_left && can_turn_right)
6401 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6402 else if (can_turn_left)
6403 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6404 else if (can_turn_right)
6405 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6407 MovDir[x][y] = back_dir;
6409 MovDelay[x][y] = 16 + 16 * RND(3);
6411 else if (element == EL_PACMAN)
6413 boolean can_turn_left = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
6414 boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
6416 if (can_turn_left && can_turn_right)
6417 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6418 else if (can_turn_left)
6419 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6420 else if (can_turn_right)
6421 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6423 MovDir[x][y] = back_dir;
6425 MovDelay[x][y] = 6 + RND(40);
6427 else if (element == EL_PIG)
6429 boolean can_turn_left = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
6430 boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
6431 boolean can_move_on = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
6432 boolean should_turn_left, should_turn_right, should_move_on;
6434 int rnd = RND(rnd_value);
6436 should_turn_left = (can_turn_left &&
6438 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
6439 y + back_dy + left_dy)));
6440 should_turn_right = (can_turn_right &&
6442 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
6443 y + back_dy + right_dy)));
6444 should_move_on = (can_move_on &&
6447 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
6448 y + move_dy + left_dy) ||
6449 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
6450 y + move_dy + right_dy)));
6452 if (should_turn_left || should_turn_right || should_move_on)
6454 if (should_turn_left && should_turn_right && should_move_on)
6455 MovDir[x][y] = (rnd < rnd_value / 3 ? left_dir :
6456 rnd < 2 * rnd_value / 3 ? right_dir :
6458 else if (should_turn_left && should_turn_right)
6459 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6460 else if (should_turn_left && should_move_on)
6461 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
6462 else if (should_turn_right && should_move_on)
6463 MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
6464 else if (should_turn_left)
6465 MovDir[x][y] = left_dir;
6466 else if (should_turn_right)
6467 MovDir[x][y] = right_dir;
6468 else if (should_move_on)
6469 MovDir[x][y] = old_move_dir;
6471 else if (can_move_on && rnd > rnd_value / 8)
6472 MovDir[x][y] = old_move_dir;
6473 else if (can_turn_left && can_turn_right)
6474 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6475 else if (can_turn_left && rnd > rnd_value / 8)
6476 MovDir[x][y] = left_dir;
6477 else if (can_turn_right && rnd > rnd_value/8)
6478 MovDir[x][y] = right_dir;
6480 MovDir[x][y] = back_dir;
6482 xx = x + move_xy[MovDir[x][y]].dx;
6483 yy = y + move_xy[MovDir[x][y]].dy;
6485 if (!IN_LEV_FIELD(xx, yy) ||
6486 (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy])))
6487 MovDir[x][y] = old_move_dir;
6491 else if (element == EL_DRAGON)
6493 boolean can_turn_left = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
6494 boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
6495 boolean can_move_on = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
6497 int rnd = RND(rnd_value);
6499 if (can_move_on && rnd > rnd_value / 8)
6500 MovDir[x][y] = old_move_dir;
6501 else if (can_turn_left && can_turn_right)
6502 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6503 else if (can_turn_left && rnd > rnd_value / 8)
6504 MovDir[x][y] = left_dir;
6505 else if (can_turn_right && rnd > rnd_value / 8)
6506 MovDir[x][y] = right_dir;
6508 MovDir[x][y] = back_dir;
6510 xx = x + move_xy[MovDir[x][y]].dx;
6511 yy = y + move_xy[MovDir[x][y]].dy;
6513 if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
6514 MovDir[x][y] = old_move_dir;
6518 else if (element == EL_MOLE)
6520 boolean can_move_on =
6521 (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
6522 IS_AMOEBOID(Feld[move_x][move_y]) ||
6523 Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
6526 boolean can_turn_left =
6527 (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
6528 IS_AMOEBOID(Feld[left_x][left_y])));
6530 boolean can_turn_right =
6531 (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
6532 IS_AMOEBOID(Feld[right_x][right_y])));
6534 if (can_turn_left && can_turn_right)
6535 MovDir[x][y] = (RND(2) ? left_dir : right_dir);
6536 else if (can_turn_left)
6537 MovDir[x][y] = left_dir;
6539 MovDir[x][y] = right_dir;
6542 if (MovDir[x][y] != old_move_dir)
6545 else if (element == EL_BALLOON)
6547 MovDir[x][y] = game.wind_direction;
6550 else if (element == EL_SPRING)
6552 if (MovDir[x][y] & MV_HORIZONTAL)
6554 if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
6555 !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
6557 Feld[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
6558 ResetGfxAnimation(move_x, move_y);
6559 TEST_DrawLevelField(move_x, move_y);
6561 MovDir[x][y] = back_dir;
6563 else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
6564 SPRING_CAN_ENTER_FIELD(element, x, y + 1))
6565 MovDir[x][y] = MV_NONE;
6570 else if (element == EL_ROBOT ||
6571 element == EL_SATELLITE ||
6572 element == EL_PENGUIN ||
6573 element == EL_EMC_ANDROID)
6575 int attr_x = -1, attr_y = -1;
6586 for (i = 0; i < MAX_PLAYERS; i++)
6588 struct PlayerInfo *player = &stored_player[i];
6589 int jx = player->jx, jy = player->jy;
6591 if (!player->active)
6595 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
6603 if (element == EL_ROBOT && ZX >= 0 && ZY >= 0 &&
6604 (Feld[ZX][ZY] == EL_ROBOT_WHEEL_ACTIVE ||
6605 game.engine_version < VERSION_IDENT(3,1,0,0)))
6611 if (element == EL_PENGUIN)
6614 static int xy[4][2] =
6622 for (i = 0; i < NUM_DIRECTIONS; i++)
6624 int ex = x + xy[i][0];
6625 int ey = y + xy[i][1];
6627 if (IN_LEV_FIELD(ex, ey) && (Feld[ex][ey] == EL_EXIT_OPEN ||
6628 Feld[ex][ey] == EL_EM_EXIT_OPEN ||
6629 Feld[ex][ey] == EL_STEEL_EXIT_OPEN ||
6630 Feld[ex][ey] == EL_EM_STEEL_EXIT_OPEN))
6639 MovDir[x][y] = MV_NONE;
6641 MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
6642 else if (attr_x > x)
6643 MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
6645 MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
6646 else if (attr_y > y)
6647 MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
6649 if (element == EL_ROBOT)
6653 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6654 MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
6655 Moving2Blocked(x, y, &newx, &newy);
6657 if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
6658 MovDelay[x][y] = 8 + 8 * !RND(3);
6660 MovDelay[x][y] = 16;
6662 else if (element == EL_PENGUIN)
6668 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6670 boolean first_horiz = RND(2);
6671 int new_move_dir = MovDir[x][y];
6674 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6675 Moving2Blocked(x, y, &newx, &newy);
6677 if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
6681 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6682 Moving2Blocked(x, y, &newx, &newy);
6684 if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
6687 MovDir[x][y] = old_move_dir;
6691 else if (element == EL_SATELLITE)
6697 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6699 boolean first_horiz = RND(2);
6700 int new_move_dir = MovDir[x][y];
6703 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6704 Moving2Blocked(x, y, &newx, &newy);
6706 if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
6710 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6711 Moving2Blocked(x, y, &newx, &newy);
6713 if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
6716 MovDir[x][y] = old_move_dir;
6720 else if (element == EL_EMC_ANDROID)
6722 static int check_pos[16] =
6724 -1, /* 0 => (invalid) */
6725 7, /* 1 => MV_LEFT */
6726 3, /* 2 => MV_RIGHT */
6727 -1, /* 3 => (invalid) */
6729 0, /* 5 => MV_LEFT | MV_UP */
6730 2, /* 6 => MV_RIGHT | MV_UP */
6731 -1, /* 7 => (invalid) */
6732 5, /* 8 => MV_DOWN */
6733 6, /* 9 => MV_LEFT | MV_DOWN */
6734 4, /* 10 => MV_RIGHT | MV_DOWN */
6735 -1, /* 11 => (invalid) */
6736 -1, /* 12 => (invalid) */
6737 -1, /* 13 => (invalid) */
6738 -1, /* 14 => (invalid) */
6739 -1, /* 15 => (invalid) */
6747 { -1, -1, MV_LEFT | MV_UP },
6749 { +1, -1, MV_RIGHT | MV_UP },
6750 { +1, 0, MV_RIGHT },
6751 { +1, +1, MV_RIGHT | MV_DOWN },
6753 { -1, +1, MV_LEFT | MV_DOWN },
6756 int start_pos, check_order;
6757 boolean can_clone = FALSE;
6760 /* check if there is any free field around current position */
6761 for (i = 0; i < 8; i++)
6763 int newx = x + check_xy[i].dx;
6764 int newy = y + check_xy[i].dy;
6766 if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
6774 if (can_clone) /* randomly find an element to clone */
6778 start_pos = check_pos[RND(8)];
6779 check_order = (RND(2) ? -1 : +1);
6781 for (i = 0; i < 8; i++)
6783 int pos_raw = start_pos + i * check_order;
6784 int pos = (pos_raw + 8) % 8;
6785 int newx = x + check_xy[pos].dx;
6786 int newy = y + check_xy[pos].dy;
6788 if (ANDROID_CAN_CLONE_FIELD(newx, newy))
6790 element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
6791 element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
6793 Store[x][y] = Feld[newx][newy];
6802 if (can_clone) /* randomly find a direction to move */
6806 start_pos = check_pos[RND(8)];
6807 check_order = (RND(2) ? -1 : +1);
6809 for (i = 0; i < 8; i++)
6811 int pos_raw = start_pos + i * check_order;
6812 int pos = (pos_raw + 8) % 8;
6813 int newx = x + check_xy[pos].dx;
6814 int newy = y + check_xy[pos].dy;
6815 int new_move_dir = check_xy[pos].dir;
6817 if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
6819 MovDir[x][y] = new_move_dir;
6820 MovDelay[x][y] = level.android_clone_time * 8 + 1;
6829 if (can_clone) /* cloning and moving successful */
6832 /* cannot clone -- try to move towards player */
6834 start_pos = check_pos[MovDir[x][y] & 0x0f];
6835 check_order = (RND(2) ? -1 : +1);
6837 for (i = 0; i < 3; i++)
6839 /* first check start_pos, then previous/next or (next/previous) pos */
6840 int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
6841 int pos = (pos_raw + 8) % 8;
6842 int newx = x + check_xy[pos].dx;
6843 int newy = y + check_xy[pos].dy;
6844 int new_move_dir = check_xy[pos].dir;
6846 if (IS_PLAYER(newx, newy))
6849 if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
6851 MovDir[x][y] = new_move_dir;
6852 MovDelay[x][y] = level.android_move_time * 8 + 1;
6859 else if (move_pattern == MV_TURNING_LEFT ||
6860 move_pattern == MV_TURNING_RIGHT ||
6861 move_pattern == MV_TURNING_LEFT_RIGHT ||
6862 move_pattern == MV_TURNING_RIGHT_LEFT ||
6863 move_pattern == MV_TURNING_RANDOM ||
6864 move_pattern == MV_ALL_DIRECTIONS)
6866 boolean can_turn_left =
6867 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
6868 boolean can_turn_right =
6869 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
6871 if (element_info[element].move_stepsize == 0) /* "not moving" */
6874 if (move_pattern == MV_TURNING_LEFT)
6875 MovDir[x][y] = left_dir;
6876 else if (move_pattern == MV_TURNING_RIGHT)
6877 MovDir[x][y] = right_dir;
6878 else if (move_pattern == MV_TURNING_LEFT_RIGHT)
6879 MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
6880 else if (move_pattern == MV_TURNING_RIGHT_LEFT)
6881 MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
6882 else if (move_pattern == MV_TURNING_RANDOM)
6883 MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
6884 can_turn_right && !can_turn_left ? right_dir :
6885 RND(2) ? left_dir : right_dir);
6886 else if (can_turn_left && can_turn_right)
6887 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6888 else if (can_turn_left)
6889 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6890 else if (can_turn_right)
6891 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6893 MovDir[x][y] = back_dir;
6895 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6897 else if (move_pattern == MV_HORIZONTAL ||
6898 move_pattern == MV_VERTICAL)
6900 if (move_pattern & old_move_dir)
6901 MovDir[x][y] = back_dir;
6902 else if (move_pattern == MV_HORIZONTAL)
6903 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
6904 else if (move_pattern == MV_VERTICAL)
6905 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
6907 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6909 else if (move_pattern & MV_ANY_DIRECTION)
6911 MovDir[x][y] = move_pattern;
6912 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6914 else if (move_pattern & MV_WIND_DIRECTION)
6916 MovDir[x][y] = game.wind_direction;
6917 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6919 else if (move_pattern == MV_ALONG_LEFT_SIDE)
6921 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
6922 MovDir[x][y] = left_dir;
6923 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
6924 MovDir[x][y] = right_dir;
6926 if (MovDir[x][y] != old_move_dir)
6927 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6929 else if (move_pattern == MV_ALONG_RIGHT_SIDE)
6931 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
6932 MovDir[x][y] = right_dir;
6933 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
6934 MovDir[x][y] = left_dir;
6936 if (MovDir[x][y] != old_move_dir)
6937 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6939 else if (move_pattern == MV_TOWARDS_PLAYER ||
6940 move_pattern == MV_AWAY_FROM_PLAYER)
6942 int attr_x = -1, attr_y = -1;
6944 boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
6955 for (i = 0; i < MAX_PLAYERS; i++)
6957 struct PlayerInfo *player = &stored_player[i];
6958 int jx = player->jx, jy = player->jy;
6960 if (!player->active)
6964 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
6972 MovDir[x][y] = MV_NONE;
6974 MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
6975 else if (attr_x > x)
6976 MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
6978 MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
6979 else if (attr_y > y)
6980 MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
6982 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6984 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6986 boolean first_horiz = RND(2);
6987 int new_move_dir = MovDir[x][y];
6989 if (element_info[element].move_stepsize == 0) /* "not moving" */
6991 first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
6992 MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6998 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6999 Moving2Blocked(x, y, &newx, &newy);
7001 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7005 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7006 Moving2Blocked(x, y, &newx, &newy);
7008 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7011 MovDir[x][y] = old_move_dir;
7014 else if (move_pattern == MV_WHEN_PUSHED ||
7015 move_pattern == MV_WHEN_DROPPED)
7017 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7018 MovDir[x][y] = MV_NONE;
7022 else if (move_pattern & MV_MAZE_RUNNER_STYLE)
7024 static int test_xy[7][2] =
7034 static int test_dir[7] =
7044 boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
7045 int move_preference = -1000000; /* start with very low preference */
7046 int new_move_dir = MV_NONE;
7047 int start_test = RND(4);
7050 for (i = 0; i < NUM_DIRECTIONS; i++)
7052 int move_dir = test_dir[start_test + i];
7053 int move_dir_preference;
7055 xx = x + test_xy[start_test + i][0];
7056 yy = y + test_xy[start_test + i][1];
7058 if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
7059 (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
7061 new_move_dir = move_dir;
7066 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
7069 move_dir_preference = -1 * RunnerVisit[xx][yy];
7070 if (hunter_mode && PlayerVisit[xx][yy] > 0)
7071 move_dir_preference = PlayerVisit[xx][yy];
7073 if (move_dir_preference > move_preference)
7075 /* prefer field that has not been visited for the longest time */
7076 move_preference = move_dir_preference;
7077 new_move_dir = move_dir;
7079 else if (move_dir_preference == move_preference &&
7080 move_dir == old_move_dir)
7082 /* prefer last direction when all directions are preferred equally */
7083 move_preference = move_dir_preference;
7084 new_move_dir = move_dir;
7088 MovDir[x][y] = new_move_dir;
7089 if (old_move_dir != new_move_dir)
7090 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7094 static void TurnRound(int x, int y)
7096 int direction = MovDir[x][y];
7100 GfxDir[x][y] = MovDir[x][y];
7102 if (direction != MovDir[x][y])
7106 GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
7108 ResetGfxFrame(x, y, FALSE);
7111 static boolean JustBeingPushed(int x, int y)
7115 for (i = 0; i < MAX_PLAYERS; i++)
7117 struct PlayerInfo *player = &stored_player[i];
7119 if (player->active && player->is_pushing && player->MovPos)
7121 int next_jx = player->jx + (player->jx - player->last_jx);
7122 int next_jy = player->jy + (player->jy - player->last_jy);
7124 if (x == next_jx && y == next_jy)
7132 void StartMoving(int x, int y)
7134 boolean started_moving = FALSE; /* some elements can fall _and_ move */
7135 int element = Feld[x][y];
7140 if (MovDelay[x][y] == 0)
7141 GfxAction[x][y] = ACTION_DEFAULT;
7143 if (CAN_FALL(element) && y < lev_fieldy - 1)
7145 if ((x > 0 && IS_PLAYER(x - 1, y)) ||
7146 (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
7147 if (JustBeingPushed(x, y))
7150 if (element == EL_QUICKSAND_FULL)
7152 if (IS_FREE(x, y + 1))
7154 InitMovingField(x, y, MV_DOWN);
7155 started_moving = TRUE;
7157 Feld[x][y] = EL_QUICKSAND_EMPTYING;
7158 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7159 if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7160 Store[x][y] = EL_ROCK;
7162 Store[x][y] = EL_ROCK;
7165 PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7167 else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7169 if (!MovDelay[x][y])
7171 MovDelay[x][y] = TILEY + 1;
7173 ResetGfxAnimation(x, y);
7174 ResetGfxAnimation(x, y + 1);
7179 DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7180 DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7187 Feld[x][y] = EL_QUICKSAND_EMPTY;
7188 Feld[x][y + 1] = EL_QUICKSAND_FULL;
7189 Store[x][y + 1] = Store[x][y];
7192 PlayLevelSoundAction(x, y, ACTION_FILLING);
7194 else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7196 if (!MovDelay[x][y])
7198 MovDelay[x][y] = TILEY + 1;
7200 ResetGfxAnimation(x, y);
7201 ResetGfxAnimation(x, y + 1);
7206 DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7207 DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7214 Feld[x][y] = EL_QUICKSAND_EMPTY;
7215 Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7216 Store[x][y + 1] = Store[x][y];
7219 PlayLevelSoundAction(x, y, ACTION_FILLING);
7222 else if (element == EL_QUICKSAND_FAST_FULL)
7224 if (IS_FREE(x, y + 1))
7226 InitMovingField(x, y, MV_DOWN);
7227 started_moving = TRUE;
7229 Feld[x][y] = EL_QUICKSAND_FAST_EMPTYING;
7230 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7231 if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7232 Store[x][y] = EL_ROCK;
7234 Store[x][y] = EL_ROCK;
7237 PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7239 else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7241 if (!MovDelay[x][y])
7243 MovDelay[x][y] = TILEY + 1;
7245 ResetGfxAnimation(x, y);
7246 ResetGfxAnimation(x, y + 1);
7251 DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7252 DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7259 Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
7260 Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7261 Store[x][y + 1] = Store[x][y];
7264 PlayLevelSoundAction(x, y, ACTION_FILLING);
7266 else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7268 if (!MovDelay[x][y])
7270 MovDelay[x][y] = TILEY + 1;
7272 ResetGfxAnimation(x, y);
7273 ResetGfxAnimation(x, y + 1);
7278 DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7279 DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7286 Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
7287 Feld[x][y + 1] = EL_QUICKSAND_FULL;
7288 Store[x][y + 1] = Store[x][y];
7291 PlayLevelSoundAction(x, y, ACTION_FILLING);
7294 else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7295 Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7297 InitMovingField(x, y, MV_DOWN);
7298 started_moving = TRUE;
7300 Feld[x][y] = EL_QUICKSAND_FILLING;
7301 Store[x][y] = element;
7303 PlayLevelSoundAction(x, y, ACTION_FILLING);
7305 else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7306 Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7308 InitMovingField(x, y, MV_DOWN);
7309 started_moving = TRUE;
7311 Feld[x][y] = EL_QUICKSAND_FAST_FILLING;
7312 Store[x][y] = element;
7314 PlayLevelSoundAction(x, y, ACTION_FILLING);
7316 else if (element == EL_MAGIC_WALL_FULL)
7318 if (IS_FREE(x, y + 1))
7320 InitMovingField(x, y, MV_DOWN);
7321 started_moving = TRUE;
7323 Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
7324 Store[x][y] = EL_CHANGED(Store[x][y]);
7326 else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
7328 if (!MovDelay[x][y])
7329 MovDelay[x][y] = TILEY / 4 + 1;
7338 Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
7339 Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
7340 Store[x][y + 1] = EL_CHANGED(Store[x][y]);
7344 else if (element == EL_BD_MAGIC_WALL_FULL)
7346 if (IS_FREE(x, y + 1))
7348 InitMovingField(x, y, MV_DOWN);
7349 started_moving = TRUE;
7351 Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
7352 Store[x][y] = EL_CHANGED_BD(Store[x][y]);
7354 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
7356 if (!MovDelay[x][y])
7357 MovDelay[x][y] = TILEY / 4 + 1;
7366 Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
7367 Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
7368 Store[x][y + 1] = EL_CHANGED_BD(Store[x][y]);
7372 else if (element == EL_DC_MAGIC_WALL_FULL)
7374 if (IS_FREE(x, y + 1))
7376 InitMovingField(x, y, MV_DOWN);
7377 started_moving = TRUE;
7379 Feld[x][y] = EL_DC_MAGIC_WALL_EMPTYING;
7380 Store[x][y] = EL_CHANGED_DC(Store[x][y]);
7382 else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
7384 if (!MovDelay[x][y])
7385 MovDelay[x][y] = TILEY / 4 + 1;
7394 Feld[x][y] = EL_DC_MAGIC_WALL_ACTIVE;
7395 Feld[x][y + 1] = EL_DC_MAGIC_WALL_FULL;
7396 Store[x][y + 1] = EL_CHANGED_DC(Store[x][y]);
7400 else if ((CAN_PASS_MAGIC_WALL(element) &&
7401 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
7402 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)) ||
7403 (CAN_PASS_DC_MAGIC_WALL(element) &&
7404 (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)))
7407 InitMovingField(x, y, MV_DOWN);
7408 started_moving = TRUE;
7411 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
7412 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ? EL_BD_MAGIC_WALL_FILLING :
7413 EL_DC_MAGIC_WALL_FILLING);
7414 Store[x][y] = element;
7416 else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
7418 SplashAcid(x, y + 1);
7420 InitMovingField(x, y, MV_DOWN);
7421 started_moving = TRUE;
7423 Store[x][y] = EL_ACID;
7426 (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7427 CheckImpact[x][y] && !IS_FREE(x, y + 1)) ||
7428 (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
7429 CAN_FALL(element) && WasJustFalling[x][y] &&
7430 (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
7432 (game.engine_version < VERSION_IDENT(2,2,0,7) &&
7433 CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
7434 (Feld[x][y + 1] == EL_BLOCKED)))
7436 /* this is needed for a special case not covered by calling "Impact()"
7437 from "ContinueMoving()": if an element moves to a tile directly below
7438 another element which was just falling on that tile (which was empty
7439 in the previous frame), the falling element above would just stop
7440 instead of smashing the element below (in previous version, the above
7441 element was just checked for "moving" instead of "falling", resulting
7442 in incorrect smashes caused by horizontal movement of the above
7443 element; also, the case of the player being the element to smash was
7444 simply not covered here... :-/ ) */
7446 CheckCollision[x][y] = 0;
7447 CheckImpact[x][y] = 0;
7451 else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
7453 if (MovDir[x][y] == MV_NONE)
7455 InitMovingField(x, y, MV_DOWN);
7456 started_moving = TRUE;
7459 else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
7461 if (WasJustFalling[x][y]) /* prevent animation from being restarted */
7462 MovDir[x][y] = MV_DOWN;
7464 InitMovingField(x, y, MV_DOWN);
7465 started_moving = TRUE;
7467 else if (element == EL_AMOEBA_DROP)
7469 Feld[x][y] = EL_AMOEBA_GROWING;
7470 Store[x][y] = EL_AMOEBA_WET;
7472 else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
7473 (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
7474 !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
7475 element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
7477 boolean can_fall_left = (x > 0 && IS_FREE(x - 1, y) &&
7478 (IS_FREE(x - 1, y + 1) ||
7479 Feld[x - 1][y + 1] == EL_ACID));
7480 boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
7481 (IS_FREE(x + 1, y + 1) ||
7482 Feld[x + 1][y + 1] == EL_ACID));
7483 boolean can_fall_any = (can_fall_left || can_fall_right);
7484 boolean can_fall_both = (can_fall_left && can_fall_right);
7485 int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
7487 if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
7489 if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
7490 can_fall_right = FALSE;
7491 else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
7492 can_fall_left = FALSE;
7493 else if (slippery_type == SLIPPERY_ONLY_LEFT)
7494 can_fall_right = FALSE;
7495 else if (slippery_type == SLIPPERY_ONLY_RIGHT)
7496 can_fall_left = FALSE;
7498 can_fall_any = (can_fall_left || can_fall_right);
7499 can_fall_both = FALSE;
7504 if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
7505 can_fall_right = FALSE; /* slip down on left side */
7507 can_fall_left = !(can_fall_right = RND(2));
7509 can_fall_both = FALSE;
7514 /* if not determined otherwise, prefer left side for slipping down */
7515 InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
7516 started_moving = TRUE;
7519 else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
7521 boolean left_is_free = (x > 0 && IS_FREE(x - 1, y));
7522 boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
7523 int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
7524 int belt_dir = game.belt_dir[belt_nr];
7526 if ((belt_dir == MV_LEFT && left_is_free) ||
7527 (belt_dir == MV_RIGHT && right_is_free))
7529 int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
7531 InitMovingField(x, y, belt_dir);
7532 started_moving = TRUE;
7534 Pushed[x][y] = TRUE;
7535 Pushed[nextx][y] = TRUE;
7537 GfxAction[x][y] = ACTION_DEFAULT;
7541 MovDir[x][y] = 0; /* if element was moving, stop it */
7546 /* not "else if" because of elements that can fall and move (EL_SPRING) */
7547 if (CAN_MOVE(element) && !started_moving)
7549 int move_pattern = element_info[element].move_pattern;
7552 Moving2Blocked(x, y, &newx, &newy);
7554 if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
7557 if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7558 CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7560 WasJustMoving[x][y] = 0;
7561 CheckCollision[x][y] = 0;
7563 TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
7565 if (Feld[x][y] != element) /* element has changed */
7569 if (!MovDelay[x][y]) /* start new movement phase */
7571 /* all objects that can change their move direction after each step
7572 (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
7574 if (element != EL_YAMYAM &&
7575 element != EL_DARK_YAMYAM &&
7576 element != EL_PACMAN &&
7577 !(move_pattern & MV_ANY_DIRECTION) &&
7578 move_pattern != MV_TURNING_LEFT &&
7579 move_pattern != MV_TURNING_RIGHT &&
7580 move_pattern != MV_TURNING_LEFT_RIGHT &&
7581 move_pattern != MV_TURNING_RIGHT_LEFT &&
7582 move_pattern != MV_TURNING_RANDOM)
7586 if (MovDelay[x][y] && (element == EL_BUG ||
7587 element == EL_SPACESHIP ||
7588 element == EL_SP_SNIKSNAK ||
7589 element == EL_SP_ELECTRON ||
7590 element == EL_MOLE))
7591 TEST_DrawLevelField(x, y);
7595 if (MovDelay[x][y]) /* wait some time before next movement */
7599 if (element == EL_ROBOT ||
7600 element == EL_YAMYAM ||
7601 element == EL_DARK_YAMYAM)
7603 DrawLevelElementAnimationIfNeeded(x, y, element);
7604 PlayLevelSoundAction(x, y, ACTION_WAITING);
7606 else if (element == EL_SP_ELECTRON)
7607 DrawLevelElementAnimationIfNeeded(x, y, element);
7608 else if (element == EL_DRAGON)
7611 int dir = MovDir[x][y];
7612 int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
7613 int dy = (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
7614 int graphic = (dir == MV_LEFT ? IMG_FLAMES_1_LEFT :
7615 dir == MV_RIGHT ? IMG_FLAMES_1_RIGHT :
7616 dir == MV_UP ? IMG_FLAMES_1_UP :
7617 dir == MV_DOWN ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
7618 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
7620 GfxAction[x][y] = ACTION_ATTACKING;
7622 if (IS_PLAYER(x, y))
7623 DrawPlayerField(x, y);
7625 TEST_DrawLevelField(x, y);
7627 PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
7629 for (i = 1; i <= 3; i++)
7631 int xx = x + i * dx;
7632 int yy = y + i * dy;
7633 int sx = SCREENX(xx);
7634 int sy = SCREENY(yy);
7635 int flame_graphic = graphic + (i - 1);
7637 if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
7642 int flamed = MovingOrBlocked2Element(xx, yy);
7644 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
7647 RemoveMovingField(xx, yy);
7649 ChangeDelay[xx][yy] = 0;
7651 Feld[xx][yy] = EL_FLAMES;
7653 if (IN_SCR_FIELD(sx, sy))
7655 TEST_DrawLevelFieldCrumbled(xx, yy);
7656 DrawGraphic(sx, sy, flame_graphic, frame);
7661 if (Feld[xx][yy] == EL_FLAMES)
7662 Feld[xx][yy] = EL_EMPTY;
7663 TEST_DrawLevelField(xx, yy);
7668 if (MovDelay[x][y]) /* element still has to wait some time */
7670 PlayLevelSoundAction(x, y, ACTION_WAITING);
7676 /* now make next step */
7678 Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
7680 if (DONT_COLLIDE_WITH(element) &&
7681 IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
7682 !PLAYER_ENEMY_PROTECTED(newx, newy))
7684 TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
7689 else if (CAN_MOVE_INTO_ACID(element) &&
7690 IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID &&
7691 !IS_MV_DIAGONAL(MovDir[x][y]) &&
7692 (MovDir[x][y] == MV_DOWN ||
7693 game.engine_version >= VERSION_IDENT(3,1,0,0)))
7695 SplashAcid(newx, newy);
7696 Store[x][y] = EL_ACID;
7698 else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
7700 if (Feld[newx][newy] == EL_EXIT_OPEN ||
7701 Feld[newx][newy] == EL_EM_EXIT_OPEN ||
7702 Feld[newx][newy] == EL_STEEL_EXIT_OPEN ||
7703 Feld[newx][newy] == EL_EM_STEEL_EXIT_OPEN)
7706 TEST_DrawLevelField(x, y);
7708 PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
7709 if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
7710 DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
7712 local_player->friends_still_needed--;
7713 if (!local_player->friends_still_needed &&
7714 !local_player->GameOver && AllPlayersGone)
7715 PlayerWins(local_player);
7719 else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
7721 if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MP_MOVING)
7722 TEST_DrawLevelField(newx, newy);
7724 GfxDir[x][y] = MovDir[x][y] = MV_NONE;
7726 else if (!IS_FREE(newx, newy))
7728 GfxAction[x][y] = ACTION_WAITING;
7730 if (IS_PLAYER(x, y))
7731 DrawPlayerField(x, y);
7733 TEST_DrawLevelField(x, y);
7738 else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
7740 if (IS_FOOD_PIG(Feld[newx][newy]))
7742 if (IS_MOVING(newx, newy))
7743 RemoveMovingField(newx, newy);
7746 Feld[newx][newy] = EL_EMPTY;
7747 TEST_DrawLevelField(newx, newy);
7750 PlayLevelSound(x, y, SND_PIG_DIGGING);
7752 else if (!IS_FREE(newx, newy))
7754 if (IS_PLAYER(x, y))
7755 DrawPlayerField(x, y);
7757 TEST_DrawLevelField(x, y);
7762 else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
7764 if (Store[x][y] != EL_EMPTY)
7766 boolean can_clone = FALSE;
7769 /* check if element to clone is still there */
7770 for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
7772 if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == Store[x][y])
7780 /* cannot clone or target field not free anymore -- do not clone */
7781 if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7782 Store[x][y] = EL_EMPTY;
7785 if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7787 if (IS_MV_DIAGONAL(MovDir[x][y]))
7789 int diagonal_move_dir = MovDir[x][y];
7790 int stored = Store[x][y];
7791 int change_delay = 8;
7794 /* android is moving diagonally */
7796 CreateField(x, y, EL_DIAGONAL_SHRINKING);
7798 Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
7799 GfxElement[x][y] = EL_EMC_ANDROID;
7800 GfxAction[x][y] = ACTION_SHRINKING;
7801 GfxDir[x][y] = diagonal_move_dir;
7802 ChangeDelay[x][y] = change_delay;
7804 graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
7807 DrawLevelGraphicAnimation(x, y, graphic);
7808 PlayLevelSoundAction(x, y, ACTION_SHRINKING);
7810 if (Feld[newx][newy] == EL_ACID)
7812 SplashAcid(newx, newy);
7817 CreateField(newx, newy, EL_DIAGONAL_GROWING);
7819 Store[newx][newy] = EL_EMC_ANDROID;
7820 GfxElement[newx][newy] = EL_EMC_ANDROID;
7821 GfxAction[newx][newy] = ACTION_GROWING;
7822 GfxDir[newx][newy] = diagonal_move_dir;
7823 ChangeDelay[newx][newy] = change_delay;
7825 graphic = el_act_dir2img(GfxElement[newx][newy],
7826 GfxAction[newx][newy], GfxDir[newx][newy]);
7828 DrawLevelGraphicAnimation(newx, newy, graphic);
7829 PlayLevelSoundAction(newx, newy, ACTION_GROWING);
7835 Feld[newx][newy] = EL_EMPTY;
7836 TEST_DrawLevelField(newx, newy);
7838 PlayLevelSoundAction(x, y, ACTION_DIGGING);
7841 else if (!IS_FREE(newx, newy))
7846 else if (IS_CUSTOM_ELEMENT(element) &&
7847 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7849 if (!DigFieldByCE(newx, newy, element))
7852 if (move_pattern & MV_MAZE_RUNNER_STYLE)
7854 RunnerVisit[x][y] = FrameCounter;
7855 PlayerVisit[x][y] /= 8; /* expire player visit path */
7858 else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
7860 if (!IS_FREE(newx, newy))
7862 if (IS_PLAYER(x, y))
7863 DrawPlayerField(x, y);
7865 TEST_DrawLevelField(x, y);
7871 boolean wanna_flame = !RND(10);
7872 int dx = newx - x, dy = newy - y;
7873 int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
7874 int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
7875 int element1 = (IN_LEV_FIELD(newx1, newy1) ?
7876 MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
7877 int element2 = (IN_LEV_FIELD(newx2, newy2) ?
7878 MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
7881 IS_CLASSIC_ENEMY(element1) ||
7882 IS_CLASSIC_ENEMY(element2)) &&
7883 element1 != EL_DRAGON && element2 != EL_DRAGON &&
7884 element1 != EL_FLAMES && element2 != EL_FLAMES)
7886 ResetGfxAnimation(x, y);
7887 GfxAction[x][y] = ACTION_ATTACKING;
7889 if (IS_PLAYER(x, y))
7890 DrawPlayerField(x, y);
7892 TEST_DrawLevelField(x, y);
7894 PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
7896 MovDelay[x][y] = 50;
7898 Feld[newx][newy] = EL_FLAMES;
7899 if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
7900 Feld[newx1][newy1] = EL_FLAMES;
7901 if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
7902 Feld[newx2][newy2] = EL_FLAMES;
7908 else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
7909 Feld[newx][newy] == EL_DIAMOND)
7911 if (IS_MOVING(newx, newy))
7912 RemoveMovingField(newx, newy);
7915 Feld[newx][newy] = EL_EMPTY;
7916 TEST_DrawLevelField(newx, newy);
7919 PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
7921 else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
7922 IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
7924 if (AmoebaNr[newx][newy])
7926 AmoebaCnt2[AmoebaNr[newx][newy]]--;
7927 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
7928 Feld[newx][newy] == EL_BD_AMOEBA)
7929 AmoebaCnt[AmoebaNr[newx][newy]]--;
7932 if (IS_MOVING(newx, newy))
7934 RemoveMovingField(newx, newy);
7938 Feld[newx][newy] = EL_EMPTY;
7939 TEST_DrawLevelField(newx, newy);
7942 PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
7944 else if ((element == EL_PACMAN || element == EL_MOLE)
7945 && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
7947 if (AmoebaNr[newx][newy])
7949 AmoebaCnt2[AmoebaNr[newx][newy]]--;
7950 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
7951 Feld[newx][newy] == EL_BD_AMOEBA)
7952 AmoebaCnt[AmoebaNr[newx][newy]]--;
7955 if (element == EL_MOLE)
7957 Feld[newx][newy] = EL_AMOEBA_SHRINKING;
7958 PlayLevelSound(x, y, SND_MOLE_DIGGING);
7960 ResetGfxAnimation(x, y);
7961 GfxAction[x][y] = ACTION_DIGGING;
7962 TEST_DrawLevelField(x, y);
7964 MovDelay[newx][newy] = 0; /* start amoeba shrinking delay */
7966 return; /* wait for shrinking amoeba */
7968 else /* element == EL_PACMAN */
7970 Feld[newx][newy] = EL_EMPTY;
7971 TEST_DrawLevelField(newx, newy);
7972 PlayLevelSound(x, y, SND_PACMAN_DIGGING);
7975 else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
7976 (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
7977 (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
7979 /* wait for shrinking amoeba to completely disappear */
7982 else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
7984 /* object was running against a wall */
7988 if (GFX_ELEMENT(element) != EL_SAND) /* !!! FIX THIS (crumble) !!! */
7989 DrawLevelElementAnimation(x, y, element);
7991 if (DONT_TOUCH(element))
7992 TestIfBadThingTouchesPlayer(x, y);
7997 InitMovingField(x, y, MovDir[x][y]);
7999 PlayLevelSoundAction(x, y, ACTION_MOVING);
8003 ContinueMoving(x, y);
8006 void ContinueMoving(int x, int y)
8008 int element = Feld[x][y];
8009 struct ElementInfo *ei = &element_info[element];
8010 int direction = MovDir[x][y];
8011 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
8012 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
8013 int newx = x + dx, newy = y + dy;
8014 int stored = Store[x][y];
8015 int stored_new = Store[newx][newy];
8016 boolean pushed_by_player = (Pushed[x][y] && IS_PLAYER(x, y));
8017 boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
8018 boolean last_line = (newy == lev_fieldy - 1);
8020 MovPos[x][y] += getElementMoveStepsize(x, y);
8022 if (pushed_by_player) /* special case: moving object pushed by player */
8023 MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
8025 if (ABS(MovPos[x][y]) < TILEX)
8027 TEST_DrawLevelField(x, y);
8029 return; /* element is still moving */
8032 /* element reached destination field */
8034 Feld[x][y] = EL_EMPTY;
8035 Feld[newx][newy] = element;
8036 MovPos[x][y] = 0; /* force "not moving" for "crumbled sand" */
8038 if (Store[x][y] == EL_ACID) /* element is moving into acid pool */
8040 element = Feld[newx][newy] = EL_ACID;
8042 else if (element == EL_MOLE)
8044 Feld[x][y] = EL_SAND;
8046 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8048 else if (element == EL_QUICKSAND_FILLING)
8050 element = Feld[newx][newy] = get_next_element(element);
8051 Store[newx][newy] = Store[x][y];
8053 else if (element == EL_QUICKSAND_EMPTYING)
8055 Feld[x][y] = get_next_element(element);
8056 element = Feld[newx][newy] = Store[x][y];
8058 else if (element == EL_QUICKSAND_FAST_FILLING)
8060 element = Feld[newx][newy] = get_next_element(element);
8061 Store[newx][newy] = Store[x][y];
8063 else if (element == EL_QUICKSAND_FAST_EMPTYING)
8065 Feld[x][y] = get_next_element(element);
8066 element = Feld[newx][newy] = Store[x][y];
8068 else if (element == EL_MAGIC_WALL_FILLING)
8070 element = Feld[newx][newy] = get_next_element(element);
8071 if (!game.magic_wall_active)
8072 element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
8073 Store[newx][newy] = Store[x][y];
8075 else if (element == EL_MAGIC_WALL_EMPTYING)
8077 Feld[x][y] = get_next_element(element);
8078 if (!game.magic_wall_active)
8079 Feld[x][y] = EL_MAGIC_WALL_DEAD;
8080 element = Feld[newx][newy] = Store[x][y];
8082 InitField(newx, newy, FALSE);
8084 else if (element == EL_BD_MAGIC_WALL_FILLING)
8086 element = Feld[newx][newy] = get_next_element(element);
8087 if (!game.magic_wall_active)
8088 element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
8089 Store[newx][newy] = Store[x][y];
8091 else if (element == EL_BD_MAGIC_WALL_EMPTYING)
8093 Feld[x][y] = get_next_element(element);
8094 if (!game.magic_wall_active)
8095 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
8096 element = Feld[newx][newy] = Store[x][y];
8098 InitField(newx, newy, FALSE);
8100 else if (element == EL_DC_MAGIC_WALL_FILLING)
8102 element = Feld[newx][newy] = get_next_element(element);
8103 if (!game.magic_wall_active)
8104 element = Feld[newx][newy] = EL_DC_MAGIC_WALL_DEAD;
8105 Store[newx][newy] = Store[x][y];
8107 else if (element == EL_DC_MAGIC_WALL_EMPTYING)
8109 Feld[x][y] = get_next_element(element);
8110 if (!game.magic_wall_active)
8111 Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
8112 element = Feld[newx][newy] = Store[x][y];
8114 InitField(newx, newy, FALSE);
8116 else if (element == EL_AMOEBA_DROPPING)
8118 Feld[x][y] = get_next_element(element);
8119 element = Feld[newx][newy] = Store[x][y];
8121 else if (element == EL_SOKOBAN_OBJECT)
8124 Feld[x][y] = Back[x][y];
8126 if (Back[newx][newy])
8127 Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
8129 Back[x][y] = Back[newx][newy] = 0;
8132 Store[x][y] = EL_EMPTY;
8137 MovDelay[newx][newy] = 0;
8139 if (CAN_CHANGE_OR_HAS_ACTION(element))
8141 /* copy element change control values to new field */
8142 ChangeDelay[newx][newy] = ChangeDelay[x][y];
8143 ChangePage[newx][newy] = ChangePage[x][y];
8144 ChangeCount[newx][newy] = ChangeCount[x][y];
8145 ChangeEvent[newx][newy] = ChangeEvent[x][y];
8148 CustomValue[newx][newy] = CustomValue[x][y];
8150 ChangeDelay[x][y] = 0;
8151 ChangePage[x][y] = -1;
8152 ChangeCount[x][y] = 0;
8153 ChangeEvent[x][y] = -1;
8155 CustomValue[x][y] = 0;
8157 /* copy animation control values to new field */
8158 GfxFrame[newx][newy] = GfxFrame[x][y];
8159 GfxRandom[newx][newy] = GfxRandom[x][y]; /* keep same random value */
8160 GfxAction[newx][newy] = GfxAction[x][y]; /* keep action one frame */
8161 GfxDir[newx][newy] = GfxDir[x][y]; /* keep element direction */
8163 Pushed[x][y] = Pushed[newx][newy] = FALSE;
8165 /* some elements can leave other elements behind after moving */
8166 if (ei->move_leave_element != EL_EMPTY &&
8167 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
8168 (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
8170 int move_leave_element = ei->move_leave_element;
8172 /* this makes it possible to leave the removed element again */
8173 if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
8174 move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
8176 Feld[x][y] = move_leave_element;
8178 if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
8179 MovDir[x][y] = direction;
8181 InitField(x, y, FALSE);
8183 if (GFX_CRUMBLED(Feld[x][y]))
8184 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8186 if (ELEM_IS_PLAYER(move_leave_element))
8187 RelocatePlayer(x, y, move_leave_element);
8190 /* do this after checking for left-behind element */
8191 ResetGfxAnimation(x, y); /* reset animation values for old field */
8193 if (!CAN_MOVE(element) ||
8194 (CAN_FALL(element) && direction == MV_DOWN &&
8195 (element == EL_SPRING ||
8196 element_info[element].move_pattern == MV_WHEN_PUSHED ||
8197 element_info[element].move_pattern == MV_WHEN_DROPPED)))
8198 GfxDir[x][y] = MovDir[newx][newy] = 0;
8200 TEST_DrawLevelField(x, y);
8201 TEST_DrawLevelField(newx, newy);
8203 Stop[newx][newy] = TRUE; /* ignore this element until the next frame */
8205 /* prevent pushed element from moving on in pushed direction */
8206 if (pushed_by_player && CAN_MOVE(element) &&
8207 element_info[element].move_pattern & MV_ANY_DIRECTION &&
8208 !(element_info[element].move_pattern & direction))
8209 TurnRound(newx, newy);
8211 /* prevent elements on conveyor belt from moving on in last direction */
8212 if (pushed_by_conveyor && CAN_FALL(element) &&
8213 direction & MV_HORIZONTAL)
8214 MovDir[newx][newy] = 0;
8216 if (!pushed_by_player)
8218 int nextx = newx + dx, nexty = newy + dy;
8219 boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
8221 WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
8223 if (CAN_FALL(element) && direction == MV_DOWN)
8224 WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
8226 if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
8227 CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
8229 if (CAN_FALL(element) && direction == MV_DOWN && check_collision_again)
8230 CheckImpact[newx][newy] = CHECK_DELAY_IMPACT;
8233 if (DONT_TOUCH(element)) /* object may be nasty to player or others */
8235 TestIfBadThingTouchesPlayer(newx, newy);
8236 TestIfBadThingTouchesFriend(newx, newy);
8238 if (!IS_CUSTOM_ELEMENT(element))
8239 TestIfBadThingTouchesOtherBadThing(newx, newy);
8241 else if (element == EL_PENGUIN)
8242 TestIfFriendTouchesBadThing(newx, newy);
8244 if (DONT_GET_HIT_BY(element))
8246 TestIfGoodThingGetsHitByBadThing(newx, newy, direction);
8249 /* give the player one last chance (one more frame) to move away */
8250 if (CAN_FALL(element) && direction == MV_DOWN &&
8251 (last_line || (!IS_FREE(x, newy + 1) &&
8252 (!IS_PLAYER(x, newy + 1) ||
8253 game.engine_version < VERSION_IDENT(3,1,1,0)))))
8256 if (pushed_by_player && !game.use_change_when_pushing_bug)
8258 int push_side = MV_DIR_OPPOSITE(direction);
8259 struct PlayerInfo *player = PLAYERINFO(x, y);
8261 CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
8262 player->index_bit, push_side);
8263 CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
8264 player->index_bit, push_side);
8267 if (element == EL_EMC_ANDROID && pushed_by_player) /* make another move */
8268 MovDelay[newx][newy] = 1;
8270 CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
8272 TestIfElementTouchesCustomElement(x, y); /* empty or new element */
8273 TestIfElementHitsCustomElement(newx, newy, direction);
8274 TestIfPlayerTouchesCustomElement(newx, newy);
8275 TestIfElementTouchesCustomElement(newx, newy);
8277 if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
8278 IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
8279 CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
8280 MV_DIR_OPPOSITE(direction));
8283 int AmoebeNachbarNr(int ax, int ay)
8286 int element = Feld[ax][ay];
8288 static int xy[4][2] =
8296 for (i = 0; i < NUM_DIRECTIONS; i++)
8298 int x = ax + xy[i][0];
8299 int y = ay + xy[i][1];
8301 if (!IN_LEV_FIELD(x, y))
8304 if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
8305 group_nr = AmoebaNr[x][y];
8311 void AmoebenVereinigen(int ax, int ay)
8313 int i, x, y, xx, yy;
8314 int new_group_nr = AmoebaNr[ax][ay];
8315 static int xy[4][2] =
8323 if (new_group_nr == 0)
8326 for (i = 0; i < NUM_DIRECTIONS; i++)
8331 if (!IN_LEV_FIELD(x, y))
8334 if ((Feld[x][y] == EL_AMOEBA_FULL ||
8335 Feld[x][y] == EL_BD_AMOEBA ||
8336 Feld[x][y] == EL_AMOEBA_DEAD) &&
8337 AmoebaNr[x][y] != new_group_nr)
8339 int old_group_nr = AmoebaNr[x][y];
8341 if (old_group_nr == 0)
8344 AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
8345 AmoebaCnt[old_group_nr] = 0;
8346 AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
8347 AmoebaCnt2[old_group_nr] = 0;
8349 SCAN_PLAYFIELD(xx, yy)
8351 if (AmoebaNr[xx][yy] == old_group_nr)
8352 AmoebaNr[xx][yy] = new_group_nr;
8358 void AmoebeUmwandeln(int ax, int ay)
8362 if (Feld[ax][ay] == EL_AMOEBA_DEAD)
8364 int group_nr = AmoebaNr[ax][ay];
8369 printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
8370 printf("AmoebeUmwandeln(): This should never happen!\n");
8375 SCAN_PLAYFIELD(x, y)
8377 if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
8380 Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
8384 PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
8385 SND_AMOEBA_TURNING_TO_GEM :
8386 SND_AMOEBA_TURNING_TO_ROCK));
8391 static int xy[4][2] =
8399 for (i = 0; i < NUM_DIRECTIONS; i++)
8404 if (!IN_LEV_FIELD(x, y))
8407 if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
8409 PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
8410 SND_AMOEBA_TURNING_TO_GEM :
8411 SND_AMOEBA_TURNING_TO_ROCK));
8418 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
8421 int group_nr = AmoebaNr[ax][ay];
8422 boolean done = FALSE;
8427 printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
8428 printf("AmoebeUmwandelnBD(): This should never happen!\n");
8433 SCAN_PLAYFIELD(x, y)
8435 if (AmoebaNr[x][y] == group_nr &&
8436 (Feld[x][y] == EL_AMOEBA_DEAD ||
8437 Feld[x][y] == EL_BD_AMOEBA ||
8438 Feld[x][y] == EL_AMOEBA_GROWING))
8441 Feld[x][y] = new_element;
8442 InitField(x, y, FALSE);
8443 TEST_DrawLevelField(x, y);
8449 PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
8450 SND_BD_AMOEBA_TURNING_TO_ROCK :
8451 SND_BD_AMOEBA_TURNING_TO_GEM));
8454 void AmoebeWaechst(int x, int y)
8456 static unsigned int sound_delay = 0;
8457 static unsigned int sound_delay_value = 0;
8459 if (!MovDelay[x][y]) /* start new growing cycle */
8463 if (DelayReached(&sound_delay, sound_delay_value))
8465 PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
8466 sound_delay_value = 30;
8470 if (MovDelay[x][y]) /* wait some time before growing bigger */
8473 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8475 int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
8476 6 - MovDelay[x][y]);
8478 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
8481 if (!MovDelay[x][y])
8483 Feld[x][y] = Store[x][y];
8485 TEST_DrawLevelField(x, y);
8490 void AmoebaDisappearing(int x, int y)
8492 static unsigned int sound_delay = 0;
8493 static unsigned int sound_delay_value = 0;
8495 if (!MovDelay[x][y]) /* start new shrinking cycle */
8499 if (DelayReached(&sound_delay, sound_delay_value))
8500 sound_delay_value = 30;
8503 if (MovDelay[x][y]) /* wait some time before shrinking */
8506 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8508 int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
8509 6 - MovDelay[x][y]);
8511 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
8514 if (!MovDelay[x][y])
8516 Feld[x][y] = EL_EMPTY;
8517 TEST_DrawLevelField(x, y);
8519 /* don't let mole enter this field in this cycle;
8520 (give priority to objects falling to this field from above) */
8526 void AmoebeAbleger(int ax, int ay)
8529 int element = Feld[ax][ay];
8530 int graphic = el2img(element);
8531 int newax = ax, neway = ay;
8532 boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
8533 static int xy[4][2] =
8541 if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
8543 Feld[ax][ay] = EL_AMOEBA_DEAD;
8544 TEST_DrawLevelField(ax, ay);
8548 if (IS_ANIMATED(graphic))
8549 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
8551 if (!MovDelay[ax][ay]) /* start making new amoeba field */
8552 MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
8554 if (MovDelay[ax][ay]) /* wait some time before making new amoeba */
8557 if (MovDelay[ax][ay])
8561 if (can_drop) /* EL_AMOEBA_WET or EL_EMC_DRIPPER */
8564 int x = ax + xy[start][0];
8565 int y = ay + xy[start][1];
8567 if (!IN_LEV_FIELD(x, y))
8570 if (IS_FREE(x, y) ||
8571 CAN_GROW_INTO(Feld[x][y]) ||
8572 Feld[x][y] == EL_QUICKSAND_EMPTY ||
8573 Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
8579 if (newax == ax && neway == ay)
8582 else /* normal or "filled" (BD style) amoeba */
8585 boolean waiting_for_player = FALSE;
8587 for (i = 0; i < NUM_DIRECTIONS; i++)
8589 int j = (start + i) % 4;
8590 int x = ax + xy[j][0];
8591 int y = ay + xy[j][1];
8593 if (!IN_LEV_FIELD(x, y))
8596 if (IS_FREE(x, y) ||
8597 CAN_GROW_INTO(Feld[x][y]) ||
8598 Feld[x][y] == EL_QUICKSAND_EMPTY ||
8599 Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
8605 else if (IS_PLAYER(x, y))
8606 waiting_for_player = TRUE;
8609 if (newax == ax && neway == ay) /* amoeba cannot grow */
8611 if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
8613 Feld[ax][ay] = EL_AMOEBA_DEAD;
8614 TEST_DrawLevelField(ax, ay);
8615 AmoebaCnt[AmoebaNr[ax][ay]]--;
8617 if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0) /* amoeba is completely dead */
8619 if (element == EL_AMOEBA_FULL)
8620 AmoebeUmwandeln(ax, ay);
8621 else if (element == EL_BD_AMOEBA)
8622 AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
8627 else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
8629 /* amoeba gets larger by growing in some direction */
8631 int new_group_nr = AmoebaNr[ax][ay];
8634 if (new_group_nr == 0)
8636 printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
8637 printf("AmoebeAbleger(): This should never happen!\n");
8642 AmoebaNr[newax][neway] = new_group_nr;
8643 AmoebaCnt[new_group_nr]++;
8644 AmoebaCnt2[new_group_nr]++;
8646 /* if amoeba touches other amoeba(s) after growing, unify them */
8647 AmoebenVereinigen(newax, neway);
8649 if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
8651 AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
8657 if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
8658 (neway == lev_fieldy - 1 && newax != ax))
8660 Feld[newax][neway] = EL_AMOEBA_GROWING; /* creation of new amoeba */
8661 Store[newax][neway] = element;
8663 else if (neway == ay || element == EL_EMC_DRIPPER)
8665 Feld[newax][neway] = EL_AMOEBA_DROP; /* drop left/right of amoeba */
8667 PlayLevelSoundAction(newax, neway, ACTION_GROWING);
8671 InitMovingField(ax, ay, MV_DOWN); /* drop dripping from amoeba */
8672 Feld[ax][ay] = EL_AMOEBA_DROPPING;
8673 Store[ax][ay] = EL_AMOEBA_DROP;
8674 ContinueMoving(ax, ay);
8678 TEST_DrawLevelField(newax, neway);
8681 void Life(int ax, int ay)
8685 int element = Feld[ax][ay];
8686 int graphic = el2img(element);
8687 int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
8689 boolean changed = FALSE;
8691 if (IS_ANIMATED(graphic))
8692 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
8697 if (!MovDelay[ax][ay]) /* start new "game of life" cycle */
8698 MovDelay[ax][ay] = life_time;
8700 if (MovDelay[ax][ay]) /* wait some time before next cycle */
8703 if (MovDelay[ax][ay])
8707 for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
8709 int xx = ax+x1, yy = ay+y1;
8712 if (!IN_LEV_FIELD(xx, yy))
8715 for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
8717 int x = xx+x2, y = yy+y2;
8719 if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
8722 if (((Feld[x][y] == element ||
8723 (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
8725 (IS_FREE(x, y) && Stop[x][y]))
8729 if (xx == ax && yy == ay) /* field in the middle */
8731 if (nachbarn < life_parameter[0] ||
8732 nachbarn > life_parameter[1])
8734 Feld[xx][yy] = EL_EMPTY;
8736 TEST_DrawLevelField(xx, yy);
8737 Stop[xx][yy] = TRUE;
8741 else if (IS_FREE(xx, yy) || CAN_GROW_INTO(Feld[xx][yy]))
8742 { /* free border field */
8743 if (nachbarn >= life_parameter[2] &&
8744 nachbarn <= life_parameter[3])
8746 Feld[xx][yy] = element;
8747 MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
8749 TEST_DrawLevelField(xx, yy);
8750 Stop[xx][yy] = TRUE;
8757 PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
8758 SND_GAME_OF_LIFE_GROWING);
8761 static void InitRobotWheel(int x, int y)
8763 ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
8766 static void RunRobotWheel(int x, int y)
8768 PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
8771 static void StopRobotWheel(int x, int y)
8773 if (ZX == x && ZY == y)
8777 game.robot_wheel_active = FALSE;
8781 static void InitTimegateWheel(int x, int y)
8783 ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
8786 static void RunTimegateWheel(int x, int y)
8788 PlayLevelSound(x, y, SND_CLASS_TIMEGATE_SWITCH_ACTIVE);
8791 static void InitMagicBallDelay(int x, int y)
8793 ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
8796 static void ActivateMagicBall(int bx, int by)
8800 if (level.ball_random)
8802 int pos_border = RND(8); /* select one of the eight border elements */
8803 int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
8804 int xx = pos_content % 3;
8805 int yy = pos_content / 3;
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 for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
8817 int xx = x - bx + 1;
8818 int yy = y - by + 1;
8820 if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
8821 CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
8825 game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
8828 void CheckExit(int x, int y)
8830 if (local_player->gems_still_needed > 0 ||
8831 local_player->sokobanfields_still_needed > 0 ||
8832 local_player->lights_still_needed > 0)
8834 int element = Feld[x][y];
8835 int graphic = el2img(element);
8837 if (IS_ANIMATED(graphic))
8838 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8843 if (AllPlayersGone) /* do not re-open exit door closed after last player */
8846 Feld[x][y] = EL_EXIT_OPENING;
8848 PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
8851 void CheckExitEM(int x, int y)
8853 if (local_player->gems_still_needed > 0 ||
8854 local_player->sokobanfields_still_needed > 0 ||
8855 local_player->lights_still_needed > 0)
8857 int element = Feld[x][y];
8858 int graphic = el2img(element);
8860 if (IS_ANIMATED(graphic))
8861 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8866 if (AllPlayersGone) /* do not re-open exit door closed after last player */
8869 Feld[x][y] = EL_EM_EXIT_OPENING;
8871 PlayLevelSoundNearest(x, y, SND_CLASS_EM_EXIT_OPENING);
8874 void CheckExitSteel(int x, int y)
8876 if (local_player->gems_still_needed > 0 ||
8877 local_player->sokobanfields_still_needed > 0 ||
8878 local_player->lights_still_needed > 0)
8880 int element = Feld[x][y];
8881 int graphic = el2img(element);
8883 if (IS_ANIMATED(graphic))
8884 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8889 if (AllPlayersGone) /* do not re-open exit door closed after last player */
8892 Feld[x][y] = EL_STEEL_EXIT_OPENING;
8894 PlayLevelSoundNearest(x, y, SND_CLASS_STEEL_EXIT_OPENING);
8897 void CheckExitSteelEM(int x, int y)
8899 if (local_player->gems_still_needed > 0 ||
8900 local_player->sokobanfields_still_needed > 0 ||
8901 local_player->lights_still_needed > 0)
8903 int element = Feld[x][y];
8904 int graphic = el2img(element);
8906 if (IS_ANIMATED(graphic))
8907 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8912 if (AllPlayersGone) /* do not re-open exit door closed after last player */
8915 Feld[x][y] = EL_EM_STEEL_EXIT_OPENING;
8917 PlayLevelSoundNearest(x, y, SND_CLASS_EM_STEEL_EXIT_OPENING);
8920 void CheckExitSP(int x, int y)
8922 if (local_player->gems_still_needed > 0)
8924 int element = Feld[x][y];
8925 int graphic = el2img(element);
8927 if (IS_ANIMATED(graphic))
8928 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8933 if (AllPlayersGone) /* do not re-open exit door closed after last player */
8936 Feld[x][y] = EL_SP_EXIT_OPENING;
8938 PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
8941 static void CloseAllOpenTimegates()
8945 SCAN_PLAYFIELD(x, y)
8947 int element = Feld[x][y];
8949 if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
8951 Feld[x][y] = EL_TIMEGATE_CLOSING;
8953 PlayLevelSoundAction(x, y, ACTION_CLOSING);
8958 void DrawTwinkleOnField(int x, int y)
8960 if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
8963 if (Feld[x][y] == EL_BD_DIAMOND)
8966 if (MovDelay[x][y] == 0) /* next animation frame */
8967 MovDelay[x][y] = 11 * !GetSimpleRandom(500);
8969 if (MovDelay[x][y] != 0) /* wait some time before next frame */
8973 DrawLevelElementAnimation(x, y, Feld[x][y]);
8975 if (MovDelay[x][y] != 0)
8977 int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
8978 10 - MovDelay[x][y]);
8980 DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
8985 void MauerWaechst(int x, int y)
8989 if (!MovDelay[x][y]) /* next animation frame */
8990 MovDelay[x][y] = 3 * delay;
8992 if (MovDelay[x][y]) /* wait some time before next frame */
8996 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8998 int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
8999 int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
9001 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
9004 if (!MovDelay[x][y])
9006 if (MovDir[x][y] == MV_LEFT)
9008 if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
9009 TEST_DrawLevelField(x - 1, y);
9011 else if (MovDir[x][y] == MV_RIGHT)
9013 if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
9014 TEST_DrawLevelField(x + 1, y);
9016 else if (MovDir[x][y] == MV_UP)
9018 if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
9019 TEST_DrawLevelField(x, y - 1);
9023 if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
9024 TEST_DrawLevelField(x, y + 1);
9027 Feld[x][y] = Store[x][y];
9029 GfxDir[x][y] = MovDir[x][y] = MV_NONE;
9030 TEST_DrawLevelField(x, y);
9035 void MauerAbleger(int ax, int ay)
9037 int element = Feld[ax][ay];
9038 int graphic = el2img(element);
9039 boolean oben_frei = FALSE, unten_frei = FALSE;
9040 boolean links_frei = FALSE, rechts_frei = FALSE;
9041 boolean oben_massiv = FALSE, unten_massiv = FALSE;
9042 boolean links_massiv = FALSE, rechts_massiv = FALSE;
9043 boolean new_wall = FALSE;
9045 if (IS_ANIMATED(graphic))
9046 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9048 if (!MovDelay[ax][ay]) /* start building new wall */
9049 MovDelay[ax][ay] = 6;
9051 if (MovDelay[ax][ay]) /* wait some time before building new wall */
9054 if (MovDelay[ax][ay])
9058 if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9060 if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9062 if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9064 if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9067 if (element == EL_EXPANDABLE_WALL_VERTICAL ||
9068 element == EL_EXPANDABLE_WALL_ANY)
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_UP;
9075 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9076 DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9077 IMG_EXPANDABLE_WALL_GROWING_UP, 0);
9082 Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
9083 Store[ax][ay+1] = element;
9084 GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9085 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9086 DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9087 IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
9092 if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9093 element == EL_EXPANDABLE_WALL_ANY ||
9094 element == EL_EXPANDABLE_WALL ||
9095 element == EL_BD_EXPANDABLE_WALL)
9099 Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
9100 Store[ax-1][ay] = element;
9101 GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9102 if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9103 DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9104 IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
9110 Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
9111 Store[ax+1][ay] = element;
9112 GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9113 if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9114 DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9115 IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
9120 if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
9121 TEST_DrawLevelField(ax, ay);
9123 if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
9125 if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
9126 unten_massiv = TRUE;
9127 if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
9128 links_massiv = TRUE;
9129 if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
9130 rechts_massiv = TRUE;
9132 if (((oben_massiv && unten_massiv) ||
9133 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9134 element == EL_EXPANDABLE_WALL) &&
9135 ((links_massiv && rechts_massiv) ||
9136 element == EL_EXPANDABLE_WALL_VERTICAL))
9137 Feld[ax][ay] = EL_WALL;
9140 PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9143 void MauerAblegerStahl(int ax, int ay)
9145 int element = Feld[ax][ay];
9146 int graphic = el2img(element);
9147 boolean oben_frei = FALSE, unten_frei = FALSE;
9148 boolean links_frei = FALSE, rechts_frei = FALSE;
9149 boolean oben_massiv = FALSE, unten_massiv = FALSE;
9150 boolean links_massiv = FALSE, rechts_massiv = FALSE;
9151 boolean new_wall = FALSE;
9153 if (IS_ANIMATED(graphic))
9154 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9156 if (!MovDelay[ax][ay]) /* start building new wall */
9157 MovDelay[ax][ay] = 6;
9159 if (MovDelay[ax][ay]) /* wait some time before building new wall */
9162 if (MovDelay[ax][ay])
9166 if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9168 if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9170 if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9172 if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9175 if (element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
9176 element == EL_EXPANDABLE_STEELWALL_ANY)
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_UP;
9183 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9184 DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9185 IMG_EXPANDABLE_STEELWALL_GROWING_UP, 0);
9190 Feld[ax][ay+1] = EL_EXPANDABLE_STEELWALL_GROWING;
9191 Store[ax][ay+1] = element;
9192 GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9193 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9194 DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9195 IMG_EXPANDABLE_STEELWALL_GROWING_DOWN, 0);
9200 if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
9201 element == EL_EXPANDABLE_STEELWALL_ANY)
9205 Feld[ax-1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9206 Store[ax-1][ay] = element;
9207 GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9208 if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9209 DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9210 IMG_EXPANDABLE_STEELWALL_GROWING_LEFT, 0);
9216 Feld[ax+1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9217 Store[ax+1][ay] = element;
9218 GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9219 if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9220 DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9221 IMG_EXPANDABLE_STEELWALL_GROWING_RIGHT, 0);
9226 if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
9228 if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
9229 unten_massiv = TRUE;
9230 if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
9231 links_massiv = TRUE;
9232 if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
9233 rechts_massiv = TRUE;
9235 if (((oben_massiv && unten_massiv) ||
9236 element == EL_EXPANDABLE_STEELWALL_HORIZONTAL) &&
9237 ((links_massiv && rechts_massiv) ||
9238 element == EL_EXPANDABLE_STEELWALL_VERTICAL))
9239 Feld[ax][ay] = EL_STEELWALL;
9242 PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9245 void CheckForDragon(int x, int y)
9248 boolean dragon_found = FALSE;
9249 static int xy[4][2] =
9257 for (i = 0; i < NUM_DIRECTIONS; i++)
9259 for (j = 0; j < 4; j++)
9261 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9263 if (IN_LEV_FIELD(xx, yy) &&
9264 (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
9266 if (Feld[xx][yy] == EL_DRAGON)
9267 dragon_found = TRUE;
9276 for (i = 0; i < NUM_DIRECTIONS; i++)
9278 for (j = 0; j < 3; j++)
9280 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9282 if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
9284 Feld[xx][yy] = EL_EMPTY;
9285 TEST_DrawLevelField(xx, yy);
9294 static void InitBuggyBase(int x, int y)
9296 int element = Feld[x][y];
9297 int activating_delay = FRAMES_PER_SECOND / 4;
9300 (element == EL_SP_BUGGY_BASE ?
9301 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
9302 element == EL_SP_BUGGY_BASE_ACTIVATING ?
9304 element == EL_SP_BUGGY_BASE_ACTIVE ?
9305 1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
9308 static void WarnBuggyBase(int x, int y)
9311 static int xy[4][2] =
9319 for (i = 0; i < NUM_DIRECTIONS; i++)
9321 int xx = x + xy[i][0];
9322 int yy = y + xy[i][1];
9324 if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
9326 PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
9333 static void InitTrap(int x, int y)
9335 ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
9338 static void ActivateTrap(int x, int y)
9340 PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
9343 static void ChangeActiveTrap(int x, int y)
9345 int graphic = IMG_TRAP_ACTIVE;
9347 /* if new animation frame was drawn, correct crumbled sand border */
9348 if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
9349 TEST_DrawLevelFieldCrumbled(x, y);
9352 static int getSpecialActionElement(int element, int number, int base_element)
9354 return (element != EL_EMPTY ? element :
9355 number != -1 ? base_element + number - 1 :
9359 static int getModifiedActionNumber(int value_old, int operator, int operand,
9360 int value_min, int value_max)
9362 int value_new = (operator == CA_MODE_SET ? operand :
9363 operator == CA_MODE_ADD ? value_old + operand :
9364 operator == CA_MODE_SUBTRACT ? value_old - operand :
9365 operator == CA_MODE_MULTIPLY ? value_old * operand :
9366 operator == CA_MODE_DIVIDE ? value_old / MAX(1, operand) :
9367 operator == CA_MODE_MODULO ? value_old % MAX(1, operand) :
9370 return (value_new < value_min ? value_min :
9371 value_new > value_max ? value_max :
9375 static void ExecuteCustomElementAction(int x, int y, int element, int page)
9377 struct ElementInfo *ei = &element_info[element];
9378 struct ElementChangeInfo *change = &ei->change_page[page];
9379 int target_element = change->target_element;
9380 int action_type = change->action_type;
9381 int action_mode = change->action_mode;
9382 int action_arg = change->action_arg;
9383 int action_element = change->action_element;
9386 if (!change->has_action)
9389 /* ---------- determine action paramater values -------------------------- */
9391 int level_time_value =
9392 (level.time > 0 ? TimeLeft :
9395 int action_arg_element_raw =
9396 (action_arg == CA_ARG_PLAYER_TRIGGER ? change->actual_trigger_player :
9397 action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
9398 action_arg == CA_ARG_ELEMENT_TARGET ? change->target_element :
9399 action_arg == CA_ARG_ELEMENT_ACTION ? change->action_element :
9400 action_arg == CA_ARG_INVENTORY_RM_TRIGGER ? change->actual_trigger_element:
9401 action_arg == CA_ARG_INVENTORY_RM_TARGET ? change->target_element :
9402 action_arg == CA_ARG_INVENTORY_RM_ACTION ? change->action_element :
9404 int action_arg_element = GetElementFromGroupElement(action_arg_element_raw);
9406 int action_arg_direction =
9407 (action_arg >= CA_ARG_DIRECTION_LEFT &&
9408 action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
9409 action_arg == CA_ARG_DIRECTION_TRIGGER ?
9410 change->actual_trigger_side :
9411 action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
9412 MV_DIR_OPPOSITE(change->actual_trigger_side) :
9415 int action_arg_number_min =
9416 (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
9419 int action_arg_number_max =
9420 (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
9421 action_type == CA_SET_LEVEL_GEMS ? 999 :
9422 action_type == CA_SET_LEVEL_TIME ? 9999 :
9423 action_type == CA_SET_LEVEL_SCORE ? 99999 :
9424 action_type == CA_SET_CE_VALUE ? 9999 :
9425 action_type == CA_SET_CE_SCORE ? 9999 :
9428 int action_arg_number_reset =
9429 (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize[0] :
9430 action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
9431 action_type == CA_SET_LEVEL_TIME ? level.time :
9432 action_type == CA_SET_LEVEL_SCORE ? 0 :
9433 action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
9434 action_type == CA_SET_CE_SCORE ? 0 :
9437 int action_arg_number =
9438 (action_arg <= CA_ARG_MAX ? action_arg :
9439 action_arg >= CA_ARG_SPEED_NOT_MOVING &&
9440 action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
9441 action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
9442 action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
9443 action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
9444 action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
9445 action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
9446 action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
9447 action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
9448 action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
9449 action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? local_player->gems_still_needed :
9450 action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? local_player->score :
9451 action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
9452 action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
9453 action_arg == CA_ARG_ELEMENT_CV_ACTION ? GET_NEW_CE_VALUE(action_element):
9454 action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
9455 action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
9456 action_arg == CA_ARG_ELEMENT_CS_ACTION ? GET_CE_SCORE(action_element) :
9457 action_arg == CA_ARG_ELEMENT_NR_TARGET ? change->target_element :
9458 action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
9459 action_arg == CA_ARG_ELEMENT_NR_ACTION ? change->action_element :
9462 int action_arg_number_old =
9463 (action_type == CA_SET_LEVEL_GEMS ? local_player->gems_still_needed :
9464 action_type == CA_SET_LEVEL_TIME ? TimeLeft :
9465 action_type == CA_SET_LEVEL_SCORE ? local_player->score :
9466 action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
9467 action_type == CA_SET_CE_SCORE ? ei->collect_score :
9470 int action_arg_number_new =
9471 getModifiedActionNumber(action_arg_number_old,
9472 action_mode, action_arg_number,
9473 action_arg_number_min, action_arg_number_max);
9475 int trigger_player_bits =
9476 (change->actual_trigger_player_bits != CH_PLAYER_NONE ?
9477 change->actual_trigger_player_bits : change->trigger_player);
9479 int action_arg_player_bits =
9480 (action_arg >= CA_ARG_PLAYER_1 &&
9481 action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
9482 action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
9483 action_arg == CA_ARG_PLAYER_ACTION ? 1 << GET_PLAYER_NR(action_element) :
9486 /* ---------- execute action -------------------------------------------- */
9488 switch (action_type)
9495 /* ---------- level actions ------------------------------------------- */
9497 case CA_RESTART_LEVEL:
9499 game.restart_level = TRUE;
9504 case CA_SHOW_ENVELOPE:
9506 int element = getSpecialActionElement(action_arg_element,
9507 action_arg_number, EL_ENVELOPE_1);
9509 if (IS_ENVELOPE(element))
9510 local_player->show_envelope = element;
9515 case CA_SET_LEVEL_TIME:
9517 if (level.time > 0) /* only modify limited time value */
9519 TimeLeft = action_arg_number_new;
9521 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
9523 DisplayGameControlValues();
9525 if (!TimeLeft && setup.time_limit)
9526 for (i = 0; i < MAX_PLAYERS; i++)
9527 KillPlayer(&stored_player[i]);
9533 case CA_SET_LEVEL_SCORE:
9535 local_player->score = action_arg_number_new;
9537 game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
9539 DisplayGameControlValues();
9544 case CA_SET_LEVEL_GEMS:
9546 local_player->gems_still_needed = action_arg_number_new;
9548 game_panel_controls[GAME_PANEL_GEMS].value =
9549 local_player->gems_still_needed;
9551 DisplayGameControlValues();
9556 case CA_SET_LEVEL_WIND:
9558 game.wind_direction = action_arg_direction;
9563 case CA_SET_LEVEL_RANDOM_SEED:
9565 /* ensure that setting a new random seed while playing is predictable */
9566 InitRND(action_arg_number_new ? action_arg_number_new : RND(1000000) + 1);
9571 /* ---------- player actions ------------------------------------------ */
9573 case CA_MOVE_PLAYER:
9575 /* automatically move to the next field in specified direction */
9576 for (i = 0; i < MAX_PLAYERS; i++)
9577 if (trigger_player_bits & (1 << i))
9578 stored_player[i].programmed_action = action_arg_direction;
9583 case CA_EXIT_PLAYER:
9585 for (i = 0; i < MAX_PLAYERS; i++)
9586 if (action_arg_player_bits & (1 << i))
9587 PlayerWins(&stored_player[i]);
9592 case CA_KILL_PLAYER:
9594 for (i = 0; i < MAX_PLAYERS; i++)
9595 if (action_arg_player_bits & (1 << i))
9596 KillPlayer(&stored_player[i]);
9601 case CA_SET_PLAYER_KEYS:
9603 int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
9604 int element = getSpecialActionElement(action_arg_element,
9605 action_arg_number, EL_KEY_1);
9607 if (IS_KEY(element))
9609 for (i = 0; i < MAX_PLAYERS; i++)
9611 if (trigger_player_bits & (1 << i))
9613 stored_player[i].key[KEY_NR(element)] = key_state;
9615 DrawGameDoorValues();
9623 case CA_SET_PLAYER_SPEED:
9625 for (i = 0; i < MAX_PLAYERS; i++)
9627 if (trigger_player_bits & (1 << i))
9629 int move_stepsize = TILEX / stored_player[i].move_delay_value;
9631 if (action_arg == CA_ARG_SPEED_FASTER &&
9632 stored_player[i].cannot_move)
9634 action_arg_number = STEPSIZE_VERY_SLOW;
9636 else if (action_arg == CA_ARG_SPEED_SLOWER ||
9637 action_arg == CA_ARG_SPEED_FASTER)
9639 action_arg_number = 2;
9640 action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
9643 else if (action_arg == CA_ARG_NUMBER_RESET)
9645 action_arg_number = level.initial_player_stepsize[i];
9649 getModifiedActionNumber(move_stepsize,
9652 action_arg_number_min,
9653 action_arg_number_max);
9655 SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
9662 case CA_SET_PLAYER_SHIELD:
9664 for (i = 0; i < MAX_PLAYERS; i++)
9666 if (trigger_player_bits & (1 << i))
9668 if (action_arg == CA_ARG_SHIELD_OFF)
9670 stored_player[i].shield_normal_time_left = 0;
9671 stored_player[i].shield_deadly_time_left = 0;
9673 else if (action_arg == CA_ARG_SHIELD_NORMAL)
9675 stored_player[i].shield_normal_time_left = 999999;
9677 else if (action_arg == CA_ARG_SHIELD_DEADLY)
9679 stored_player[i].shield_normal_time_left = 999999;
9680 stored_player[i].shield_deadly_time_left = 999999;
9688 case CA_SET_PLAYER_GRAVITY:
9690 for (i = 0; i < MAX_PLAYERS; i++)
9692 if (trigger_player_bits & (1 << i))
9694 stored_player[i].gravity =
9695 (action_arg == CA_ARG_GRAVITY_OFF ? FALSE :
9696 action_arg == CA_ARG_GRAVITY_ON ? TRUE :
9697 action_arg == CA_ARG_GRAVITY_TOGGLE ? !stored_player[i].gravity :
9698 stored_player[i].gravity);
9705 case CA_SET_PLAYER_ARTWORK:
9707 for (i = 0; i < MAX_PLAYERS; i++)
9709 if (trigger_player_bits & (1 << i))
9711 int artwork_element = action_arg_element;
9713 if (action_arg == CA_ARG_ELEMENT_RESET)
9715 (level.use_artwork_element[i] ? level.artwork_element[i] :
9716 stored_player[i].element_nr);
9718 if (stored_player[i].artwork_element != artwork_element)
9719 stored_player[i].Frame = 0;
9721 stored_player[i].artwork_element = artwork_element;
9723 SetPlayerWaiting(&stored_player[i], FALSE);
9725 /* set number of special actions for bored and sleeping animation */
9726 stored_player[i].num_special_action_bored =
9727 get_num_special_action(artwork_element,
9728 ACTION_BORING_1, ACTION_BORING_LAST);
9729 stored_player[i].num_special_action_sleeping =
9730 get_num_special_action(artwork_element,
9731 ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
9738 case CA_SET_PLAYER_INVENTORY:
9740 for (i = 0; i < MAX_PLAYERS; i++)
9742 struct PlayerInfo *player = &stored_player[i];
9745 if (trigger_player_bits & (1 << i))
9747 int inventory_element = action_arg_element;
9749 if (action_arg == CA_ARG_ELEMENT_TARGET ||
9750 action_arg == CA_ARG_ELEMENT_TRIGGER ||
9751 action_arg == CA_ARG_ELEMENT_ACTION)
9753 int element = inventory_element;
9754 int collect_count = element_info[element].collect_count_initial;
9756 if (!IS_CUSTOM_ELEMENT(element))
9759 if (collect_count == 0)
9760 player->inventory_infinite_element = element;
9762 for (k = 0; k < collect_count; k++)
9763 if (player->inventory_size < MAX_INVENTORY_SIZE)
9764 player->inventory_element[player->inventory_size++] =
9767 else if (action_arg == CA_ARG_INVENTORY_RM_TARGET ||
9768 action_arg == CA_ARG_INVENTORY_RM_TRIGGER ||
9769 action_arg == CA_ARG_INVENTORY_RM_ACTION)
9771 if (player->inventory_infinite_element != EL_UNDEFINED &&
9772 IS_EQUAL_OR_IN_GROUP(player->inventory_infinite_element,
9773 action_arg_element_raw))
9774 player->inventory_infinite_element = EL_UNDEFINED;
9776 for (k = 0, j = 0; j < player->inventory_size; j++)
9778 if (!IS_EQUAL_OR_IN_GROUP(player->inventory_element[j],
9779 action_arg_element_raw))
9780 player->inventory_element[k++] = player->inventory_element[j];
9783 player->inventory_size = k;
9785 else if (action_arg == CA_ARG_INVENTORY_RM_FIRST)
9787 if (player->inventory_size > 0)
9789 for (j = 0; j < player->inventory_size - 1; j++)
9790 player->inventory_element[j] = player->inventory_element[j + 1];
9792 player->inventory_size--;
9795 else if (action_arg == CA_ARG_INVENTORY_RM_LAST)
9797 if (player->inventory_size > 0)
9798 player->inventory_size--;
9800 else if (action_arg == CA_ARG_INVENTORY_RM_ALL)
9802 player->inventory_infinite_element = EL_UNDEFINED;
9803 player->inventory_size = 0;
9805 else if (action_arg == CA_ARG_INVENTORY_RESET)
9807 player->inventory_infinite_element = EL_UNDEFINED;
9808 player->inventory_size = 0;
9810 if (level.use_initial_inventory[i])
9812 for (j = 0; j < level.initial_inventory_size[i]; j++)
9814 int element = level.initial_inventory_content[i][j];
9815 int collect_count = element_info[element].collect_count_initial;
9817 if (!IS_CUSTOM_ELEMENT(element))
9820 if (collect_count == 0)
9821 player->inventory_infinite_element = element;
9823 for (k = 0; k < collect_count; k++)
9824 if (player->inventory_size < MAX_INVENTORY_SIZE)
9825 player->inventory_element[player->inventory_size++] =
9836 /* ---------- CE actions ---------------------------------------------- */
9838 case CA_SET_CE_VALUE:
9840 int last_ce_value = CustomValue[x][y];
9842 CustomValue[x][y] = action_arg_number_new;
9844 if (CustomValue[x][y] != last_ce_value)
9846 CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_CHANGES);
9847 CheckTriggeredElementChange(x, y, element, CE_VALUE_CHANGES_OF_X);
9849 if (CustomValue[x][y] == 0)
9851 CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
9852 CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
9859 case CA_SET_CE_SCORE:
9861 int last_ce_score = ei->collect_score;
9863 ei->collect_score = action_arg_number_new;
9865 if (ei->collect_score != last_ce_score)
9867 CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
9868 CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
9870 if (ei->collect_score == 0)
9874 CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
9875 CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
9878 This is a very special case that seems to be a mixture between
9879 CheckElementChange() and CheckTriggeredElementChange(): while
9880 the first one only affects single elements that are triggered
9881 directly, the second one affects multiple elements in the playfield
9882 that are triggered indirectly by another element. This is a third
9883 case: Changing the CE score always affects multiple identical CEs,
9884 so every affected CE must be checked, not only the single CE for
9885 which the CE score was changed in the first place (as every instance
9886 of that CE shares the same CE score, and therefore also can change)!
9888 SCAN_PLAYFIELD(xx, yy)
9890 if (Feld[xx][yy] == element)
9891 CheckElementChange(xx, yy, element, EL_UNDEFINED,
9892 CE_SCORE_GETS_ZERO);
9900 case CA_SET_CE_ARTWORK:
9902 int artwork_element = action_arg_element;
9903 boolean reset_frame = FALSE;
9906 if (action_arg == CA_ARG_ELEMENT_RESET)
9907 artwork_element = (ei->use_gfx_element ? ei->gfx_element_initial :
9910 if (ei->gfx_element != artwork_element)
9913 ei->gfx_element = artwork_element;
9915 SCAN_PLAYFIELD(xx, yy)
9917 if (Feld[xx][yy] == element)
9921 ResetGfxAnimation(xx, yy);
9922 ResetRandomAnimationValue(xx, yy);
9925 TEST_DrawLevelField(xx, yy);
9932 /* ---------- engine actions ------------------------------------------ */
9934 case CA_SET_ENGINE_SCAN_MODE:
9936 InitPlayfieldScanMode(action_arg);
9946 static void CreateFieldExt(int x, int y, int element, boolean is_change)
9948 int old_element = Feld[x][y];
9949 int new_element = GetElementFromGroupElement(element);
9950 int previous_move_direction = MovDir[x][y];
9951 int last_ce_value = CustomValue[x][y];
9952 boolean player_explosion_protected = PLAYER_EXPLOSION_PROTECTED(x, y);
9953 boolean new_element_is_player = ELEM_IS_PLAYER(new_element);
9954 boolean add_player_onto_element = (new_element_is_player &&
9955 new_element != EL_SOKOBAN_FIELD_PLAYER &&
9956 IS_WALKABLE(old_element));
9958 if (!add_player_onto_element)
9960 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
9961 RemoveMovingField(x, y);
9965 Feld[x][y] = new_element;
9967 if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
9968 MovDir[x][y] = previous_move_direction;
9970 if (element_info[new_element].use_last_ce_value)
9971 CustomValue[x][y] = last_ce_value;
9973 InitField_WithBug1(x, y, FALSE);
9975 new_element = Feld[x][y]; /* element may have changed */
9977 ResetGfxAnimation(x, y);
9978 ResetRandomAnimationValue(x, y);
9980 TEST_DrawLevelField(x, y);
9982 if (GFX_CRUMBLED(new_element))
9983 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
9986 /* check if element under the player changes from accessible to unaccessible
9987 (needed for special case of dropping element which then changes) */
9988 /* (must be checked after creating new element for walkable group elements) */
9989 if (IS_PLAYER(x, y) && !player_explosion_protected &&
9990 IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
9997 /* "ChangeCount" not set yet to allow "entered by player" change one time */
9998 if (new_element_is_player)
9999 RelocatePlayer(x, y, new_element);
10002 ChangeCount[x][y]++; /* count number of changes in the same frame */
10004 TestIfBadThingTouchesPlayer(x, y);
10005 TestIfPlayerTouchesCustomElement(x, y);
10006 TestIfElementTouchesCustomElement(x, y);
10009 static void CreateField(int x, int y, int element)
10011 CreateFieldExt(x, y, element, FALSE);
10014 static void CreateElementFromChange(int x, int y, int element)
10016 element = GET_VALID_RUNTIME_ELEMENT(element);
10018 if (game.engine_version >= VERSION_IDENT(3,2,0,7))
10020 int old_element = Feld[x][y];
10022 /* prevent changed element from moving in same engine frame
10023 unless both old and new element can either fall or move */
10024 if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
10025 (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
10029 CreateFieldExt(x, y, element, TRUE);
10032 static boolean ChangeElement(int x, int y, int element, int page)
10034 struct ElementInfo *ei = &element_info[element];
10035 struct ElementChangeInfo *change = &ei->change_page[page];
10036 int ce_value = CustomValue[x][y];
10037 int ce_score = ei->collect_score;
10038 int target_element;
10039 int old_element = Feld[x][y];
10041 /* always use default change event to prevent running into a loop */
10042 if (ChangeEvent[x][y] == -1)
10043 ChangeEvent[x][y] = CE_DELAY;
10045 if (ChangeEvent[x][y] == CE_DELAY)
10047 /* reset actual trigger element, trigger player and action element */
10048 change->actual_trigger_element = EL_EMPTY;
10049 change->actual_trigger_player = EL_EMPTY;
10050 change->actual_trigger_player_bits = CH_PLAYER_NONE;
10051 change->actual_trigger_side = CH_SIDE_NONE;
10052 change->actual_trigger_ce_value = 0;
10053 change->actual_trigger_ce_score = 0;
10056 /* do not change elements more than a specified maximum number of changes */
10057 if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
10060 ChangeCount[x][y]++; /* count number of changes in the same frame */
10062 if (change->explode)
10069 if (change->use_target_content)
10071 boolean complete_replace = TRUE;
10072 boolean can_replace[3][3];
10075 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10078 boolean is_walkable;
10079 boolean is_diggable;
10080 boolean is_collectible;
10081 boolean is_removable;
10082 boolean is_destructible;
10083 int ex = x + xx - 1;
10084 int ey = y + yy - 1;
10085 int content_element = change->target_content.e[xx][yy];
10088 can_replace[xx][yy] = TRUE;
10090 if (ex == x && ey == y) /* do not check changing element itself */
10093 if (content_element == EL_EMPTY_SPACE)
10095 can_replace[xx][yy] = FALSE; /* do not replace border with space */
10100 if (!IN_LEV_FIELD(ex, ey))
10102 can_replace[xx][yy] = FALSE;
10103 complete_replace = FALSE;
10110 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10111 e = MovingOrBlocked2Element(ex, ey);
10113 is_empty = (IS_FREE(ex, ey) ||
10114 (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
10116 is_walkable = (is_empty || IS_WALKABLE(e));
10117 is_diggable = (is_empty || IS_DIGGABLE(e));
10118 is_collectible = (is_empty || IS_COLLECTIBLE(e));
10119 is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
10120 is_removable = (is_diggable || is_collectible);
10122 can_replace[xx][yy] =
10123 (((change->replace_when == CP_WHEN_EMPTY && is_empty) ||
10124 (change->replace_when == CP_WHEN_WALKABLE && is_walkable) ||
10125 (change->replace_when == CP_WHEN_DIGGABLE && is_diggable) ||
10126 (change->replace_when == CP_WHEN_COLLECTIBLE && is_collectible) ||
10127 (change->replace_when == CP_WHEN_REMOVABLE && is_removable) ||
10128 (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
10129 !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
10131 if (!can_replace[xx][yy])
10132 complete_replace = FALSE;
10135 if (!change->only_if_complete || complete_replace)
10137 boolean something_has_changed = FALSE;
10139 if (change->only_if_complete && change->use_random_replace &&
10140 RND(100) < change->random_percentage)
10143 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10145 int ex = x + xx - 1;
10146 int ey = y + yy - 1;
10147 int content_element;
10149 if (can_replace[xx][yy] && (!change->use_random_replace ||
10150 RND(100) < change->random_percentage))
10152 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10153 RemoveMovingField(ex, ey);
10155 ChangeEvent[ex][ey] = ChangeEvent[x][y];
10157 content_element = change->target_content.e[xx][yy];
10158 target_element = GET_TARGET_ELEMENT(element, content_element, change,
10159 ce_value, ce_score);
10161 CreateElementFromChange(ex, ey, target_element);
10163 something_has_changed = TRUE;
10165 /* for symmetry reasons, freeze newly created border elements */
10166 if (ex != x || ey != y)
10167 Stop[ex][ey] = TRUE; /* no more moving in this frame */
10171 if (something_has_changed)
10173 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10174 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10180 target_element = GET_TARGET_ELEMENT(element, change->target_element, change,
10181 ce_value, ce_score);
10183 if (element == EL_DIAGONAL_GROWING ||
10184 element == EL_DIAGONAL_SHRINKING)
10186 target_element = Store[x][y];
10188 Store[x][y] = EL_EMPTY;
10191 CreateElementFromChange(x, y, target_element);
10193 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10194 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10197 /* this uses direct change before indirect change */
10198 CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
10203 static void HandleElementChange(int x, int y, int page)
10205 int element = MovingOrBlocked2Element(x, y);
10206 struct ElementInfo *ei = &element_info[element];
10207 struct ElementChangeInfo *change = &ei->change_page[page];
10208 boolean handle_action_before_change = FALSE;
10211 if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
10212 !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
10215 printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
10216 x, y, element, element_info[element].token_name);
10217 printf("HandleElementChange(): This should never happen!\n");
10222 /* this can happen with classic bombs on walkable, changing elements */
10223 if (!CAN_CHANGE_OR_HAS_ACTION(element))
10228 if (ChangeDelay[x][y] == 0) /* initialize element change */
10230 ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
10232 if (change->can_change)
10234 /* !!! not clear why graphic animation should be reset at all here !!! */
10235 /* !!! UPDATE: but is needed for correct Snake Bite tail animation !!! */
10236 /* when a custom element is about to change (for example by change delay),
10237 do not reset graphic animation when the custom element is moving */
10238 if (!IS_MOVING(x, y))
10240 ResetGfxAnimation(x, y);
10241 ResetRandomAnimationValue(x, y);
10244 if (change->pre_change_function)
10245 change->pre_change_function(x, y);
10249 ChangeDelay[x][y]--;
10251 if (ChangeDelay[x][y] != 0) /* continue element change */
10253 if (change->can_change)
10255 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10257 if (IS_ANIMATED(graphic))
10258 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10260 if (change->change_function)
10261 change->change_function(x, y);
10264 else /* finish element change */
10266 if (ChangePage[x][y] != -1) /* remember page from delayed change */
10268 page = ChangePage[x][y];
10269 ChangePage[x][y] = -1;
10271 change = &ei->change_page[page];
10274 if (IS_MOVING(x, y)) /* never change a running system ;-) */
10276 ChangeDelay[x][y] = 1; /* try change after next move step */
10277 ChangePage[x][y] = page; /* remember page to use for change */
10282 /* special case: set new level random seed before changing element */
10283 if (change->has_action && change->action_type == CA_SET_LEVEL_RANDOM_SEED)
10284 handle_action_before_change = TRUE;
10286 if (change->has_action && handle_action_before_change)
10287 ExecuteCustomElementAction(x, y, element, page);
10289 if (change->can_change)
10291 if (ChangeElement(x, y, element, page))
10293 if (change->post_change_function)
10294 change->post_change_function(x, y);
10298 if (change->has_action && !handle_action_before_change)
10299 ExecuteCustomElementAction(x, y, element, page);
10303 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
10304 int trigger_element,
10306 int trigger_player,
10310 boolean change_done_any = FALSE;
10311 int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
10314 if (!(trigger_events[trigger_element][trigger_event]))
10317 RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
10319 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
10321 int element = EL_CUSTOM_START + i;
10322 boolean change_done = FALSE;
10325 if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
10326 !HAS_ANY_CHANGE_EVENT(element, trigger_event))
10329 for (p = 0; p < element_info[element].num_change_pages; p++)
10331 struct ElementChangeInfo *change = &element_info[element].change_page[p];
10333 if (change->can_change_or_has_action &&
10334 change->has_event[trigger_event] &&
10335 change->trigger_side & trigger_side &&
10336 change->trigger_player & trigger_player &&
10337 change->trigger_page & trigger_page_bits &&
10338 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
10340 change->actual_trigger_element = trigger_element;
10341 change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
10342 change->actual_trigger_player_bits = trigger_player;
10343 change->actual_trigger_side = trigger_side;
10344 change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
10345 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10347 if ((change->can_change && !change_done) || change->has_action)
10351 SCAN_PLAYFIELD(x, y)
10353 if (Feld[x][y] == element)
10355 if (change->can_change && !change_done)
10357 /* if element already changed in this frame, not only prevent
10358 another element change (checked in ChangeElement()), but
10359 also prevent additional element actions for this element */
10361 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
10362 !level.use_action_after_change_bug)
10365 ChangeDelay[x][y] = 1;
10366 ChangeEvent[x][y] = trigger_event;
10368 HandleElementChange(x, y, p);
10370 else if (change->has_action)
10372 /* if element already changed in this frame, not only prevent
10373 another element change (checked in ChangeElement()), but
10374 also prevent additional element actions for this element */
10376 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
10377 !level.use_action_after_change_bug)
10380 ExecuteCustomElementAction(x, y, element, p);
10381 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10386 if (change->can_change)
10388 change_done = TRUE;
10389 change_done_any = TRUE;
10396 RECURSION_LOOP_DETECTION_END();
10398 return change_done_any;
10401 static boolean CheckElementChangeExt(int x, int y,
10403 int trigger_element,
10405 int trigger_player,
10408 boolean change_done = FALSE;
10411 if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
10412 !HAS_ANY_CHANGE_EVENT(element, trigger_event))
10415 if (Feld[x][y] == EL_BLOCKED)
10417 Blocked2Moving(x, y, &x, &y);
10418 element = Feld[x][y];
10421 /* check if element has already changed or is about to change after moving */
10422 if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
10423 Feld[x][y] != element) ||
10425 (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
10426 (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
10427 ChangePage[x][y] != -1)))
10430 RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
10432 for (p = 0; p < element_info[element].num_change_pages; p++)
10434 struct ElementChangeInfo *change = &element_info[element].change_page[p];
10436 /* check trigger element for all events where the element that is checked
10437 for changing interacts with a directly adjacent element -- this is
10438 different to element changes that affect other elements to change on the
10439 whole playfield (which is handeld by CheckTriggeredElementChangeExt()) */
10440 boolean check_trigger_element =
10441 (trigger_event == CE_TOUCHING_X ||
10442 trigger_event == CE_HITTING_X ||
10443 trigger_event == CE_HIT_BY_X ||
10444 trigger_event == CE_DIGGING_X); /* this one was forgotten until 3.2.3 */
10446 if (change->can_change_or_has_action &&
10447 change->has_event[trigger_event] &&
10448 change->trigger_side & trigger_side &&
10449 change->trigger_player & trigger_player &&
10450 (!check_trigger_element ||
10451 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
10453 change->actual_trigger_element = trigger_element;
10454 change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
10455 change->actual_trigger_player_bits = trigger_player;
10456 change->actual_trigger_side = trigger_side;
10457 change->actual_trigger_ce_value = CustomValue[x][y];
10458 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10460 /* special case: trigger element not at (x,y) position for some events */
10461 if (check_trigger_element)
10473 { 0, 0 }, { 0, 0 }, { 0, 0 },
10477 int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
10478 int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
10480 change->actual_trigger_ce_value = CustomValue[xx][yy];
10481 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10484 if (change->can_change && !change_done)
10486 ChangeDelay[x][y] = 1;
10487 ChangeEvent[x][y] = trigger_event;
10489 HandleElementChange(x, y, p);
10491 change_done = TRUE;
10493 else if (change->has_action)
10495 ExecuteCustomElementAction(x, y, element, p);
10496 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10501 RECURSION_LOOP_DETECTION_END();
10503 return change_done;
10506 static void PlayPlayerSound(struct PlayerInfo *player)
10508 int jx = player->jx, jy = player->jy;
10509 int sound_element = player->artwork_element;
10510 int last_action = player->last_action_waiting;
10511 int action = player->action_waiting;
10513 if (player->is_waiting)
10515 if (action != last_action)
10516 PlayLevelSoundElementAction(jx, jy, sound_element, action);
10518 PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
10522 if (action != last_action)
10523 StopSound(element_info[sound_element].sound[last_action]);
10525 if (last_action == ACTION_SLEEPING)
10526 PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
10530 static void PlayAllPlayersSound()
10534 for (i = 0; i < MAX_PLAYERS; i++)
10535 if (stored_player[i].active)
10536 PlayPlayerSound(&stored_player[i]);
10539 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
10541 boolean last_waiting = player->is_waiting;
10542 int move_dir = player->MovDir;
10544 player->dir_waiting = move_dir;
10545 player->last_action_waiting = player->action_waiting;
10549 if (!last_waiting) /* not waiting -> waiting */
10551 player->is_waiting = TRUE;
10553 player->frame_counter_bored =
10555 game.player_boring_delay_fixed +
10556 GetSimpleRandom(game.player_boring_delay_random);
10557 player->frame_counter_sleeping =
10559 game.player_sleeping_delay_fixed +
10560 GetSimpleRandom(game.player_sleeping_delay_random);
10562 InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
10565 if (game.player_sleeping_delay_fixed +
10566 game.player_sleeping_delay_random > 0 &&
10567 player->anim_delay_counter == 0 &&
10568 player->post_delay_counter == 0 &&
10569 FrameCounter >= player->frame_counter_sleeping)
10570 player->is_sleeping = TRUE;
10571 else if (game.player_boring_delay_fixed +
10572 game.player_boring_delay_random > 0 &&
10573 FrameCounter >= player->frame_counter_bored)
10574 player->is_bored = TRUE;
10576 player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
10577 player->is_bored ? ACTION_BORING :
10580 if (player->is_sleeping && player->use_murphy)
10582 /* special case for sleeping Murphy when leaning against non-free tile */
10584 if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
10585 (Feld[player->jx - 1][player->jy] != EL_EMPTY &&
10586 !IS_MOVING(player->jx - 1, player->jy)))
10587 move_dir = MV_LEFT;
10588 else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
10589 (Feld[player->jx + 1][player->jy] != EL_EMPTY &&
10590 !IS_MOVING(player->jx + 1, player->jy)))
10591 move_dir = MV_RIGHT;
10593 player->is_sleeping = FALSE;
10595 player->dir_waiting = move_dir;
10598 if (player->is_sleeping)
10600 if (player->num_special_action_sleeping > 0)
10602 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
10604 int last_special_action = player->special_action_sleeping;
10605 int num_special_action = player->num_special_action_sleeping;
10606 int special_action =
10607 (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
10608 last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
10609 last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
10610 last_special_action + 1 : ACTION_SLEEPING);
10611 int special_graphic =
10612 el_act_dir2img(player->artwork_element, special_action, move_dir);
10614 player->anim_delay_counter =
10615 graphic_info[special_graphic].anim_delay_fixed +
10616 GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
10617 player->post_delay_counter =
10618 graphic_info[special_graphic].post_delay_fixed +
10619 GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
10621 player->special_action_sleeping = special_action;
10624 if (player->anim_delay_counter > 0)
10626 player->action_waiting = player->special_action_sleeping;
10627 player->anim_delay_counter--;
10629 else if (player->post_delay_counter > 0)
10631 player->post_delay_counter--;
10635 else if (player->is_bored)
10637 if (player->num_special_action_bored > 0)
10639 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
10641 int special_action =
10642 ACTION_BORING_1 + GetSimpleRandom(player->num_special_action_bored);
10643 int special_graphic =
10644 el_act_dir2img(player->artwork_element, special_action, move_dir);
10646 player->anim_delay_counter =
10647 graphic_info[special_graphic].anim_delay_fixed +
10648 GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
10649 player->post_delay_counter =
10650 graphic_info[special_graphic].post_delay_fixed +
10651 GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
10653 player->special_action_bored = special_action;
10656 if (player->anim_delay_counter > 0)
10658 player->action_waiting = player->special_action_bored;
10659 player->anim_delay_counter--;
10661 else if (player->post_delay_counter > 0)
10663 player->post_delay_counter--;
10668 else if (last_waiting) /* waiting -> not waiting */
10670 player->is_waiting = FALSE;
10671 player->is_bored = FALSE;
10672 player->is_sleeping = FALSE;
10674 player->frame_counter_bored = -1;
10675 player->frame_counter_sleeping = -1;
10677 player->anim_delay_counter = 0;
10678 player->post_delay_counter = 0;
10680 player->dir_waiting = player->MovDir;
10681 player->action_waiting = ACTION_DEFAULT;
10683 player->special_action_bored = ACTION_DEFAULT;
10684 player->special_action_sleeping = ACTION_DEFAULT;
10688 static void CheckSaveEngineSnapshot(struct PlayerInfo *player)
10690 static boolean player_was_moving = FALSE;
10691 static boolean player_was_snapping = FALSE;
10692 static boolean player_was_dropping = FALSE;
10694 if ((!player->is_moving && player_was_moving) ||
10695 (player->MovPos == 0 && player_was_moving) ||
10696 (player->is_snapping && !player_was_snapping) ||
10697 (player->is_dropping && !player_was_dropping))
10699 if (!SaveEngineSnapshotToList())
10702 player_was_moving = FALSE;
10703 player_was_snapping = TRUE;
10704 player_was_dropping = TRUE;
10708 if (player->is_moving)
10709 player_was_moving = TRUE;
10711 if (!player->is_snapping)
10712 player_was_snapping = FALSE;
10714 if (!player->is_dropping)
10715 player_was_dropping = FALSE;
10719 static void CheckSingleStepMode(struct PlayerInfo *player)
10721 if (tape.single_step && tape.recording && !tape.pausing)
10723 /* as it is called "single step mode", just return to pause mode when the
10724 player stopped moving after one tile (or never starts moving at all) */
10725 if (!player->is_moving && !player->is_pushing)
10727 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
10728 SnapField(player, 0, 0); /* stop snapping */
10732 CheckSaveEngineSnapshot(player);
10735 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
10737 int left = player_action & JOY_LEFT;
10738 int right = player_action & JOY_RIGHT;
10739 int up = player_action & JOY_UP;
10740 int down = player_action & JOY_DOWN;
10741 int button1 = player_action & JOY_BUTTON_1;
10742 int button2 = player_action & JOY_BUTTON_2;
10743 int dx = (left ? -1 : right ? 1 : 0);
10744 int dy = (up ? -1 : down ? 1 : 0);
10746 if (!player->active || tape.pausing)
10752 SnapField(player, dx, dy);
10756 DropElement(player);
10758 MovePlayer(player, dx, dy);
10761 CheckSingleStepMode(player);
10763 SetPlayerWaiting(player, FALSE);
10765 return player_action;
10769 /* no actions for this player (no input at player's configured device) */
10771 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
10772 SnapField(player, 0, 0);
10773 CheckGravityMovementWhenNotMoving(player);
10775 if (player->MovPos == 0)
10776 SetPlayerWaiting(player, TRUE);
10778 if (player->MovPos == 0) /* needed for tape.playing */
10779 player->is_moving = FALSE;
10781 player->is_dropping = FALSE;
10782 player->is_dropping_pressed = FALSE;
10783 player->drop_pressed_delay = 0;
10785 CheckSingleStepMode(player);
10791 static void CheckLevelTime()
10795 /* !!! SAME CODE AS IN "GameActions()" -- FIX THIS !!! */
10796 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
10798 if (level.native_em_level->lev->home == 0) /* all players at home */
10800 PlayerWins(local_player);
10802 AllPlayersGone = TRUE;
10804 level.native_em_level->lev->home = -1;
10807 if (level.native_em_level->ply[0]->alive == 0 &&
10808 level.native_em_level->ply[1]->alive == 0 &&
10809 level.native_em_level->ply[2]->alive == 0 &&
10810 level.native_em_level->ply[3]->alive == 0) /* all dead */
10811 AllPlayersGone = TRUE;
10813 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
10815 if (game_sp.LevelSolved &&
10816 !game_sp.GameOver) /* game won */
10818 PlayerWins(local_player);
10820 game_sp.GameOver = TRUE;
10822 AllPlayersGone = TRUE;
10825 if (game_sp.GameOver) /* game lost */
10826 AllPlayersGone = TRUE;
10829 if (TimeFrames >= FRAMES_PER_SECOND)
10834 for (i = 0; i < MAX_PLAYERS; i++)
10836 struct PlayerInfo *player = &stored_player[i];
10838 if (SHIELD_ON(player))
10840 player->shield_normal_time_left--;
10842 if (player->shield_deadly_time_left > 0)
10843 player->shield_deadly_time_left--;
10847 if (!local_player->LevelSolved && !level.use_step_counter)
10855 if (TimeLeft <= 10 && setup.time_limit)
10856 PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
10858 /* this does not make sense: game_panel_controls[GAME_PANEL_TIME].value
10859 is reset from other values in UpdateGameDoorValues() -- FIX THIS */
10861 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
10863 if (!TimeLeft && setup.time_limit)
10865 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
10866 level.native_em_level->lev->killed_out_of_time = TRUE;
10868 for (i = 0; i < MAX_PLAYERS; i++)
10869 KillPlayer(&stored_player[i]);
10872 else if (game.no_time_limit && !AllPlayersGone) /* level w/o time limit */
10874 game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
10877 level.native_em_level->lev->time =
10878 (game.no_time_limit ? TimePlayed : TimeLeft);
10881 if (tape.recording || tape.playing)
10882 DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
10885 if (tape.recording || tape.playing)
10886 DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
10888 UpdateAndDisplayGameControlValues();
10891 void AdvanceFrameAndPlayerCounters(int player_nr)
10895 /* advance frame counters (global frame counter and time frame counter) */
10899 /* advance player counters (counters for move delay, move animation etc.) */
10900 for (i = 0; i < MAX_PLAYERS; i++)
10902 boolean advance_player_counters = (player_nr == -1 || player_nr == i);
10903 int move_delay_value = stored_player[i].move_delay_value;
10904 int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
10906 if (!advance_player_counters) /* not all players may be affected */
10909 if (move_frames == 0) /* less than one move per game frame */
10911 int stepsize = TILEX / move_delay_value;
10912 int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
10913 int count = (stored_player[i].is_moving ?
10914 ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
10916 if (count % delay == 0)
10920 stored_player[i].Frame += move_frames;
10922 if (stored_player[i].MovPos != 0)
10923 stored_player[i].StepFrame += move_frames;
10925 if (stored_player[i].move_delay > 0)
10926 stored_player[i].move_delay--;
10928 /* due to bugs in previous versions, counter must count up, not down */
10929 if (stored_player[i].push_delay != -1)
10930 stored_player[i].push_delay++;
10932 if (stored_player[i].drop_delay > 0)
10933 stored_player[i].drop_delay--;
10935 if (stored_player[i].is_dropping_pressed)
10936 stored_player[i].drop_pressed_delay++;
10940 void StartGameActions(boolean init_network_game, boolean record_tape,
10943 unsigned int new_random_seed = InitRND(random_seed);
10946 TapeStartRecording(new_random_seed);
10948 #if defined(NETWORK_AVALIABLE)
10949 if (init_network_game)
10951 SendToServer_StartPlaying();
10962 static unsigned int game_frame_delay = 0;
10963 unsigned int game_frame_delay_value;
10964 byte *recorded_player_action;
10965 byte summarized_player_action = 0;
10966 byte tape_action[MAX_PLAYERS];
10969 for (i = 0; i < MAX_PLAYERS; i++)
10971 struct PlayerInfo *player = &stored_player[i];
10973 // allow engine snapshot if movement attempt was stopped
10974 if ((game.snapshot.last_action[i] & KEY_MOTION) != 0 &&
10975 (player->action & KEY_MOTION) == 0)
10976 game.snapshot.changed_action = TRUE;
10978 // allow engine snapshot in case of snapping/dropping attempt
10979 if ((game.snapshot.last_action[i] & KEY_BUTTON) == 0 &&
10980 (player->action & KEY_BUTTON) != 0)
10981 game.snapshot.changed_action = TRUE;
10983 game.snapshot.last_action[i] = player->action;
10986 /* detect endless loops, caused by custom element programming */
10987 if (recursion_loop_detected && recursion_loop_depth == 0)
10989 char *message = getStringCat3("Internal Error! Element ",
10990 EL_NAME(recursion_loop_element),
10991 " caused endless loop! Quit the game?");
10993 Error(ERR_WARN, "element '%s' caused endless loop in game engine",
10994 EL_NAME(recursion_loop_element));
10996 RequestQuitGameExt(FALSE, level_editor_test_game, message);
10998 recursion_loop_detected = FALSE; /* if game should be continued */
11005 if (game.restart_level)
11006 StartGameActions(options.network, setup.autorecord, level.random_seed);
11008 /* !!! SAME CODE AS IN "CheckLevelTime()" -- FIX THIS !!! */
11009 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11011 if (level.native_em_level->lev->home == 0) /* all players at home */
11013 PlayerWins(local_player);
11015 AllPlayersGone = TRUE;
11017 level.native_em_level->lev->home = -1;
11020 if (level.native_em_level->ply[0]->alive == 0 &&
11021 level.native_em_level->ply[1]->alive == 0 &&
11022 level.native_em_level->ply[2]->alive == 0 &&
11023 level.native_em_level->ply[3]->alive == 0) /* all dead */
11024 AllPlayersGone = TRUE;
11026 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11028 if (game_sp.LevelSolved &&
11029 !game_sp.GameOver) /* game won */
11031 PlayerWins(local_player);
11033 game_sp.GameOver = TRUE;
11035 AllPlayersGone = TRUE;
11038 if (game_sp.GameOver) /* game lost */
11039 AllPlayersGone = TRUE;
11042 if (local_player->LevelSolved && !local_player->LevelSolved_GameEnd)
11045 if (AllPlayersGone && !TAPE_IS_STOPPED(tape))
11048 if (game_status != GAME_MODE_PLAYING) /* status might have changed */
11051 game_frame_delay_value =
11052 (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
11054 if (tape.playing && tape.warp_forward && !tape.pausing)
11055 game_frame_delay_value = 0;
11057 /* ---------- main game synchronization point ---------- */
11059 WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11061 if (network_playing && !network_player_action_received)
11063 /* try to get network player actions in time */
11065 #if defined(NETWORK_AVALIABLE)
11066 /* last chance to get network player actions without main loop delay */
11067 HandleNetworking();
11070 /* game was quit by network peer */
11071 if (game_status != GAME_MODE_PLAYING)
11074 if (!network_player_action_received)
11075 return; /* failed to get network player actions in time */
11077 /* do not yet reset "network_player_action_received" (for tape.pausing) */
11083 /* at this point we know that we really continue executing the game */
11085 network_player_action_received = FALSE;
11087 /* when playing tape, read previously recorded player input from tape data */
11088 recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
11090 /* TapePlayAction() may return NULL when toggling to "pause before death" */
11094 if (tape.set_centered_player)
11096 game.centered_player_nr_next = tape.centered_player_nr_next;
11097 game.set_centered_player = TRUE;
11100 for (i = 0; i < MAX_PLAYERS; i++)
11102 summarized_player_action |= stored_player[i].action;
11104 if (!network_playing && (game.team_mode || tape.playing))
11105 stored_player[i].effective_action = stored_player[i].action;
11108 #if defined(NETWORK_AVALIABLE)
11109 if (network_playing)
11110 SendToServer_MovePlayer(summarized_player_action);
11113 if (!options.network && !game.team_mode)
11114 local_player->effective_action = summarized_player_action;
11116 if (tape.recording &&
11118 setup.input_on_focus &&
11119 game.centered_player_nr != -1)
11121 for (i = 0; i < MAX_PLAYERS; i++)
11122 stored_player[i].effective_action =
11123 (i == game.centered_player_nr ? summarized_player_action : 0);
11126 if (recorded_player_action != NULL)
11127 for (i = 0; i < MAX_PLAYERS; i++)
11128 stored_player[i].effective_action = recorded_player_action[i];
11130 for (i = 0; i < MAX_PLAYERS; i++)
11132 tape_action[i] = stored_player[i].effective_action;
11134 /* (this may happen in the RND game engine if a player was not present on
11135 the playfield on level start, but appeared later from a custom element */
11136 if (setup.team_mode &&
11139 !tape.player_participates[i])
11140 tape.player_participates[i] = TRUE;
11143 /* only record actions from input devices, but not programmed actions */
11144 if (tape.recording)
11145 TapeRecordAction(tape_action);
11147 #if USE_NEW_PLAYER_ASSIGNMENTS
11148 // !!! also map player actions in single player mode !!!
11149 // if (game.team_mode)
11151 byte mapped_action[MAX_PLAYERS];
11153 #if DEBUG_PLAYER_ACTIONS
11155 for (i = 0; i < MAX_PLAYERS; i++)
11156 printf(" %d, ", stored_player[i].effective_action);
11159 for (i = 0; i < MAX_PLAYERS; i++)
11160 mapped_action[i] = stored_player[map_player_action[i]].effective_action;
11162 for (i = 0; i < MAX_PLAYERS; i++)
11163 stored_player[i].effective_action = mapped_action[i];
11165 #if DEBUG_PLAYER_ACTIONS
11167 for (i = 0; i < MAX_PLAYERS; i++)
11168 printf(" %d, ", stored_player[i].effective_action);
11172 #if DEBUG_PLAYER_ACTIONS
11176 for (i = 0; i < MAX_PLAYERS; i++)
11177 printf(" %d, ", stored_player[i].effective_action);
11183 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11185 GameActions_EM_Main();
11187 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11189 GameActions_SP_Main();
11197 void GameActions_EM_Main()
11199 byte effective_action[MAX_PLAYERS];
11200 boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11203 for (i = 0; i < MAX_PLAYERS; i++)
11204 effective_action[i] = stored_player[i].effective_action;
11206 GameActions_EM(effective_action, warp_mode);
11210 AdvanceFrameAndPlayerCounters(-1); /* advance counters for all players */
11213 void GameActions_SP_Main()
11215 byte effective_action[MAX_PLAYERS];
11216 boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11219 for (i = 0; i < MAX_PLAYERS; i++)
11220 effective_action[i] = stored_player[i].effective_action;
11222 GameActions_SP(effective_action, warp_mode);
11226 AdvanceFrameAndPlayerCounters(-1); /* advance counters for all players */
11229 void GameActions_RND()
11231 int magic_wall_x = 0, magic_wall_y = 0;
11232 int i, x, y, element, graphic;
11234 InitPlayfieldScanModeVars();
11236 if (game.engine_version >= VERSION_IDENT(3,2,0,7))
11238 SCAN_PLAYFIELD(x, y)
11240 ChangeCount[x][y] = 0;
11241 ChangeEvent[x][y] = -1;
11245 if (game.set_centered_player)
11247 boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
11249 /* switching to "all players" only possible if all players fit to screen */
11250 if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
11252 game.centered_player_nr_next = game.centered_player_nr;
11253 game.set_centered_player = FALSE;
11256 /* do not switch focus to non-existing (or non-active) player */
11257 if (game.centered_player_nr_next >= 0 &&
11258 !stored_player[game.centered_player_nr_next].active)
11260 game.centered_player_nr_next = game.centered_player_nr;
11261 game.set_centered_player = FALSE;
11265 if (game.set_centered_player &&
11266 ScreenMovPos == 0) /* screen currently aligned at tile position */
11270 if (game.centered_player_nr_next == -1)
11272 setScreenCenteredToAllPlayers(&sx, &sy);
11276 sx = stored_player[game.centered_player_nr_next].jx;
11277 sy = stored_player[game.centered_player_nr_next].jy;
11280 game.centered_player_nr = game.centered_player_nr_next;
11281 game.set_centered_player = FALSE;
11283 DrawRelocateScreen(0, 0, sx, sy, MV_NONE, TRUE, setup.quick_switch);
11284 DrawGameDoorValues();
11287 for (i = 0; i < MAX_PLAYERS; i++)
11289 int actual_player_action = stored_player[i].effective_action;
11292 /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
11293 - rnd_equinox_tetrachloride 048
11294 - rnd_equinox_tetrachloride_ii 096
11295 - rnd_emanuel_schmieg 002
11296 - doctor_sloan_ww 001, 020
11298 if (stored_player[i].MovPos == 0)
11299 CheckGravityMovement(&stored_player[i]);
11302 /* overwrite programmed action with tape action */
11303 if (stored_player[i].programmed_action)
11304 actual_player_action = stored_player[i].programmed_action;
11306 PlayerActions(&stored_player[i], actual_player_action);
11308 ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
11311 ScrollScreen(NULL, SCROLL_GO_ON);
11313 /* for backwards compatibility, the following code emulates a fixed bug that
11314 occured when pushing elements (causing elements that just made their last
11315 pushing step to already (if possible) make their first falling step in the
11316 same game frame, which is bad); this code is also needed to use the famous
11317 "spring push bug" which is used in older levels and might be wanted to be
11318 used also in newer levels, but in this case the buggy pushing code is only
11319 affecting the "spring" element and no other elements */
11321 if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
11323 for (i = 0; i < MAX_PLAYERS; i++)
11325 struct PlayerInfo *player = &stored_player[i];
11326 int x = player->jx;
11327 int y = player->jy;
11329 if (player->active && player->is_pushing && player->is_moving &&
11331 (game.engine_version < VERSION_IDENT(2,2,0,7) ||
11332 Feld[x][y] == EL_SPRING))
11334 ContinueMoving(x, y);
11336 /* continue moving after pushing (this is actually a bug) */
11337 if (!IS_MOVING(x, y))
11338 Stop[x][y] = FALSE;
11343 SCAN_PLAYFIELD(x, y)
11345 ChangeCount[x][y] = 0;
11346 ChangeEvent[x][y] = -1;
11348 /* this must be handled before main playfield loop */
11349 if (Feld[x][y] == EL_PLAYER_IS_LEAVING)
11352 if (MovDelay[x][y] <= 0)
11356 if (Feld[x][y] == EL_ELEMENT_SNAPPING)
11359 if (MovDelay[x][y] <= 0)
11362 TEST_DrawLevelField(x, y);
11364 TestIfElementTouchesCustomElement(x, y); /* for empty space */
11369 if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
11371 printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
11372 printf("GameActions(): This should never happen!\n");
11374 ChangePage[x][y] = -1;
11378 Stop[x][y] = FALSE;
11379 if (WasJustMoving[x][y] > 0)
11380 WasJustMoving[x][y]--;
11381 if (WasJustFalling[x][y] > 0)
11382 WasJustFalling[x][y]--;
11383 if (CheckCollision[x][y] > 0)
11384 CheckCollision[x][y]--;
11385 if (CheckImpact[x][y] > 0)
11386 CheckImpact[x][y]--;
11390 /* reset finished pushing action (not done in ContinueMoving() to allow
11391 continuous pushing animation for elements with zero push delay) */
11392 if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
11394 ResetGfxAnimation(x, y);
11395 TEST_DrawLevelField(x, y);
11399 if (IS_BLOCKED(x, y))
11403 Blocked2Moving(x, y, &oldx, &oldy);
11404 if (!IS_MOVING(oldx, oldy))
11406 printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
11407 printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
11408 printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
11409 printf("GameActions(): This should never happen!\n");
11415 SCAN_PLAYFIELD(x, y)
11417 element = Feld[x][y];
11418 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11420 ResetGfxFrame(x, y, TRUE);
11422 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
11423 IS_NEXT_FRAME(GfxFrame[x][y], graphic))
11424 ResetRandomAnimationValue(x, y);
11426 SetRandomAnimationValue(x, y);
11428 PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
11430 if (IS_INACTIVE(element))
11432 if (IS_ANIMATED(graphic))
11433 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11438 /* this may take place after moving, so 'element' may have changed */
11439 if (IS_CHANGING(x, y) &&
11440 (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
11442 int page = element_info[element].event_page_nr[CE_DELAY];
11444 HandleElementChange(x, y, page);
11446 element = Feld[x][y];
11447 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11450 if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
11454 element = Feld[x][y];
11455 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11457 if (IS_ANIMATED(graphic) &&
11458 !IS_MOVING(x, y) &&
11460 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11462 if (IS_GEM(element) || element == EL_SP_INFOTRON)
11463 TEST_DrawTwinkleOnField(x, y);
11465 else if ((element == EL_ACID ||
11466 element == EL_EXIT_OPEN ||
11467 element == EL_EM_EXIT_OPEN ||
11468 element == EL_SP_EXIT_OPEN ||
11469 element == EL_STEEL_EXIT_OPEN ||
11470 element == EL_EM_STEEL_EXIT_OPEN ||
11471 element == EL_SP_TERMINAL ||
11472 element == EL_SP_TERMINAL_ACTIVE ||
11473 element == EL_EXTRA_TIME ||
11474 element == EL_SHIELD_NORMAL ||
11475 element == EL_SHIELD_DEADLY) &&
11476 IS_ANIMATED(graphic))
11477 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11478 else if (IS_MOVING(x, y))
11479 ContinueMoving(x, y);
11480 else if (IS_ACTIVE_BOMB(element))
11481 CheckDynamite(x, y);
11482 else if (element == EL_AMOEBA_GROWING)
11483 AmoebeWaechst(x, y);
11484 else if (element == EL_AMOEBA_SHRINKING)
11485 AmoebaDisappearing(x, y);
11487 #if !USE_NEW_AMOEBA_CODE
11488 else if (IS_AMOEBALIVE(element))
11489 AmoebeAbleger(x, y);
11492 else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
11494 else if (element == EL_EXIT_CLOSED)
11496 else if (element == EL_EM_EXIT_CLOSED)
11498 else if (element == EL_STEEL_EXIT_CLOSED)
11499 CheckExitSteel(x, y);
11500 else if (element == EL_EM_STEEL_EXIT_CLOSED)
11501 CheckExitSteelEM(x, y);
11502 else if (element == EL_SP_EXIT_CLOSED)
11504 else if (element == EL_EXPANDABLE_WALL_GROWING ||
11505 element == EL_EXPANDABLE_STEELWALL_GROWING)
11506 MauerWaechst(x, y);
11507 else if (element == EL_EXPANDABLE_WALL ||
11508 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
11509 element == EL_EXPANDABLE_WALL_VERTICAL ||
11510 element == EL_EXPANDABLE_WALL_ANY ||
11511 element == EL_BD_EXPANDABLE_WALL)
11512 MauerAbleger(x, y);
11513 else if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
11514 element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
11515 element == EL_EXPANDABLE_STEELWALL_ANY)
11516 MauerAblegerStahl(x, y);
11517 else if (element == EL_FLAMES)
11518 CheckForDragon(x, y);
11519 else if (element == EL_EXPLOSION)
11520 ; /* drawing of correct explosion animation is handled separately */
11521 else if (element == EL_ELEMENT_SNAPPING ||
11522 element == EL_DIAGONAL_SHRINKING ||
11523 element == EL_DIAGONAL_GROWING)
11525 graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
11527 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11529 else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
11530 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11532 if (IS_BELT_ACTIVE(element))
11533 PlayLevelSoundAction(x, y, ACTION_ACTIVE);
11535 if (game.magic_wall_active)
11537 int jx = local_player->jx, jy = local_player->jy;
11539 /* play the element sound at the position nearest to the player */
11540 if ((element == EL_MAGIC_WALL_FULL ||
11541 element == EL_MAGIC_WALL_ACTIVE ||
11542 element == EL_MAGIC_WALL_EMPTYING ||
11543 element == EL_BD_MAGIC_WALL_FULL ||
11544 element == EL_BD_MAGIC_WALL_ACTIVE ||
11545 element == EL_BD_MAGIC_WALL_EMPTYING ||
11546 element == EL_DC_MAGIC_WALL_FULL ||
11547 element == EL_DC_MAGIC_WALL_ACTIVE ||
11548 element == EL_DC_MAGIC_WALL_EMPTYING) &&
11549 ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
11557 #if USE_NEW_AMOEBA_CODE
11558 /* new experimental amoeba growth stuff */
11559 if (!(FrameCounter % 8))
11561 static unsigned int random = 1684108901;
11563 for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
11565 x = RND(lev_fieldx);
11566 y = RND(lev_fieldy);
11567 element = Feld[x][y];
11569 if (!IS_PLAYER(x,y) &&
11570 (element == EL_EMPTY ||
11571 CAN_GROW_INTO(element) ||
11572 element == EL_QUICKSAND_EMPTY ||
11573 element == EL_QUICKSAND_FAST_EMPTY ||
11574 element == EL_ACID_SPLASH_LEFT ||
11575 element == EL_ACID_SPLASH_RIGHT))
11577 if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
11578 (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
11579 (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
11580 (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
11581 Feld[x][y] = EL_AMOEBA_DROP;
11584 random = random * 129 + 1;
11589 game.explosions_delayed = FALSE;
11591 SCAN_PLAYFIELD(x, y)
11593 element = Feld[x][y];
11595 if (ExplodeField[x][y])
11596 Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
11597 else if (element == EL_EXPLOSION)
11598 Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
11600 ExplodeField[x][y] = EX_TYPE_NONE;
11603 game.explosions_delayed = TRUE;
11605 if (game.magic_wall_active)
11607 if (!(game.magic_wall_time_left % 4))
11609 int element = Feld[magic_wall_x][magic_wall_y];
11611 if (element == EL_BD_MAGIC_WALL_FULL ||
11612 element == EL_BD_MAGIC_WALL_ACTIVE ||
11613 element == EL_BD_MAGIC_WALL_EMPTYING)
11614 PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
11615 else if (element == EL_DC_MAGIC_WALL_FULL ||
11616 element == EL_DC_MAGIC_WALL_ACTIVE ||
11617 element == EL_DC_MAGIC_WALL_EMPTYING)
11618 PlayLevelSound(magic_wall_x, magic_wall_y, SND_DC_MAGIC_WALL_ACTIVE);
11620 PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
11623 if (game.magic_wall_time_left > 0)
11625 game.magic_wall_time_left--;
11627 if (!game.magic_wall_time_left)
11629 SCAN_PLAYFIELD(x, y)
11631 element = Feld[x][y];
11633 if (element == EL_MAGIC_WALL_ACTIVE ||
11634 element == EL_MAGIC_WALL_FULL)
11636 Feld[x][y] = EL_MAGIC_WALL_DEAD;
11637 TEST_DrawLevelField(x, y);
11639 else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
11640 element == EL_BD_MAGIC_WALL_FULL)
11642 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
11643 TEST_DrawLevelField(x, y);
11645 else if (element == EL_DC_MAGIC_WALL_ACTIVE ||
11646 element == EL_DC_MAGIC_WALL_FULL)
11648 Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
11649 TEST_DrawLevelField(x, y);
11653 game.magic_wall_active = FALSE;
11658 if (game.light_time_left > 0)
11660 game.light_time_left--;
11662 if (game.light_time_left == 0)
11663 RedrawAllLightSwitchesAndInvisibleElements();
11666 if (game.timegate_time_left > 0)
11668 game.timegate_time_left--;
11670 if (game.timegate_time_left == 0)
11671 CloseAllOpenTimegates();
11674 if (game.lenses_time_left > 0)
11676 game.lenses_time_left--;
11678 if (game.lenses_time_left == 0)
11679 RedrawAllInvisibleElementsForLenses();
11682 if (game.magnify_time_left > 0)
11684 game.magnify_time_left--;
11686 if (game.magnify_time_left == 0)
11687 RedrawAllInvisibleElementsForMagnifier();
11690 for (i = 0; i < MAX_PLAYERS; i++)
11692 struct PlayerInfo *player = &stored_player[i];
11694 if (SHIELD_ON(player))
11696 if (player->shield_deadly_time_left)
11697 PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
11698 else if (player->shield_normal_time_left)
11699 PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
11703 #if USE_DELAYED_GFX_REDRAW
11704 SCAN_PLAYFIELD(x, y)
11706 if (GfxRedraw[x][y] != GFX_REDRAW_NONE)
11708 /* !!! PROBLEM: THIS REDRAWS THE PLAYFIELD _AFTER_ THE SCAN, BUT TILES
11709 !!! MAY HAVE CHANGED AFTER BEING DRAWN DURING PLAYFIELD SCAN !!! */
11711 if (GfxRedraw[x][y] & GFX_REDRAW_TILE)
11712 DrawLevelField(x, y);
11714 if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED)
11715 DrawLevelFieldCrumbled(x, y);
11717 if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS)
11718 DrawLevelFieldCrumbledNeighbours(x, y);
11720 if (GfxRedraw[x][y] & GFX_REDRAW_TILE_TWINKLED)
11721 DrawTwinkleOnField(x, y);
11724 GfxRedraw[x][y] = GFX_REDRAW_NONE;
11731 PlayAllPlayersSound();
11733 if (options.debug) /* calculate frames per second */
11735 static unsigned int fps_counter = 0;
11736 static int fps_frames = 0;
11737 unsigned int fps_delay_ms = Counter() - fps_counter;
11741 if (fps_delay_ms >= 500) /* calculate fps every 0.5 seconds */
11743 global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
11746 fps_counter = Counter();
11749 redraw_mask |= REDRAW_FPS;
11752 AdvanceFrameAndPlayerCounters(-1); /* advance counters for all players */
11754 if (local_player->show_envelope != 0 && local_player->MovPos == 0)
11756 ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
11758 local_player->show_envelope = 0;
11761 /* use random number generator in every frame to make it less predictable */
11762 if (game.engine_version >= VERSION_IDENT(3,1,1,0))
11766 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
11768 int min_x = x, min_y = y, max_x = x, max_y = y;
11771 for (i = 0; i < MAX_PLAYERS; i++)
11773 int jx = stored_player[i].jx, jy = stored_player[i].jy;
11775 if (!stored_player[i].active || &stored_player[i] == player)
11778 min_x = MIN(min_x, jx);
11779 min_y = MIN(min_y, jy);
11780 max_x = MAX(max_x, jx);
11781 max_y = MAX(max_y, jy);
11784 return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
11787 static boolean AllPlayersInVisibleScreen()
11791 for (i = 0; i < MAX_PLAYERS; i++)
11793 int jx = stored_player[i].jx, jy = stored_player[i].jy;
11795 if (!stored_player[i].active)
11798 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
11805 void ScrollLevel(int dx, int dy)
11807 int softscroll_offset = (setup.soft_scrolling ? 2 * TILEX_VAR : 0);
11810 BlitBitmap(drawto_field, drawto_field,
11811 FX + TILEX_VAR * (dx == -1) - softscroll_offset,
11812 FY + TILEY_VAR * (dy == -1) - softscroll_offset,
11813 SXSIZE - TILEX_VAR * (dx != 0) + 2 * softscroll_offset,
11814 SYSIZE - TILEY_VAR * (dy != 0) + 2 * softscroll_offset,
11815 FX + TILEX_VAR * (dx == 1) - softscroll_offset,
11816 FY + TILEY_VAR * (dy == 1) - softscroll_offset);
11820 x = (dx == 1 ? BX1 : BX2);
11821 for (y = BY1; y <= BY2; y++)
11822 DrawScreenField(x, y);
11827 y = (dy == 1 ? BY1 : BY2);
11828 for (x = BX1; x <= BX2; x++)
11829 DrawScreenField(x, y);
11832 redraw_mask |= REDRAW_FIELD;
11835 static boolean canFallDown(struct PlayerInfo *player)
11837 int jx = player->jx, jy = player->jy;
11839 return (IN_LEV_FIELD(jx, jy + 1) &&
11840 (IS_FREE(jx, jy + 1) ||
11841 (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
11842 IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
11843 !IS_WALKABLE_INSIDE(Feld[jx][jy]));
11846 static boolean canPassField(int x, int y, int move_dir)
11848 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
11849 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
11850 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
11851 int nextx = x + dx;
11852 int nexty = y + dy;
11853 int element = Feld[x][y];
11855 return (IS_PASSABLE_FROM(element, opposite_dir) &&
11856 !CAN_MOVE(element) &&
11857 IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
11858 IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
11859 (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
11862 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
11864 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
11865 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
11866 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
11870 return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
11871 IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
11872 (IS_DIGGABLE(Feld[newx][newy]) ||
11873 IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
11874 canPassField(newx, newy, move_dir)));
11877 static void CheckGravityMovement(struct PlayerInfo *player)
11879 if (player->gravity && !player->programmed_action)
11881 int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
11882 int move_dir_vertical = player->effective_action & MV_VERTICAL;
11883 boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
11884 int jx = player->jx, jy = player->jy;
11885 boolean player_is_moving_to_valid_field =
11886 (!player_is_snapping &&
11887 (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
11888 canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
11889 boolean player_can_fall_down = canFallDown(player);
11891 if (player_can_fall_down &&
11892 !player_is_moving_to_valid_field)
11893 player->programmed_action = MV_DOWN;
11897 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
11899 return CheckGravityMovement(player);
11901 if (player->gravity && !player->programmed_action)
11903 int jx = player->jx, jy = player->jy;
11904 boolean field_under_player_is_free =
11905 (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
11906 boolean player_is_standing_on_valid_field =
11907 (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
11908 (IS_WALKABLE(Feld[jx][jy]) &&
11909 !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
11911 if (field_under_player_is_free && !player_is_standing_on_valid_field)
11912 player->programmed_action = MV_DOWN;
11917 MovePlayerOneStep()
11918 -----------------------------------------------------------------------------
11919 dx, dy: direction (non-diagonal) to try to move the player to
11920 real_dx, real_dy: direction as read from input device (can be diagonal)
11923 boolean MovePlayerOneStep(struct PlayerInfo *player,
11924 int dx, int dy, int real_dx, int real_dy)
11926 int jx = player->jx, jy = player->jy;
11927 int new_jx = jx + dx, new_jy = jy + dy;
11929 boolean player_can_move = !player->cannot_move;
11931 if (!player->active || (!dx && !dy))
11932 return MP_NO_ACTION;
11934 player->MovDir = (dx < 0 ? MV_LEFT :
11935 dx > 0 ? MV_RIGHT :
11937 dy > 0 ? MV_DOWN : MV_NONE);
11939 if (!IN_LEV_FIELD(new_jx, new_jy))
11940 return MP_NO_ACTION;
11942 if (!player_can_move)
11944 if (player->MovPos == 0)
11946 player->is_moving = FALSE;
11947 player->is_digging = FALSE;
11948 player->is_collecting = FALSE;
11949 player->is_snapping = FALSE;
11950 player->is_pushing = FALSE;
11954 if (!options.network && game.centered_player_nr == -1 &&
11955 !AllPlayersInSight(player, new_jx, new_jy))
11956 return MP_NO_ACTION;
11958 can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
11959 if (can_move != MP_MOVING)
11962 /* check if DigField() has caused relocation of the player */
11963 if (player->jx != jx || player->jy != jy)
11964 return MP_NO_ACTION; /* <-- !!! CHECK THIS [-> MP_ACTION ?] !!! */
11966 StorePlayer[jx][jy] = 0;
11967 player->last_jx = jx;
11968 player->last_jy = jy;
11969 player->jx = new_jx;
11970 player->jy = new_jy;
11971 StorePlayer[new_jx][new_jy] = player->element_nr;
11973 if (player->move_delay_value_next != -1)
11975 player->move_delay_value = player->move_delay_value_next;
11976 player->move_delay_value_next = -1;
11980 (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
11982 player->step_counter++;
11984 PlayerVisit[jx][jy] = FrameCounter;
11986 player->is_moving = TRUE;
11989 /* should better be called in MovePlayer(), but this breaks some tapes */
11990 ScrollPlayer(player, SCROLL_INIT);
11996 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
11998 int jx = player->jx, jy = player->jy;
11999 int old_jx = jx, old_jy = jy;
12000 int moved = MP_NO_ACTION;
12002 if (!player->active)
12007 if (player->MovPos == 0)
12009 player->is_moving = FALSE;
12010 player->is_digging = FALSE;
12011 player->is_collecting = FALSE;
12012 player->is_snapping = FALSE;
12013 player->is_pushing = FALSE;
12019 if (player->move_delay > 0)
12022 player->move_delay = -1; /* set to "uninitialized" value */
12024 /* store if player is automatically moved to next field */
12025 player->is_auto_moving = (player->programmed_action != MV_NONE);
12027 /* remove the last programmed player action */
12028 player->programmed_action = 0;
12030 if (player->MovPos)
12032 /* should only happen if pre-1.2 tape recordings are played */
12033 /* this is only for backward compatibility */
12035 int original_move_delay_value = player->move_delay_value;
12038 printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%d]\n",
12042 /* scroll remaining steps with finest movement resolution */
12043 player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
12045 while (player->MovPos)
12047 ScrollPlayer(player, SCROLL_GO_ON);
12048 ScrollScreen(NULL, SCROLL_GO_ON);
12050 AdvanceFrameAndPlayerCounters(player->index_nr);
12056 player->move_delay_value = original_move_delay_value;
12059 player->is_active = FALSE;
12061 if (player->last_move_dir & MV_HORIZONTAL)
12063 if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
12064 moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
12068 if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
12069 moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
12072 if (!moved && !player->is_active)
12074 player->is_moving = FALSE;
12075 player->is_digging = FALSE;
12076 player->is_collecting = FALSE;
12077 player->is_snapping = FALSE;
12078 player->is_pushing = FALSE;
12084 if (moved & MP_MOVING && !ScreenMovPos &&
12085 (player->index_nr == game.centered_player_nr ||
12086 game.centered_player_nr == -1))
12088 int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
12089 int offset = game.scroll_delay_value;
12091 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12093 /* actual player has left the screen -- scroll in that direction */
12094 if (jx != old_jx) /* player has moved horizontally */
12095 scroll_x += (jx - old_jx);
12096 else /* player has moved vertically */
12097 scroll_y += (jy - old_jy);
12101 if (jx != old_jx) /* player has moved horizontally */
12103 if ((player->MovDir == MV_LEFT && scroll_x > jx - MIDPOSX + offset) ||
12104 (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
12105 scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
12107 /* don't scroll over playfield boundaries */
12108 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
12109 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
12111 /* don't scroll more than one field at a time */
12112 scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
12114 /* don't scroll against the player's moving direction */
12115 if ((player->MovDir == MV_LEFT && scroll_x > old_scroll_x) ||
12116 (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
12117 scroll_x = old_scroll_x;
12119 else /* player has moved vertically */
12121 if ((player->MovDir == MV_UP && scroll_y > jy - MIDPOSY + offset) ||
12122 (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
12123 scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
12125 /* don't scroll over playfield boundaries */
12126 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
12127 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
12129 /* don't scroll more than one field at a time */
12130 scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
12132 /* don't scroll against the player's moving direction */
12133 if ((player->MovDir == MV_UP && scroll_y > old_scroll_y) ||
12134 (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
12135 scroll_y = old_scroll_y;
12139 if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
12141 if (!options.network && game.centered_player_nr == -1 &&
12142 !AllPlayersInVisibleScreen())
12144 scroll_x = old_scroll_x;
12145 scroll_y = old_scroll_y;
12149 ScrollScreen(player, SCROLL_INIT);
12150 ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
12155 player->StepFrame = 0;
12157 if (moved & MP_MOVING)
12159 if (old_jx != jx && old_jy == jy)
12160 player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
12161 else if (old_jx == jx && old_jy != jy)
12162 player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
12164 TEST_DrawLevelField(jx, jy); /* for "crumbled sand" */
12166 player->last_move_dir = player->MovDir;
12167 player->is_moving = TRUE;
12168 player->is_snapping = FALSE;
12169 player->is_switching = FALSE;
12170 player->is_dropping = FALSE;
12171 player->is_dropping_pressed = FALSE;
12172 player->drop_pressed_delay = 0;
12175 /* should better be called here than above, but this breaks some tapes */
12176 ScrollPlayer(player, SCROLL_INIT);
12181 CheckGravityMovementWhenNotMoving(player);
12183 player->is_moving = FALSE;
12185 /* at this point, the player is allowed to move, but cannot move right now
12186 (e.g. because of something blocking the way) -- ensure that the player
12187 is also allowed to move in the next frame (in old versions before 3.1.1,
12188 the player was forced to wait again for eight frames before next try) */
12190 if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12191 player->move_delay = 0; /* allow direct movement in the next frame */
12194 if (player->move_delay == -1) /* not yet initialized by DigField() */
12195 player->move_delay = player->move_delay_value;
12197 if (game.engine_version < VERSION_IDENT(3,0,7,0))
12199 TestIfPlayerTouchesBadThing(jx, jy);
12200 TestIfPlayerTouchesCustomElement(jx, jy);
12203 if (!player->active)
12204 RemovePlayer(player);
12209 void ScrollPlayer(struct PlayerInfo *player, int mode)
12211 int jx = player->jx, jy = player->jy;
12212 int last_jx = player->last_jx, last_jy = player->last_jy;
12213 int move_stepsize = TILEX / player->move_delay_value;
12215 if (!player->active)
12218 if (player->MovPos == 0 && mode == SCROLL_GO_ON) /* player not moving */
12221 if (mode == SCROLL_INIT)
12223 player->actual_frame_counter = FrameCounter;
12224 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
12226 if ((player->block_last_field || player->block_delay_adjustment > 0) &&
12227 Feld[last_jx][last_jy] == EL_EMPTY)
12229 int last_field_block_delay = 0; /* start with no blocking at all */
12230 int block_delay_adjustment = player->block_delay_adjustment;
12232 /* if player blocks last field, add delay for exactly one move */
12233 if (player->block_last_field)
12235 last_field_block_delay += player->move_delay_value;
12237 /* when blocking enabled, prevent moving up despite gravity */
12238 if (player->gravity && player->MovDir == MV_UP)
12239 block_delay_adjustment = -1;
12242 /* add block delay adjustment (also possible when not blocking) */
12243 last_field_block_delay += block_delay_adjustment;
12245 Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
12246 MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
12249 if (player->MovPos != 0) /* player has not yet reached destination */
12252 else if (!FrameReached(&player->actual_frame_counter, 1))
12255 if (player->MovPos != 0)
12257 player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
12258 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
12260 /* before DrawPlayer() to draw correct player graphic for this case */
12261 if (player->MovPos == 0)
12262 CheckGravityMovement(player);
12265 if (player->MovPos == 0) /* player reached destination field */
12267 if (player->move_delay_reset_counter > 0)
12269 player->move_delay_reset_counter--;
12271 if (player->move_delay_reset_counter == 0)
12273 /* continue with normal speed after quickly moving through gate */
12274 HALVE_PLAYER_SPEED(player);
12276 /* be able to make the next move without delay */
12277 player->move_delay = 0;
12281 player->last_jx = jx;
12282 player->last_jy = jy;
12284 if (Feld[jx][jy] == EL_EXIT_OPEN ||
12285 Feld[jx][jy] == EL_EM_EXIT_OPEN ||
12286 Feld[jx][jy] == EL_EM_EXIT_OPENING ||
12287 Feld[jx][jy] == EL_STEEL_EXIT_OPEN ||
12288 Feld[jx][jy] == EL_EM_STEEL_EXIT_OPEN ||
12289 Feld[jx][jy] == EL_EM_STEEL_EXIT_OPENING ||
12290 Feld[jx][jy] == EL_SP_EXIT_OPEN ||
12291 Feld[jx][jy] == EL_SP_EXIT_OPENING) /* <-- special case */
12293 DrawPlayer(player); /* needed here only to cleanup last field */
12294 RemovePlayer(player);
12296 if (local_player->friends_still_needed == 0 ||
12297 IS_SP_ELEMENT(Feld[jx][jy]))
12298 PlayerWins(player);
12301 /* this breaks one level: "machine", level 000 */
12303 int move_direction = player->MovDir;
12304 int enter_side = MV_DIR_OPPOSITE(move_direction);
12305 int leave_side = move_direction;
12306 int old_jx = last_jx;
12307 int old_jy = last_jy;
12308 int old_element = Feld[old_jx][old_jy];
12309 int new_element = Feld[jx][jy];
12311 if (IS_CUSTOM_ELEMENT(old_element))
12312 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
12314 player->index_bit, leave_side);
12316 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
12317 CE_PLAYER_LEAVES_X,
12318 player->index_bit, leave_side);
12320 if (IS_CUSTOM_ELEMENT(new_element))
12321 CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
12322 player->index_bit, enter_side);
12324 CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
12325 CE_PLAYER_ENTERS_X,
12326 player->index_bit, enter_side);
12328 CheckTriggeredElementChangeBySide(jx, jy, player->initial_element,
12329 CE_MOVE_OF_X, move_direction);
12332 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
12334 TestIfPlayerTouchesBadThing(jx, jy);
12335 TestIfPlayerTouchesCustomElement(jx, jy);
12337 /* needed because pushed element has not yet reached its destination,
12338 so it would trigger a change event at its previous field location */
12339 if (!player->is_pushing)
12340 TestIfElementTouchesCustomElement(jx, jy); /* for empty space */
12342 if (!player->active)
12343 RemovePlayer(player);
12346 if (!local_player->LevelSolved && level.use_step_counter)
12356 if (TimeLeft <= 10 && setup.time_limit)
12357 PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
12359 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
12361 DisplayGameControlValues();
12363 if (!TimeLeft && setup.time_limit)
12364 for (i = 0; i < MAX_PLAYERS; i++)
12365 KillPlayer(&stored_player[i]);
12367 else if (game.no_time_limit && !AllPlayersGone) /* level w/o time limit */
12369 game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
12371 DisplayGameControlValues();
12375 if (tape.single_step && tape.recording && !tape.pausing &&
12376 !player->programmed_action)
12377 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
12379 if (!player->programmed_action)
12380 CheckSaveEngineSnapshot(player);
12384 void ScrollScreen(struct PlayerInfo *player, int mode)
12386 static unsigned int screen_frame_counter = 0;
12388 if (mode == SCROLL_INIT)
12390 /* set scrolling step size according to actual player's moving speed */
12391 ScrollStepSize = TILEX / player->move_delay_value;
12393 screen_frame_counter = FrameCounter;
12394 ScreenMovDir = player->MovDir;
12395 ScreenMovPos = player->MovPos;
12396 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
12399 else if (!FrameReached(&screen_frame_counter, 1))
12404 ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
12405 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
12406 redraw_mask |= REDRAW_FIELD;
12409 ScreenMovDir = MV_NONE;
12412 void TestIfPlayerTouchesCustomElement(int x, int y)
12414 static int xy[4][2] =
12421 static int trigger_sides[4][2] =
12423 /* center side border side */
12424 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
12425 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
12426 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
12427 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
12429 static int touch_dir[4] =
12431 MV_LEFT | MV_RIGHT,
12436 int center_element = Feld[x][y]; /* should always be non-moving! */
12439 for (i = 0; i < NUM_DIRECTIONS; i++)
12441 int xx = x + xy[i][0];
12442 int yy = y + xy[i][1];
12443 int center_side = trigger_sides[i][0];
12444 int border_side = trigger_sides[i][1];
12445 int border_element;
12447 if (!IN_LEV_FIELD(xx, yy))
12450 if (IS_PLAYER(x, y)) /* player found at center element */
12452 struct PlayerInfo *player = PLAYERINFO(x, y);
12454 if (game.engine_version < VERSION_IDENT(3,0,7,0))
12455 border_element = Feld[xx][yy]; /* may be moving! */
12456 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
12457 border_element = Feld[xx][yy];
12458 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
12459 border_element = MovingOrBlocked2Element(xx, yy);
12461 continue; /* center and border element do not touch */
12463 CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
12464 player->index_bit, border_side);
12465 CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
12466 CE_PLAYER_TOUCHES_X,
12467 player->index_bit, border_side);
12470 /* use player element that is initially defined in the level playfield,
12471 not the player element that corresponds to the runtime player number
12472 (example: a level that contains EL_PLAYER_3 as the only player would
12473 incorrectly give EL_PLAYER_1 for "player->element_nr") */
12474 int player_element = PLAYERINFO(x, y)->initial_element;
12476 CheckElementChangeBySide(xx, yy, border_element, player_element,
12477 CE_TOUCHING_X, border_side);
12480 else if (IS_PLAYER(xx, yy)) /* player found at border element */
12482 struct PlayerInfo *player = PLAYERINFO(xx, yy);
12484 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
12486 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
12487 continue; /* center and border element do not touch */
12490 CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
12491 player->index_bit, center_side);
12492 CheckTriggeredElementChangeByPlayer(x, y, center_element,
12493 CE_PLAYER_TOUCHES_X,
12494 player->index_bit, center_side);
12497 /* use player element that is initially defined in the level playfield,
12498 not the player element that corresponds to the runtime player number
12499 (example: a level that contains EL_PLAYER_3 as the only player would
12500 incorrectly give EL_PLAYER_1 for "player->element_nr") */
12501 int player_element = PLAYERINFO(xx, yy)->initial_element;
12503 CheckElementChangeBySide(x, y, center_element, player_element,
12504 CE_TOUCHING_X, center_side);
12512 void TestIfElementTouchesCustomElement(int x, int y)
12514 static int xy[4][2] =
12521 static int trigger_sides[4][2] =
12523 /* center side border side */
12524 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
12525 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
12526 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
12527 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
12529 static int touch_dir[4] =
12531 MV_LEFT | MV_RIGHT,
12536 boolean change_center_element = FALSE;
12537 int center_element = Feld[x][y]; /* should always be non-moving! */
12538 int border_element_old[NUM_DIRECTIONS];
12541 for (i = 0; i < NUM_DIRECTIONS; i++)
12543 int xx = x + xy[i][0];
12544 int yy = y + xy[i][1];
12545 int border_element;
12547 border_element_old[i] = -1;
12549 if (!IN_LEV_FIELD(xx, yy))
12552 if (game.engine_version < VERSION_IDENT(3,0,7,0))
12553 border_element = Feld[xx][yy]; /* may be moving! */
12554 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
12555 border_element = Feld[xx][yy];
12556 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
12557 border_element = MovingOrBlocked2Element(xx, yy);
12559 continue; /* center and border element do not touch */
12561 border_element_old[i] = border_element;
12564 for (i = 0; i < NUM_DIRECTIONS; i++)
12566 int xx = x + xy[i][0];
12567 int yy = y + xy[i][1];
12568 int center_side = trigger_sides[i][0];
12569 int border_element = border_element_old[i];
12571 if (border_element == -1)
12574 /* check for change of border element */
12575 CheckElementChangeBySide(xx, yy, border_element, center_element,
12576 CE_TOUCHING_X, center_side);
12578 /* (center element cannot be player, so we dont have to check this here) */
12581 for (i = 0; i < NUM_DIRECTIONS; i++)
12583 int xx = x + xy[i][0];
12584 int yy = y + xy[i][1];
12585 int border_side = trigger_sides[i][1];
12586 int border_element = border_element_old[i];
12588 if (border_element == -1)
12591 /* check for change of center element (but change it only once) */
12592 if (!change_center_element)
12593 change_center_element =
12594 CheckElementChangeBySide(x, y, center_element, border_element,
12595 CE_TOUCHING_X, border_side);
12597 if (IS_PLAYER(xx, yy))
12599 /* use player element that is initially defined in the level playfield,
12600 not the player element that corresponds to the runtime player number
12601 (example: a level that contains EL_PLAYER_3 as the only player would
12602 incorrectly give EL_PLAYER_1 for "player->element_nr") */
12603 int player_element = PLAYERINFO(xx, yy)->initial_element;
12605 CheckElementChangeBySide(x, y, center_element, player_element,
12606 CE_TOUCHING_X, border_side);
12611 void TestIfElementHitsCustomElement(int x, int y, int direction)
12613 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
12614 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
12615 int hitx = x + dx, hity = y + dy;
12616 int hitting_element = Feld[x][y];
12617 int touched_element;
12619 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
12622 touched_element = (IN_LEV_FIELD(hitx, hity) ?
12623 MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
12625 if (IN_LEV_FIELD(hitx, hity))
12627 int opposite_direction = MV_DIR_OPPOSITE(direction);
12628 int hitting_side = direction;
12629 int touched_side = opposite_direction;
12630 boolean object_hit = (!IS_MOVING(hitx, hity) ||
12631 MovDir[hitx][hity] != direction ||
12632 ABS(MovPos[hitx][hity]) <= TILEY / 2);
12638 CheckElementChangeBySide(x, y, hitting_element, touched_element,
12639 CE_HITTING_X, touched_side);
12641 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
12642 CE_HIT_BY_X, hitting_side);
12644 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
12645 CE_HIT_BY_SOMETHING, opposite_direction);
12647 if (IS_PLAYER(hitx, hity))
12649 /* use player element that is initially defined in the level playfield,
12650 not the player element that corresponds to the runtime player number
12651 (example: a level that contains EL_PLAYER_3 as the only player would
12652 incorrectly give EL_PLAYER_1 for "player->element_nr") */
12653 int player_element = PLAYERINFO(hitx, hity)->initial_element;
12655 CheckElementChangeBySide(x, y, hitting_element, player_element,
12656 CE_HITTING_X, touched_side);
12661 /* "hitting something" is also true when hitting the playfield border */
12662 CheckElementChangeBySide(x, y, hitting_element, touched_element,
12663 CE_HITTING_SOMETHING, direction);
12666 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
12668 int i, kill_x = -1, kill_y = -1;
12670 int bad_element = -1;
12671 static int test_xy[4][2] =
12678 static int test_dir[4] =
12686 for (i = 0; i < NUM_DIRECTIONS; i++)
12688 int test_x, test_y, test_move_dir, test_element;
12690 test_x = good_x + test_xy[i][0];
12691 test_y = good_y + test_xy[i][1];
12693 if (!IN_LEV_FIELD(test_x, test_y))
12697 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
12699 test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
12701 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
12702 2nd case: DONT_TOUCH style bad thing does not move away from good thing
12704 if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
12705 (DONT_TOUCH(test_element) && test_move_dir != test_dir[i]))
12709 bad_element = test_element;
12715 if (kill_x != -1 || kill_y != -1)
12717 if (IS_PLAYER(good_x, good_y))
12719 struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
12721 if (player->shield_deadly_time_left > 0 &&
12722 !IS_INDESTRUCTIBLE(bad_element))
12723 Bang(kill_x, kill_y);
12724 else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
12725 KillPlayer(player);
12728 Bang(good_x, good_y);
12732 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
12734 int i, kill_x = -1, kill_y = -1;
12735 int bad_element = Feld[bad_x][bad_y];
12736 static int test_xy[4][2] =
12743 static int touch_dir[4] =
12745 MV_LEFT | MV_RIGHT,
12750 static int test_dir[4] =
12758 if (bad_element == EL_EXPLOSION) /* skip just exploding bad things */
12761 for (i = 0; i < NUM_DIRECTIONS; i++)
12763 int test_x, test_y, test_move_dir, test_element;
12765 test_x = bad_x + test_xy[i][0];
12766 test_y = bad_y + test_xy[i][1];
12768 if (!IN_LEV_FIELD(test_x, test_y))
12772 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
12774 test_element = Feld[test_x][test_y];
12776 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
12777 2nd case: DONT_TOUCH style bad thing does not move away from good thing
12779 if ((DONT_RUN_INTO(bad_element) && bad_move_dir == test_dir[i]) ||
12780 (DONT_TOUCH(bad_element) && test_move_dir != test_dir[i]))
12782 /* good thing is player or penguin that does not move away */
12783 if (IS_PLAYER(test_x, test_y))
12785 struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
12787 if (bad_element == EL_ROBOT && player->is_moving)
12788 continue; /* robot does not kill player if he is moving */
12790 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
12792 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
12793 continue; /* center and border element do not touch */
12801 else if (test_element == EL_PENGUIN)
12811 if (kill_x != -1 || kill_y != -1)
12813 if (IS_PLAYER(kill_x, kill_y))
12815 struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
12817 if (player->shield_deadly_time_left > 0 &&
12818 !IS_INDESTRUCTIBLE(bad_element))
12819 Bang(bad_x, bad_y);
12820 else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
12821 KillPlayer(player);
12824 Bang(kill_x, kill_y);
12828 void TestIfGoodThingGetsHitByBadThing(int bad_x, int bad_y, int bad_move_dir)
12830 int bad_element = Feld[bad_x][bad_y];
12831 int dx = (bad_move_dir == MV_LEFT ? -1 : bad_move_dir == MV_RIGHT ? +1 : 0);
12832 int dy = (bad_move_dir == MV_UP ? -1 : bad_move_dir == MV_DOWN ? +1 : 0);
12833 int test_x = bad_x + dx, test_y = bad_y + dy;
12834 int test_move_dir, test_element;
12835 int kill_x = -1, kill_y = -1;
12837 if (!IN_LEV_FIELD(test_x, test_y))
12841 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
12843 test_element = Feld[test_x][test_y];
12845 if (test_move_dir != bad_move_dir)
12847 /* good thing can be player or penguin that does not move away */
12848 if (IS_PLAYER(test_x, test_y))
12850 struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
12852 /* (note: in comparison to DONT_RUN_TO and DONT_TOUCH, also handle the
12853 player as being hit when he is moving towards the bad thing, because
12854 the "get hit by" condition would be lost after the player stops) */
12855 if (player->MovPos != 0 && player->MovDir == bad_move_dir)
12856 return; /* player moves away from bad thing */
12861 else if (test_element == EL_PENGUIN)
12868 if (kill_x != -1 || kill_y != -1)
12870 if (IS_PLAYER(kill_x, kill_y))
12872 struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
12874 if (player->shield_deadly_time_left > 0 &&
12875 !IS_INDESTRUCTIBLE(bad_element))
12876 Bang(bad_x, bad_y);
12877 else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
12878 KillPlayer(player);
12881 Bang(kill_x, kill_y);
12885 void TestIfPlayerTouchesBadThing(int x, int y)
12887 TestIfGoodThingHitsBadThing(x, y, MV_NONE);
12890 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
12892 TestIfGoodThingHitsBadThing(x, y, move_dir);
12895 void TestIfBadThingTouchesPlayer(int x, int y)
12897 TestIfBadThingHitsGoodThing(x, y, MV_NONE);
12900 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
12902 TestIfBadThingHitsGoodThing(x, y, move_dir);
12905 void TestIfFriendTouchesBadThing(int x, int y)
12907 TestIfGoodThingHitsBadThing(x, y, MV_NONE);
12910 void TestIfBadThingTouchesFriend(int x, int y)
12912 TestIfBadThingHitsGoodThing(x, y, MV_NONE);
12915 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
12917 int i, kill_x = bad_x, kill_y = bad_y;
12918 static int xy[4][2] =
12926 for (i = 0; i < NUM_DIRECTIONS; i++)
12930 x = bad_x + xy[i][0];
12931 y = bad_y + xy[i][1];
12932 if (!IN_LEV_FIELD(x, y))
12935 element = Feld[x][y];
12936 if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
12937 element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
12945 if (kill_x != bad_x || kill_y != bad_y)
12946 Bang(bad_x, bad_y);
12949 void KillPlayer(struct PlayerInfo *player)
12951 int jx = player->jx, jy = player->jy;
12953 if (!player->active)
12957 printf("::: 0: killed == %d, active == %d, reanimated == %d\n",
12958 player->killed, player->active, player->reanimated);
12961 /* the following code was introduced to prevent an infinite loop when calling
12963 -> CheckTriggeredElementChangeExt()
12964 -> ExecuteCustomElementAction()
12966 -> (infinitely repeating the above sequence of function calls)
12967 which occurs when killing the player while having a CE with the setting
12968 "kill player X when explosion of <player X>"; the solution using a new
12969 field "player->killed" was chosen for backwards compatibility, although
12970 clever use of the fields "player->active" etc. would probably also work */
12972 if (player->killed)
12976 player->killed = TRUE;
12978 /* remove accessible field at the player's position */
12979 Feld[jx][jy] = EL_EMPTY;
12981 /* deactivate shield (else Bang()/Explode() would not work right) */
12982 player->shield_normal_time_left = 0;
12983 player->shield_deadly_time_left = 0;
12986 printf("::: 1: killed == %d, active == %d, reanimated == %d\n",
12987 player->killed, player->active, player->reanimated);
12993 printf("::: 2: killed == %d, active == %d, reanimated == %d\n",
12994 player->killed, player->active, player->reanimated);
12997 if (player->reanimated) /* killed player may have been reanimated */
12998 player->killed = player->reanimated = FALSE;
13000 BuryPlayer(player);
13003 static void KillPlayerUnlessEnemyProtected(int x, int y)
13005 if (!PLAYER_ENEMY_PROTECTED(x, y))
13006 KillPlayer(PLAYERINFO(x, y));
13009 static void KillPlayerUnlessExplosionProtected(int x, int y)
13011 if (!PLAYER_EXPLOSION_PROTECTED(x, y))
13012 KillPlayer(PLAYERINFO(x, y));
13015 void BuryPlayer(struct PlayerInfo *player)
13017 int jx = player->jx, jy = player->jy;
13019 if (!player->active)
13022 PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
13023 PlayLevelSound(jx, jy, SND_GAME_LOSING);
13025 player->GameOver = TRUE;
13026 RemovePlayer(player);
13029 void RemovePlayer(struct PlayerInfo *player)
13031 int jx = player->jx, jy = player->jy;
13032 int i, found = FALSE;
13034 player->present = FALSE;
13035 player->active = FALSE;
13037 if (!ExplodeField[jx][jy])
13038 StorePlayer[jx][jy] = 0;
13040 if (player->is_moving)
13041 TEST_DrawLevelField(player->last_jx, player->last_jy);
13043 for (i = 0; i < MAX_PLAYERS; i++)
13044 if (stored_player[i].active)
13048 AllPlayersGone = TRUE;
13054 static void setFieldForSnapping(int x, int y, int element, int direction)
13056 struct ElementInfo *ei = &element_info[element];
13057 int direction_bit = MV_DIR_TO_BIT(direction);
13058 int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
13059 int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
13060 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
13062 Feld[x][y] = EL_ELEMENT_SNAPPING;
13063 MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
13065 ResetGfxAnimation(x, y);
13067 GfxElement[x][y] = element;
13068 GfxAction[x][y] = action;
13069 GfxDir[x][y] = direction;
13070 GfxFrame[x][y] = -1;
13074 =============================================================================
13075 checkDiagonalPushing()
13076 -----------------------------------------------------------------------------
13077 check if diagonal input device direction results in pushing of object
13078 (by checking if the alternative direction is walkable, diggable, ...)
13079 =============================================================================
13082 static boolean checkDiagonalPushing(struct PlayerInfo *player,
13083 int x, int y, int real_dx, int real_dy)
13085 int jx, jy, dx, dy, xx, yy;
13087 if (real_dx == 0 || real_dy == 0) /* no diagonal direction => push */
13090 /* diagonal direction: check alternative direction */
13095 xx = jx + (dx == 0 ? real_dx : 0);
13096 yy = jy + (dy == 0 ? real_dy : 0);
13098 return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
13102 =============================================================================
13104 -----------------------------------------------------------------------------
13105 x, y: field next to player (non-diagonal) to try to dig to
13106 real_dx, real_dy: direction as read from input device (can be diagonal)
13107 =============================================================================
13110 static int DigField(struct PlayerInfo *player,
13111 int oldx, int oldy, int x, int y,
13112 int real_dx, int real_dy, int mode)
13114 boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
13115 boolean player_was_pushing = player->is_pushing;
13116 boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
13117 boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
13118 int jx = oldx, jy = oldy;
13119 int dx = x - jx, dy = y - jy;
13120 int nextx = x + dx, nexty = y + dy;
13121 int move_direction = (dx == -1 ? MV_LEFT :
13122 dx == +1 ? MV_RIGHT :
13124 dy == +1 ? MV_DOWN : MV_NONE);
13125 int opposite_direction = MV_DIR_OPPOSITE(move_direction);
13126 int dig_side = MV_DIR_OPPOSITE(move_direction);
13127 int old_element = Feld[jx][jy];
13128 int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
13131 if (is_player) /* function can also be called by EL_PENGUIN */
13133 if (player->MovPos == 0)
13135 player->is_digging = FALSE;
13136 player->is_collecting = FALSE;
13139 if (player->MovPos == 0) /* last pushing move finished */
13140 player->is_pushing = FALSE;
13142 if (mode == DF_NO_PUSH) /* player just stopped pushing */
13144 player->is_switching = FALSE;
13145 player->push_delay = -1;
13147 return MP_NO_ACTION;
13151 if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
13152 old_element = Back[jx][jy];
13154 /* in case of element dropped at player position, check background */
13155 else if (Back[jx][jy] != EL_EMPTY &&
13156 game.engine_version >= VERSION_IDENT(2,2,0,0))
13157 old_element = Back[jx][jy];
13159 if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
13160 return MP_NO_ACTION; /* field has no opening in this direction */
13162 if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
13163 return MP_NO_ACTION; /* field has no opening in this direction */
13165 if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
13169 Feld[jx][jy] = player->artwork_element;
13170 InitMovingField(jx, jy, MV_DOWN);
13171 Store[jx][jy] = EL_ACID;
13172 ContinueMoving(jx, jy);
13173 BuryPlayer(player);
13175 return MP_DONT_RUN_INTO;
13178 if (player_can_move && DONT_RUN_INTO(element))
13180 TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
13182 return MP_DONT_RUN_INTO;
13185 if (IS_MOVING(x, y) || IS_PLAYER(x, y))
13186 return MP_NO_ACTION;
13188 collect_count = element_info[element].collect_count_initial;
13190 if (!is_player && !IS_COLLECTIBLE(element)) /* penguin cannot collect it */
13191 return MP_NO_ACTION;
13193 if (game.engine_version < VERSION_IDENT(2,2,0,0))
13194 player_can_move = player_can_move_or_snap;
13196 if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
13197 game.engine_version >= VERSION_IDENT(2,2,0,0))
13199 CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
13200 player->index_bit, dig_side);
13201 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13202 player->index_bit, dig_side);
13204 if (element == EL_DC_LANDMINE)
13207 if (Feld[x][y] != element) /* field changed by snapping */
13210 return MP_NO_ACTION;
13213 if (player->gravity && is_player && !player->is_auto_moving &&
13214 canFallDown(player) && move_direction != MV_DOWN &&
13215 !canMoveToValidFieldWithGravity(jx, jy, move_direction))
13216 return MP_NO_ACTION; /* player cannot walk here due to gravity */
13218 if (player_can_move &&
13219 IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
13221 int sound_element = SND_ELEMENT(element);
13222 int sound_action = ACTION_WALKING;
13224 if (IS_RND_GATE(element))
13226 if (!player->key[RND_GATE_NR(element)])
13227 return MP_NO_ACTION;
13229 else if (IS_RND_GATE_GRAY(element))
13231 if (!player->key[RND_GATE_GRAY_NR(element)])
13232 return MP_NO_ACTION;
13234 else if (IS_RND_GATE_GRAY_ACTIVE(element))
13236 if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
13237 return MP_NO_ACTION;
13239 else if (element == EL_EXIT_OPEN ||
13240 element == EL_EM_EXIT_OPEN ||
13241 element == EL_EM_EXIT_OPENING ||
13242 element == EL_STEEL_EXIT_OPEN ||
13243 element == EL_EM_STEEL_EXIT_OPEN ||
13244 element == EL_EM_STEEL_EXIT_OPENING ||
13245 element == EL_SP_EXIT_OPEN ||
13246 element == EL_SP_EXIT_OPENING)
13248 sound_action = ACTION_PASSING; /* player is passing exit */
13250 else if (element == EL_EMPTY)
13252 sound_action = ACTION_MOVING; /* nothing to walk on */
13255 /* play sound from background or player, whatever is available */
13256 if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
13257 PlayLevelSoundElementAction(x, y, sound_element, sound_action);
13259 PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
13261 else if (player_can_move &&
13262 IS_PASSABLE(element) && canPassField(x, y, move_direction))
13264 if (!ACCESS_FROM(element, opposite_direction))
13265 return MP_NO_ACTION; /* field not accessible from this direction */
13267 if (CAN_MOVE(element)) /* only fixed elements can be passed! */
13268 return MP_NO_ACTION;
13270 if (IS_EM_GATE(element))
13272 if (!player->key[EM_GATE_NR(element)])
13273 return MP_NO_ACTION;
13275 else if (IS_EM_GATE_GRAY(element))
13277 if (!player->key[EM_GATE_GRAY_NR(element)])
13278 return MP_NO_ACTION;
13280 else if (IS_EM_GATE_GRAY_ACTIVE(element))
13282 if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
13283 return MP_NO_ACTION;
13285 else if (IS_EMC_GATE(element))
13287 if (!player->key[EMC_GATE_NR(element)])
13288 return MP_NO_ACTION;
13290 else if (IS_EMC_GATE_GRAY(element))
13292 if (!player->key[EMC_GATE_GRAY_NR(element)])
13293 return MP_NO_ACTION;
13295 else if (IS_EMC_GATE_GRAY_ACTIVE(element))
13297 if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
13298 return MP_NO_ACTION;
13300 else if (element == EL_DC_GATE_WHITE ||
13301 element == EL_DC_GATE_WHITE_GRAY ||
13302 element == EL_DC_GATE_WHITE_GRAY_ACTIVE)
13304 if (player->num_white_keys == 0)
13305 return MP_NO_ACTION;
13307 player->num_white_keys--;
13309 else if (IS_SP_PORT(element))
13311 if (element == EL_SP_GRAVITY_PORT_LEFT ||
13312 element == EL_SP_GRAVITY_PORT_RIGHT ||
13313 element == EL_SP_GRAVITY_PORT_UP ||
13314 element == EL_SP_GRAVITY_PORT_DOWN)
13315 player->gravity = !player->gravity;
13316 else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
13317 element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
13318 element == EL_SP_GRAVITY_ON_PORT_UP ||
13319 element == EL_SP_GRAVITY_ON_PORT_DOWN)
13320 player->gravity = TRUE;
13321 else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
13322 element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
13323 element == EL_SP_GRAVITY_OFF_PORT_UP ||
13324 element == EL_SP_GRAVITY_OFF_PORT_DOWN)
13325 player->gravity = FALSE;
13328 /* automatically move to the next field with double speed */
13329 player->programmed_action = move_direction;
13331 if (player->move_delay_reset_counter == 0)
13333 player->move_delay_reset_counter = 2; /* two double speed steps */
13335 DOUBLE_PLAYER_SPEED(player);
13338 PlayLevelSoundAction(x, y, ACTION_PASSING);
13340 else if (player_can_move_or_snap && IS_DIGGABLE(element))
13344 if (mode != DF_SNAP)
13346 GfxElement[x][y] = GFX_ELEMENT(element);
13347 player->is_digging = TRUE;
13350 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
13352 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
13353 player->index_bit, dig_side);
13355 if (mode == DF_SNAP)
13357 if (level.block_snap_field)
13358 setFieldForSnapping(x, y, element, move_direction);
13360 TestIfElementTouchesCustomElement(x, y); /* for empty space */
13362 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13363 player->index_bit, dig_side);
13366 else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
13370 if (is_player && mode != DF_SNAP)
13372 GfxElement[x][y] = element;
13373 player->is_collecting = TRUE;
13376 if (element == EL_SPEED_PILL)
13378 player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
13380 else if (element == EL_EXTRA_TIME && level.time > 0)
13382 TimeLeft += level.extra_time;
13384 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
13386 DisplayGameControlValues();
13388 else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
13390 player->shield_normal_time_left += level.shield_normal_time;
13391 if (element == EL_SHIELD_DEADLY)
13392 player->shield_deadly_time_left += level.shield_deadly_time;
13394 else if (element == EL_DYNAMITE ||
13395 element == EL_EM_DYNAMITE ||
13396 element == EL_SP_DISK_RED)
13398 if (player->inventory_size < MAX_INVENTORY_SIZE)
13399 player->inventory_element[player->inventory_size++] = element;
13401 DrawGameDoorValues();
13403 else if (element == EL_DYNABOMB_INCREASE_NUMBER)
13405 player->dynabomb_count++;
13406 player->dynabombs_left++;
13408 else if (element == EL_DYNABOMB_INCREASE_SIZE)
13410 player->dynabomb_size++;
13412 else if (element == EL_DYNABOMB_INCREASE_POWER)
13414 player->dynabomb_xl = TRUE;
13416 else if (IS_KEY(element))
13418 player->key[KEY_NR(element)] = TRUE;
13420 DrawGameDoorValues();
13422 else if (element == EL_DC_KEY_WHITE)
13424 player->num_white_keys++;
13426 /* display white keys? */
13427 /* DrawGameDoorValues(); */
13429 else if (IS_ENVELOPE(element))
13431 player->show_envelope = element;
13433 else if (element == EL_EMC_LENSES)
13435 game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
13437 RedrawAllInvisibleElementsForLenses();
13439 else if (element == EL_EMC_MAGNIFIER)
13441 game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
13443 RedrawAllInvisibleElementsForMagnifier();
13445 else if (IS_DROPPABLE(element) ||
13446 IS_THROWABLE(element)) /* can be collected and dropped */
13450 if (collect_count == 0)
13451 player->inventory_infinite_element = element;
13453 for (i = 0; i < collect_count; i++)
13454 if (player->inventory_size < MAX_INVENTORY_SIZE)
13455 player->inventory_element[player->inventory_size++] = element;
13457 DrawGameDoorValues();
13459 else if (collect_count > 0)
13461 local_player->gems_still_needed -= collect_count;
13462 if (local_player->gems_still_needed < 0)
13463 local_player->gems_still_needed = 0;
13465 game_panel_controls[GAME_PANEL_GEMS].value = local_player->gems_still_needed;
13467 DisplayGameControlValues();
13470 RaiseScoreElement(element);
13471 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
13474 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
13475 player->index_bit, dig_side);
13477 if (mode == DF_SNAP)
13479 if (level.block_snap_field)
13480 setFieldForSnapping(x, y, element, move_direction);
13482 TestIfElementTouchesCustomElement(x, y); /* for empty space */
13484 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13485 player->index_bit, dig_side);
13488 else if (player_can_move_or_snap && IS_PUSHABLE(element))
13490 if (mode == DF_SNAP && element != EL_BD_ROCK)
13491 return MP_NO_ACTION;
13493 if (CAN_FALL(element) && dy)
13494 return MP_NO_ACTION;
13496 if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
13497 !(element == EL_SPRING && level.use_spring_bug))
13498 return MP_NO_ACTION;
13500 if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
13501 ((move_direction & MV_VERTICAL &&
13502 ((element_info[element].move_pattern & MV_LEFT &&
13503 IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
13504 (element_info[element].move_pattern & MV_RIGHT &&
13505 IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
13506 (move_direction & MV_HORIZONTAL &&
13507 ((element_info[element].move_pattern & MV_UP &&
13508 IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
13509 (element_info[element].move_pattern & MV_DOWN &&
13510 IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
13511 return MP_NO_ACTION;
13513 /* do not push elements already moving away faster than player */
13514 if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
13515 ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
13516 return MP_NO_ACTION;
13518 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
13520 if (player->push_delay_value == -1 || !player_was_pushing)
13521 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13523 else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
13525 if (player->push_delay_value == -1)
13526 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13528 else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
13530 if (!player->is_pushing)
13531 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13534 player->is_pushing = TRUE;
13535 player->is_active = TRUE;
13537 if (!(IN_LEV_FIELD(nextx, nexty) &&
13538 (IS_FREE(nextx, nexty) ||
13539 (IS_SB_ELEMENT(element) &&
13540 Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY) ||
13541 (IS_CUSTOM_ELEMENT(element) &&
13542 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty)))))
13543 return MP_NO_ACTION;
13545 if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
13546 return MP_NO_ACTION;
13548 if (player->push_delay == -1) /* new pushing; restart delay */
13549 player->push_delay = 0;
13551 if (player->push_delay < player->push_delay_value &&
13552 !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
13553 element != EL_SPRING && element != EL_BALLOON)
13555 /* make sure that there is no move delay before next try to push */
13556 if (game.engine_version >= VERSION_IDENT(3,0,7,1))
13557 player->move_delay = 0;
13559 return MP_NO_ACTION;
13562 if (IS_CUSTOM_ELEMENT(element) &&
13563 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty))
13565 if (!DigFieldByCE(nextx, nexty, element))
13566 return MP_NO_ACTION;
13569 if (IS_SB_ELEMENT(element))
13571 if (element == EL_SOKOBAN_FIELD_FULL)
13573 Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
13574 local_player->sokobanfields_still_needed++;
13577 if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
13579 Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
13580 local_player->sokobanfields_still_needed--;
13583 Feld[x][y] = EL_SOKOBAN_OBJECT;
13585 if (Back[x][y] == Back[nextx][nexty])
13586 PlayLevelSoundAction(x, y, ACTION_PUSHING);
13587 else if (Back[x][y] != 0)
13588 PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
13591 PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
13594 if (local_player->sokobanfields_still_needed == 0 &&
13595 (game.emulation == EMU_SOKOBAN || level.auto_exit_sokoban))
13597 PlayerWins(player);
13599 PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
13603 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
13605 InitMovingField(x, y, move_direction);
13606 GfxAction[x][y] = ACTION_PUSHING;
13608 if (mode == DF_SNAP)
13609 ContinueMoving(x, y);
13611 MovPos[x][y] = (dx != 0 ? dx : dy);
13613 Pushed[x][y] = TRUE;
13614 Pushed[nextx][nexty] = TRUE;
13616 if (game.engine_version < VERSION_IDENT(2,2,0,7))
13617 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13619 player->push_delay_value = -1; /* get new value later */
13621 /* check for element change _after_ element has been pushed */
13622 if (game.use_change_when_pushing_bug)
13624 CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
13625 player->index_bit, dig_side);
13626 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
13627 player->index_bit, dig_side);
13630 else if (IS_SWITCHABLE(element))
13632 if (PLAYER_SWITCHING(player, x, y))
13634 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
13635 player->index_bit, dig_side);
13640 player->is_switching = TRUE;
13641 player->switch_x = x;
13642 player->switch_y = y;
13644 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
13646 if (element == EL_ROBOT_WHEEL)
13648 Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
13652 game.robot_wheel_active = TRUE;
13654 TEST_DrawLevelField(x, y);
13656 else if (element == EL_SP_TERMINAL)
13660 SCAN_PLAYFIELD(xx, yy)
13662 if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
13664 else if (Feld[xx][yy] == EL_SP_TERMINAL)
13665 Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
13668 else if (IS_BELT_SWITCH(element))
13670 ToggleBeltSwitch(x, y);
13672 else if (element == EL_SWITCHGATE_SWITCH_UP ||
13673 element == EL_SWITCHGATE_SWITCH_DOWN ||
13674 element == EL_DC_SWITCHGATE_SWITCH_UP ||
13675 element == EL_DC_SWITCHGATE_SWITCH_DOWN)
13677 ToggleSwitchgateSwitch(x, y);
13679 else if (element == EL_LIGHT_SWITCH ||
13680 element == EL_LIGHT_SWITCH_ACTIVE)
13682 ToggleLightSwitch(x, y);
13684 else if (element == EL_TIMEGATE_SWITCH ||
13685 element == EL_DC_TIMEGATE_SWITCH)
13687 ActivateTimegateSwitch(x, y);
13689 else if (element == EL_BALLOON_SWITCH_LEFT ||
13690 element == EL_BALLOON_SWITCH_RIGHT ||
13691 element == EL_BALLOON_SWITCH_UP ||
13692 element == EL_BALLOON_SWITCH_DOWN ||
13693 element == EL_BALLOON_SWITCH_NONE ||
13694 element == EL_BALLOON_SWITCH_ANY)
13696 game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT ? MV_LEFT :
13697 element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
13698 element == EL_BALLOON_SWITCH_UP ? MV_UP :
13699 element == EL_BALLOON_SWITCH_DOWN ? MV_DOWN :
13700 element == EL_BALLOON_SWITCH_NONE ? MV_NONE :
13703 else if (element == EL_LAMP)
13705 Feld[x][y] = EL_LAMP_ACTIVE;
13706 local_player->lights_still_needed--;
13708 ResetGfxAnimation(x, y);
13709 TEST_DrawLevelField(x, y);
13711 else if (element == EL_TIME_ORB_FULL)
13713 Feld[x][y] = EL_TIME_ORB_EMPTY;
13715 if (level.time > 0 || level.use_time_orb_bug)
13717 TimeLeft += level.time_orb_time;
13718 game.no_time_limit = FALSE;
13720 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
13722 DisplayGameControlValues();
13725 ResetGfxAnimation(x, y);
13726 TEST_DrawLevelField(x, y);
13728 else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
13729 element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
13733 game.ball_state = !game.ball_state;
13735 SCAN_PLAYFIELD(xx, yy)
13737 int e = Feld[xx][yy];
13739 if (game.ball_state)
13741 if (e == EL_EMC_MAGIC_BALL)
13742 CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
13743 else if (e == EL_EMC_MAGIC_BALL_SWITCH)
13744 CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
13748 if (e == EL_EMC_MAGIC_BALL_ACTIVE)
13749 CreateField(xx, yy, EL_EMC_MAGIC_BALL);
13750 else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
13751 CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
13756 CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
13757 player->index_bit, dig_side);
13759 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
13760 player->index_bit, dig_side);
13762 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
13763 player->index_bit, dig_side);
13769 if (!PLAYER_SWITCHING(player, x, y))
13771 player->is_switching = TRUE;
13772 player->switch_x = x;
13773 player->switch_y = y;
13775 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
13776 player->index_bit, dig_side);
13777 CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
13778 player->index_bit, dig_side);
13780 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
13781 player->index_bit, dig_side);
13782 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
13783 player->index_bit, dig_side);
13786 CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
13787 player->index_bit, dig_side);
13788 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
13789 player->index_bit, dig_side);
13791 return MP_NO_ACTION;
13794 player->push_delay = -1;
13796 if (is_player) /* function can also be called by EL_PENGUIN */
13798 if (Feld[x][y] != element) /* really digged/collected something */
13800 player->is_collecting = !player->is_digging;
13801 player->is_active = TRUE;
13808 static boolean DigFieldByCE(int x, int y, int digging_element)
13810 int element = Feld[x][y];
13812 if (!IS_FREE(x, y))
13814 int action = (IS_DIGGABLE(element) ? ACTION_DIGGING :
13815 IS_COLLECTIBLE(element) ? ACTION_COLLECTING :
13818 /* no element can dig solid indestructible elements */
13819 if (IS_INDESTRUCTIBLE(element) &&
13820 !IS_DIGGABLE(element) &&
13821 !IS_COLLECTIBLE(element))
13824 if (AmoebaNr[x][y] &&
13825 (element == EL_AMOEBA_FULL ||
13826 element == EL_BD_AMOEBA ||
13827 element == EL_AMOEBA_GROWING))
13829 AmoebaCnt[AmoebaNr[x][y]]--;
13830 AmoebaCnt2[AmoebaNr[x][y]]--;
13833 if (IS_MOVING(x, y))
13834 RemoveMovingField(x, y);
13838 TEST_DrawLevelField(x, y);
13841 /* if digged element was about to explode, prevent the explosion */
13842 ExplodeField[x][y] = EX_TYPE_NONE;
13844 PlayLevelSoundAction(x, y, action);
13847 Store[x][y] = EL_EMPTY;
13849 /* this makes it possible to leave the removed element again */
13850 if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element)))
13851 Store[x][y] = element;
13856 static boolean SnapField(struct PlayerInfo *player, int dx, int dy)
13858 int jx = player->jx, jy = player->jy;
13859 int x = jx + dx, y = jy + dy;
13860 int snap_direction = (dx == -1 ? MV_LEFT :
13861 dx == +1 ? MV_RIGHT :
13863 dy == +1 ? MV_DOWN : MV_NONE);
13864 boolean can_continue_snapping = (level.continuous_snapping &&
13865 WasJustFalling[x][y] < CHECK_DELAY_FALLING);
13867 if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
13870 if (!player->active || !IN_LEV_FIELD(x, y))
13878 if (player->MovPos == 0)
13879 player->is_pushing = FALSE;
13881 player->is_snapping = FALSE;
13883 if (player->MovPos == 0)
13885 player->is_moving = FALSE;
13886 player->is_digging = FALSE;
13887 player->is_collecting = FALSE;
13893 /* prevent snapping with already pressed snap key when not allowed */
13894 if (player->is_snapping && !can_continue_snapping)
13897 player->MovDir = snap_direction;
13899 if (player->MovPos == 0)
13901 player->is_moving = FALSE;
13902 player->is_digging = FALSE;
13903 player->is_collecting = FALSE;
13906 player->is_dropping = FALSE;
13907 player->is_dropping_pressed = FALSE;
13908 player->drop_pressed_delay = 0;
13910 if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
13913 player->is_snapping = TRUE;
13914 player->is_active = TRUE;
13916 if (player->MovPos == 0)
13918 player->is_moving = FALSE;
13919 player->is_digging = FALSE;
13920 player->is_collecting = FALSE;
13923 if (player->MovPos != 0) /* prevent graphic bugs in versions < 2.2.0 */
13924 TEST_DrawLevelField(player->last_jx, player->last_jy);
13926 TEST_DrawLevelField(x, y);
13931 static boolean DropElement(struct PlayerInfo *player)
13933 int old_element, new_element;
13934 int dropx = player->jx, dropy = player->jy;
13935 int drop_direction = player->MovDir;
13936 int drop_side = drop_direction;
13937 int drop_element = get_next_dropped_element(player);
13939 player->is_dropping_pressed = TRUE;
13941 /* do not drop an element on top of another element; when holding drop key
13942 pressed without moving, dropped element must move away before the next
13943 element can be dropped (this is especially important if the next element
13944 is dynamite, which can be placed on background for historical reasons) */
13945 if (PLAYER_DROPPING(player, dropx, dropy) && Feld[dropx][dropy] != EL_EMPTY)
13948 if (IS_THROWABLE(drop_element))
13950 dropx += GET_DX_FROM_DIR(drop_direction);
13951 dropy += GET_DY_FROM_DIR(drop_direction);
13953 if (!IN_LEV_FIELD(dropx, dropy))
13957 old_element = Feld[dropx][dropy]; /* old element at dropping position */
13958 new_element = drop_element; /* default: no change when dropping */
13960 /* check if player is active, not moving and ready to drop */
13961 if (!player->active || player->MovPos || player->drop_delay > 0)
13964 /* check if player has anything that can be dropped */
13965 if (new_element == EL_UNDEFINED)
13968 /* check if drop key was pressed long enough for EM style dynamite */
13969 if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
13972 /* check if anything can be dropped at the current position */
13973 if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
13976 /* collected custom elements can only be dropped on empty fields */
13977 if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
13980 if (old_element != EL_EMPTY)
13981 Back[dropx][dropy] = old_element; /* store old element on this field */
13983 ResetGfxAnimation(dropx, dropy);
13984 ResetRandomAnimationValue(dropx, dropy);
13986 if (player->inventory_size > 0 ||
13987 player->inventory_infinite_element != EL_UNDEFINED)
13989 if (player->inventory_size > 0)
13991 player->inventory_size--;
13993 DrawGameDoorValues();
13995 if (new_element == EL_DYNAMITE)
13996 new_element = EL_DYNAMITE_ACTIVE;
13997 else if (new_element == EL_EM_DYNAMITE)
13998 new_element = EL_EM_DYNAMITE_ACTIVE;
13999 else if (new_element == EL_SP_DISK_RED)
14000 new_element = EL_SP_DISK_RED_ACTIVE;
14003 Feld[dropx][dropy] = new_element;
14005 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
14006 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
14007 el2img(Feld[dropx][dropy]), 0);
14009 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
14011 /* needed if previous element just changed to "empty" in the last frame */
14012 ChangeCount[dropx][dropy] = 0; /* allow at least one more change */
14014 CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
14015 player->index_bit, drop_side);
14016 CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
14018 player->index_bit, drop_side);
14020 TestIfElementTouchesCustomElement(dropx, dropy);
14022 else /* player is dropping a dyna bomb */
14024 player->dynabombs_left--;
14026 Feld[dropx][dropy] = new_element;
14028 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
14029 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
14030 el2img(Feld[dropx][dropy]), 0);
14032 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
14035 if (Feld[dropx][dropy] == new_element) /* uninitialized unless CE change */
14036 InitField_WithBug1(dropx, dropy, FALSE);
14038 new_element = Feld[dropx][dropy]; /* element might have changed */
14040 if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
14041 element_info[new_element].move_pattern == MV_WHEN_DROPPED)
14043 if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
14044 MovDir[dropx][dropy] = drop_direction;
14046 ChangeCount[dropx][dropy] = 0; /* allow at least one more change */
14048 /* do not cause impact style collision by dropping elements that can fall */
14049 CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
14052 player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
14053 player->is_dropping = TRUE;
14055 player->drop_pressed_delay = 0;
14056 player->is_dropping_pressed = FALSE;
14058 player->drop_x = dropx;
14059 player->drop_y = dropy;
14064 /* ------------------------------------------------------------------------- */
14065 /* game sound playing functions */
14066 /* ------------------------------------------------------------------------- */
14068 static int *loop_sound_frame = NULL;
14069 static int *loop_sound_volume = NULL;
14071 void InitPlayLevelSound()
14073 int num_sounds = getSoundListSize();
14075 checked_free(loop_sound_frame);
14076 checked_free(loop_sound_volume);
14078 loop_sound_frame = checked_calloc(num_sounds * sizeof(int));
14079 loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
14082 static void PlayLevelSound(int x, int y, int nr)
14084 int sx = SCREENX(x), sy = SCREENY(y);
14085 int volume, stereo_position;
14086 int max_distance = 8;
14087 int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
14089 if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
14090 (!setup.sound_loops && IS_LOOP_SOUND(nr)))
14093 if (!IN_LEV_FIELD(x, y) ||
14094 sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
14095 sy < -max_distance || sy >= SCR_FIELDY + max_distance)
14098 volume = SOUND_MAX_VOLUME;
14100 if (!IN_SCR_FIELD(sx, sy))
14102 int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
14103 int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
14105 volume -= volume * (dx > dy ? dx : dy) / max_distance;
14108 stereo_position = (SOUND_MAX_LEFT +
14109 (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
14110 (SCR_FIELDX + 2 * max_distance));
14112 if (IS_LOOP_SOUND(nr))
14114 /* This assures that quieter loop sounds do not overwrite louder ones,
14115 while restarting sound volume comparison with each new game frame. */
14117 if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
14120 loop_sound_volume[nr] = volume;
14121 loop_sound_frame[nr] = FrameCounter;
14124 PlaySoundExt(nr, volume, stereo_position, type);
14127 static void PlayLevelSoundNearest(int x, int y, int sound_action)
14129 PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
14130 x > LEVELX(BX2) ? LEVELX(BX2) : x,
14131 y < LEVELY(BY1) ? LEVELY(BY1) :
14132 y > LEVELY(BY2) ? LEVELY(BY2) : y,
14136 static void PlayLevelSoundAction(int x, int y, int action)
14138 PlayLevelSoundElementAction(x, y, Feld[x][y], action);
14141 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
14143 int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
14145 if (sound_effect != SND_UNDEFINED)
14146 PlayLevelSound(x, y, sound_effect);
14149 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
14152 int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
14154 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14155 PlayLevelSound(x, y, sound_effect);
14158 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
14160 int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
14162 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14163 PlayLevelSound(x, y, sound_effect);
14166 static void StopLevelSoundActionIfLoop(int x, int y, int action)
14168 int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
14170 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14171 StopSound(sound_effect);
14174 static void PlayLevelMusic()
14176 if (levelset.music[level_nr] != MUS_UNDEFINED)
14177 PlayMusic(levelset.music[level_nr]); /* from config file */
14179 PlayMusic(MAP_NOCONF_MUSIC(level_nr)); /* from music dir */
14182 void PlayLevelSound_EM(int xx, int yy, int element_em, int sample)
14184 int element = (element_em > -1 ? map_element_EM_to_RND(element_em) : 0);
14185 int offset = (BorderElement == EL_STEELWALL ? 1 : 0);
14186 int x = xx - 1 - offset;
14187 int y = yy - 1 - offset;
14192 PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
14196 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14200 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14204 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14208 PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
14212 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14216 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14219 case SAMPLE_android_clone:
14220 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14223 case SAMPLE_android_move:
14224 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14227 case SAMPLE_spring:
14228 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14232 PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
14236 PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
14239 case SAMPLE_eater_eat:
14240 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14244 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14247 case SAMPLE_collect:
14248 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
14251 case SAMPLE_diamond:
14252 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14255 case SAMPLE_squash:
14256 /* !!! CHECK THIS !!! */
14258 PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
14260 PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
14264 case SAMPLE_wonderfall:
14265 PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
14269 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14273 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14277 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14281 PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
14285 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14289 PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
14292 case SAMPLE_wonder:
14293 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14297 PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
14300 case SAMPLE_exit_open:
14301 PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
14304 case SAMPLE_exit_leave:
14305 PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
14308 case SAMPLE_dynamite:
14309 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14313 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14317 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
14321 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14325 PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
14329 PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
14333 PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
14337 PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
14342 void PlayLevelSound_SP(int xx, int yy, int element_sp, int action_sp)
14344 int element = map_element_SP_to_RND(element_sp);
14345 int action = map_action_SP_to_RND(action_sp);
14346 int offset = (setup.sp_show_border_elements ? 0 : 1);
14347 int x = xx - offset;
14348 int y = yy - offset;
14350 PlayLevelSoundElementAction(x, y, element, action);
14353 void RaiseScore(int value)
14355 local_player->score += value;
14357 game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
14359 DisplayGameControlValues();
14362 void RaiseScoreElement(int element)
14367 case EL_BD_DIAMOND:
14368 case EL_EMERALD_YELLOW:
14369 case EL_EMERALD_RED:
14370 case EL_EMERALD_PURPLE:
14371 case EL_SP_INFOTRON:
14372 RaiseScore(level.score[SC_EMERALD]);
14375 RaiseScore(level.score[SC_DIAMOND]);
14378 RaiseScore(level.score[SC_CRYSTAL]);
14381 RaiseScore(level.score[SC_PEARL]);
14384 case EL_BD_BUTTERFLY:
14385 case EL_SP_ELECTRON:
14386 RaiseScore(level.score[SC_BUG]);
14389 case EL_BD_FIREFLY:
14390 case EL_SP_SNIKSNAK:
14391 RaiseScore(level.score[SC_SPACESHIP]);
14394 case EL_DARK_YAMYAM:
14395 RaiseScore(level.score[SC_YAMYAM]);
14398 RaiseScore(level.score[SC_ROBOT]);
14401 RaiseScore(level.score[SC_PACMAN]);
14404 RaiseScore(level.score[SC_NUT]);
14407 case EL_EM_DYNAMITE:
14408 case EL_SP_DISK_RED:
14409 case EL_DYNABOMB_INCREASE_NUMBER:
14410 case EL_DYNABOMB_INCREASE_SIZE:
14411 case EL_DYNABOMB_INCREASE_POWER:
14412 RaiseScore(level.score[SC_DYNAMITE]);
14414 case EL_SHIELD_NORMAL:
14415 case EL_SHIELD_DEADLY:
14416 RaiseScore(level.score[SC_SHIELD]);
14418 case EL_EXTRA_TIME:
14419 RaiseScore(level.extra_time_score);
14433 case EL_DC_KEY_WHITE:
14434 RaiseScore(level.score[SC_KEY]);
14437 RaiseScore(element_info[element].collect_score);
14442 void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message)
14444 if (skip_request || Request(message, REQ_ASK | REQ_STAY_CLOSED))
14446 /* closing door required in case of envelope style request dialogs */
14448 CloseDoor(DOOR_CLOSE_1);
14450 #if defined(NETWORK_AVALIABLE)
14451 if (options.network)
14452 SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
14458 FadeSkipNextFadeIn();
14460 game_status = GAME_MODE_MAIN;
14462 DrawAndFadeInMainMenu(REDRAW_FIELD);
14466 game_status = GAME_MODE_MAIN;
14468 DrawAndFadeInMainMenu(REDRAW_FIELD);
14472 else /* continue playing the game */
14474 if (tape.playing && tape.deactivate_display)
14475 TapeDeactivateDisplayOff(TRUE);
14477 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
14479 if (tape.playing && tape.deactivate_display)
14480 TapeDeactivateDisplayOn();
14484 void RequestQuitGame(boolean ask_if_really_quit)
14486 boolean quick_quit = (!ask_if_really_quit || level_editor_test_game);
14487 boolean skip_request = AllPlayersGone || quick_quit;
14489 RequestQuitGameExt(skip_request, quick_quit,
14490 "Do you really want to quit the game?");
14494 /* ------------------------------------------------------------------------- */
14495 /* random generator functions */
14496 /* ------------------------------------------------------------------------- */
14498 unsigned int InitEngineRandom_RND(int seed)
14500 game.num_random_calls = 0;
14502 return InitEngineRandom(seed);
14505 unsigned int RND(int max)
14509 game.num_random_calls++;
14511 return GetEngineRandom(max);
14518 /* ------------------------------------------------------------------------- */
14519 /* game engine snapshot handling functions */
14520 /* ------------------------------------------------------------------------- */
14522 struct EngineSnapshotInfo
14524 /* runtime values for custom element collect score */
14525 int collect_score[NUM_CUSTOM_ELEMENTS];
14527 /* runtime values for group element choice position */
14528 int choice_pos[NUM_GROUP_ELEMENTS];
14530 /* runtime values for belt position animations */
14531 int belt_graphic[4][NUM_BELT_PARTS];
14532 int belt_anim_mode[4][NUM_BELT_PARTS];
14535 static struct EngineSnapshotInfo engine_snapshot_rnd;
14536 static char *snapshot_level_identifier = NULL;
14537 static int snapshot_level_nr = -1;
14539 static void SaveEngineSnapshotValues_RND()
14541 static int belt_base_active_element[4] =
14543 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
14544 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
14545 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
14546 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
14550 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
14552 int element = EL_CUSTOM_START + i;
14554 engine_snapshot_rnd.collect_score[i] = element_info[element].collect_score;
14557 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
14559 int element = EL_GROUP_START + i;
14561 engine_snapshot_rnd.choice_pos[i] = element_info[element].group->choice_pos;
14564 for (i = 0; i < 4; i++)
14566 for (j = 0; j < NUM_BELT_PARTS; j++)
14568 int element = belt_base_active_element[i] + j;
14569 int graphic = el2img(element);
14570 int anim_mode = graphic_info[graphic].anim_mode;
14572 engine_snapshot_rnd.belt_graphic[i][j] = graphic;
14573 engine_snapshot_rnd.belt_anim_mode[i][j] = anim_mode;
14578 static void LoadEngineSnapshotValues_RND()
14580 unsigned int num_random_calls = game.num_random_calls;
14583 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
14585 int element = EL_CUSTOM_START + i;
14587 element_info[element].collect_score = engine_snapshot_rnd.collect_score[i];
14590 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
14592 int element = EL_GROUP_START + i;
14594 element_info[element].group->choice_pos = engine_snapshot_rnd.choice_pos[i];
14597 for (i = 0; i < 4; i++)
14599 for (j = 0; j < NUM_BELT_PARTS; j++)
14601 int graphic = engine_snapshot_rnd.belt_graphic[i][j];
14602 int anim_mode = engine_snapshot_rnd.belt_anim_mode[i][j];
14604 graphic_info[graphic].anim_mode = anim_mode;
14608 if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
14610 InitRND(tape.random_seed);
14611 for (i = 0; i < num_random_calls; i++)
14615 if (game.num_random_calls != num_random_calls)
14617 Error(ERR_INFO, "number of random calls out of sync");
14618 Error(ERR_INFO, "number of random calls should be %d", num_random_calls);
14619 Error(ERR_INFO, "number of random calls is %d", game.num_random_calls);
14620 Error(ERR_EXIT, "this should not happen -- please debug");
14624 void FreeEngineSnapshotSingle()
14626 FreeSnapshotSingle();
14628 setString(&snapshot_level_identifier, NULL);
14629 snapshot_level_nr = -1;
14632 void FreeEngineSnapshotList()
14634 FreeSnapshotList();
14637 ListNode *SaveEngineSnapshotBuffers()
14639 ListNode *buffers = NULL;
14641 /* copy some special values to a structure better suited for the snapshot */
14643 if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
14644 SaveEngineSnapshotValues_RND();
14645 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
14646 SaveEngineSnapshotValues_EM();
14647 if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
14648 SaveEngineSnapshotValues_SP(&buffers);
14650 /* save values stored in special snapshot structure */
14652 if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
14653 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
14654 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
14655 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
14656 if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
14657 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_sp));
14659 /* save further RND engine values */
14661 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(stored_player));
14662 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(game));
14663 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(tape));
14665 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ZX));
14666 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ZY));
14667 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExitX));
14668 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExitY));
14670 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(FrameCounter));
14671 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeFrames));
14672 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimePlayed));
14673 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeLeft));
14674 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TapeTime));
14676 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovDir));
14677 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovPos));
14678 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenGfxPos));
14680 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScrollStepSize));
14682 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AllPlayersGone));
14684 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt));
14685 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2));
14687 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Feld));
14688 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovPos));
14689 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDir));
14690 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDelay));
14691 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeDelay));
14692 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangePage));
14693 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CustomValue));
14694 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store));
14695 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store2));
14696 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(StorePlayer));
14697 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Back));
14698 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaNr));
14699 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustMoving));
14700 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustFalling));
14701 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckCollision));
14702 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckImpact));
14703 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Stop));
14704 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Pushed));
14706 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeCount));
14707 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeEvent));
14709 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodePhase));
14710 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeDelay));
14711 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeField));
14713 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(RunnerVisit));
14714 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(PlayerVisit));
14716 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxFrame));
14717 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxRandom));
14718 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxElement));
14719 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxAction));
14720 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxDir));
14722 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_x));
14723 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_y));
14725 /* save level identification information */
14727 setString(&snapshot_level_identifier, leveldir_current->identifier);
14728 snapshot_level_nr = level_nr;
14731 ListNode *node = engine_snapshot_list_rnd;
14734 while (node != NULL)
14736 num_bytes += ((struct EngineSnapshotNodeInfo *)node->content)->size;
14741 printf("::: size of engine snapshot: %d bytes\n", num_bytes);
14747 void SaveEngineSnapshotSingle()
14749 ListNode *buffers = SaveEngineSnapshotBuffers();
14751 /* finally save all snapshot buffers to single snapshot */
14752 SaveSnapshotSingle(buffers);
14755 boolean SaveEngineSnapshotToList()
14757 boolean save_snapshot =
14758 (FrameCounter == 0 ||
14759 (game.snapshot.mode == SNAPSHOT_MODE_EVERY_STEP) ||
14760 (game.snapshot.mode == SNAPSHOT_MODE_EVERY_MOVE &&
14761 game.snapshot.changed_action));
14763 game.snapshot.changed_action = FALSE;
14765 if (game.snapshot.mode == SNAPSHOT_MODE_OFF || !save_snapshot)
14768 ListNode *buffers = SaveEngineSnapshotBuffers();
14770 /* finally save all snapshot buffers to snapshot list */
14771 SaveSnapshotToList(buffers);
14776 void LoadEngineSnapshotValues()
14778 /* restore special values from snapshot structure */
14780 if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
14781 LoadEngineSnapshotValues_RND();
14782 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
14783 LoadEngineSnapshotValues_EM();
14784 if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
14785 LoadEngineSnapshotValues_SP();
14788 void LoadEngineSnapshotSingle()
14790 LoadSnapshotSingle();
14792 LoadEngineSnapshotValues();
14795 void LoadEngineSnapshot_Undo(int steps)
14797 LoadSnapshotFromList_Older(steps);
14799 LoadEngineSnapshotValues();
14802 void LoadEngineSnapshot_Redo(int steps)
14804 LoadSnapshotFromList_Newer(steps);
14806 LoadEngineSnapshotValues();
14809 boolean CheckEngineSnapshotSingle()
14811 return (strEqual(snapshot_level_identifier, leveldir_current->identifier) &&
14812 snapshot_level_nr == level_nr);
14815 boolean CheckEngineSnapshotList()
14817 return CheckSnapshotList();
14821 /* ---------- new game button stuff ---------------------------------------- */
14829 } gamebutton_info[NUM_GAME_BUTTONS] =
14832 IMG_GAME_BUTTON_GFX_STOP, &game.button.stop,
14833 GAME_CTRL_ID_STOP, "stop game"
14836 IMG_GAME_BUTTON_GFX_PAUSE, &game.button.pause,
14837 GAME_CTRL_ID_PAUSE, "pause game"
14840 IMG_GAME_BUTTON_GFX_PLAY, &game.button.play,
14841 GAME_CTRL_ID_PLAY, "play game"
14844 IMG_GAME_BUTTON_GFX_UNDO, &game.button.undo,
14845 GAME_CTRL_ID_UNDO, "undo step"
14848 IMG_GAME_BUTTON_GFX_REDO, &game.button.redo,
14849 GAME_CTRL_ID_REDO, "redo step"
14852 IMG_GAME_BUTTON_GFX_SAVE, &game.button.save,
14853 GAME_CTRL_ID_SAVE, "save game"
14856 IMG_GAME_BUTTON_GFX_LOAD, &game.button.load,
14857 GAME_CTRL_ID_LOAD, "load game"
14860 IMG_GAME_BUTTON_GFX_SOUND_MUSIC, &game.button.sound_music,
14861 SOUND_CTRL_ID_MUSIC, "background music on/off"
14864 IMG_GAME_BUTTON_GFX_SOUND_LOOPS, &game.button.sound_loops,
14865 SOUND_CTRL_ID_LOOPS, "sound loops on/off"
14868 IMG_GAME_BUTTON_GFX_SOUND_SIMPLE, &game.button.sound_simple,
14869 SOUND_CTRL_ID_SIMPLE, "normal sounds on/off"
14873 void CreateGameButtons()
14877 for (i = 0; i < NUM_GAME_BUTTONS; i++)
14879 struct GraphicInfo *gfx = &graphic_info[gamebutton_info[i].graphic];
14880 struct XY *pos = gamebutton_info[i].pos;
14881 struct GadgetInfo *gi;
14884 unsigned int event_mask;
14885 int base_x = (tape.show_game_buttons ? VX : DX);
14886 int base_y = (tape.show_game_buttons ? VY : DY);
14887 int gd_x = gfx->src_x;
14888 int gd_y = gfx->src_y;
14889 int gd_xp = gfx->src_x + gfx->pressed_xoffset;
14890 int gd_yp = gfx->src_y + gfx->pressed_yoffset;
14891 int gd_xa = gfx->src_x + gfx->active_xoffset;
14892 int gd_ya = gfx->src_y + gfx->active_yoffset;
14893 int gd_xap = gfx->src_x + gfx->active_xoffset + gfx->pressed_xoffset;
14894 int gd_yap = gfx->src_y + gfx->active_yoffset + gfx->pressed_yoffset;
14897 if (gfx->bitmap == NULL)
14899 game_gadget[id] = NULL;
14904 if (id == GAME_CTRL_ID_STOP ||
14905 id == GAME_CTRL_ID_PAUSE ||
14906 id == GAME_CTRL_ID_PLAY ||
14907 id == GAME_CTRL_ID_SAVE ||
14908 id == GAME_CTRL_ID_LOAD)
14910 button_type = GD_TYPE_NORMAL_BUTTON;
14912 event_mask = GD_EVENT_RELEASED;
14914 else if (id == GAME_CTRL_ID_UNDO ||
14915 id == GAME_CTRL_ID_REDO)
14917 button_type = GD_TYPE_NORMAL_BUTTON;
14919 event_mask = GD_EVENT_PRESSED | GD_EVENT_REPEATED;
14923 button_type = GD_TYPE_CHECK_BUTTON;
14925 ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
14926 (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
14927 (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
14928 event_mask = GD_EVENT_PRESSED;
14931 gi = CreateGadget(GDI_CUSTOM_ID, id,
14932 GDI_INFO_TEXT, gamebutton_info[i].infotext,
14933 GDI_X, base_x + GDI_ACTIVE_POS(pos->x),
14934 GDI_Y, base_y + GDI_ACTIVE_POS(pos->y),
14935 GDI_WIDTH, gfx->width,
14936 GDI_HEIGHT, gfx->height,
14937 GDI_TYPE, button_type,
14938 GDI_STATE, GD_BUTTON_UNPRESSED,
14939 GDI_CHECKED, checked,
14940 GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
14941 GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
14942 GDI_ALT_DESIGN_UNPRESSED, gfx->bitmap, gd_xa, gd_ya,
14943 GDI_ALT_DESIGN_PRESSED, gfx->bitmap, gd_xap, gd_yap,
14944 GDI_DIRECT_DRAW, FALSE,
14945 GDI_EVENT_MASK, event_mask,
14946 GDI_CALLBACK_ACTION, HandleGameButtons,
14950 Error(ERR_EXIT, "cannot create gadget");
14952 game_gadget[id] = gi;
14956 void FreeGameButtons()
14960 for (i = 0; i < NUM_GAME_BUTTONS; i++)
14961 FreeGadget(game_gadget[i]);
14964 void MapStopPlayButtons()
14966 UnmapGadget(game_gadget[GAME_CTRL_ID_UNDO]);
14967 UnmapGadget(game_gadget[GAME_CTRL_ID_REDO]);
14969 MapGadget(game_gadget[GAME_CTRL_ID_STOP]);
14970 MapGadget(game_gadget[GAME_CTRL_ID_PLAY]);
14973 void MapUndoRedoButtons()
14975 UnmapGadget(game_gadget[GAME_CTRL_ID_STOP]);
14976 UnmapGadget(game_gadget[GAME_CTRL_ID_PLAY]);
14978 MapGadget(game_gadget[GAME_CTRL_ID_UNDO]);
14979 MapGadget(game_gadget[GAME_CTRL_ID_REDO]);
14982 void MapGameButtons()
14986 for (i = 0; i < NUM_GAME_BUTTONS; i++)
14987 if (i != GAME_CTRL_ID_UNDO &&
14988 i != GAME_CTRL_ID_REDO)
14989 MapGadget(game_gadget[i]);
14992 void UnmapGameButtons()
14996 for (i = 0; i < NUM_GAME_BUTTONS; i++)
14997 UnmapGadget(game_gadget[i]);
15000 void RedrawGameButtons()
15004 for (i = 0; i < NUM_GAME_BUTTONS; i++)
15005 RedrawGadget(game_gadget[i]);
15007 // RedrawGadget() may have set REDRAW_ALL if buttons are defined off-area
15008 redraw_mask &= ~REDRAW_ALL;
15011 void GameUndoRedoExt()
15013 ClearPlayerAction();
15015 tape.pausing = TRUE;
15018 UpdateAndDisplayGameControlValues();
15020 DrawCompleteVideoDisplay();
15021 DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
15022 DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
15023 DrawVideoDisplay((tape.single_step ? VIDEO_STATE_1STEP_ON :
15024 VIDEO_STATE_1STEP_OFF), 0);
15029 void GameUndo(int steps)
15031 if (!CheckEngineSnapshotList())
15034 LoadEngineSnapshot_Undo(steps);
15039 void GameRedo(int steps)
15041 if (!CheckEngineSnapshotList())
15044 LoadEngineSnapshot_Redo(steps);
15049 static void HandleGameButtonsExt(int id, int button)
15051 int steps = BUTTON_STEPSIZE(button);
15052 boolean handle_game_buttons =
15053 (game_status == GAME_MODE_PLAYING ||
15054 (game_status == GAME_MODE_MAIN && tape.show_game_buttons));
15056 if (!handle_game_buttons)
15061 case GAME_CTRL_ID_STOP:
15062 if (game_status == GAME_MODE_MAIN)
15068 RequestQuitGame(TRUE);
15072 case GAME_CTRL_ID_PAUSE:
15073 if (options.network && game_status == GAME_MODE_PLAYING)
15075 #if defined(NETWORK_AVALIABLE)
15077 SendToServer_ContinuePlaying();
15079 SendToServer_PausePlaying();
15083 TapeTogglePause(TAPE_TOGGLE_MANUAL);
15086 case GAME_CTRL_ID_PLAY:
15087 if (game_status == GAME_MODE_MAIN)
15089 StartGameActions(options.network, setup.autorecord, level.random_seed);
15091 else if (tape.pausing)
15093 #if defined(NETWORK_AVALIABLE)
15094 if (options.network)
15095 SendToServer_ContinuePlaying();
15099 tape.pausing = FALSE;
15100 DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF, 0);
15105 case GAME_CTRL_ID_UNDO:
15109 case GAME_CTRL_ID_REDO:
15113 case GAME_CTRL_ID_SAVE:
15117 case GAME_CTRL_ID_LOAD:
15121 case SOUND_CTRL_ID_MUSIC:
15122 if (setup.sound_music)
15124 setup.sound_music = FALSE;
15128 else if (audio.music_available)
15130 setup.sound = setup.sound_music = TRUE;
15132 SetAudioMode(setup.sound);
15138 case SOUND_CTRL_ID_LOOPS:
15139 if (setup.sound_loops)
15140 setup.sound_loops = FALSE;
15141 else if (audio.loops_available)
15143 setup.sound = setup.sound_loops = TRUE;
15145 SetAudioMode(setup.sound);
15149 case SOUND_CTRL_ID_SIMPLE:
15150 if (setup.sound_simple)
15151 setup.sound_simple = FALSE;
15152 else if (audio.sound_available)
15154 setup.sound = setup.sound_simple = TRUE;
15156 SetAudioMode(setup.sound);
15165 static void HandleGameButtons(struct GadgetInfo *gi)
15167 HandleGameButtonsExt(gi->custom_id, gi->event.button);
15170 void HandleSoundButtonKeys(Key key)
15173 if (key == setup.shortcut.sound_simple)
15174 ClickOnGadget(game_gadget[SOUND_CTRL_ID_SIMPLE], MB_LEFTBUTTON);
15175 else if (key == setup.shortcut.sound_loops)
15176 ClickOnGadget(game_gadget[SOUND_CTRL_ID_LOOPS], MB_LEFTBUTTON);
15177 else if (key == setup.shortcut.sound_music)
15178 ClickOnGadget(game_gadget[SOUND_CTRL_ID_MUSIC], MB_LEFTBUTTON);