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 // test element position in level set "test_gfxframe" / level "000"
29 #define DEBUG_GFXFRAME_X 11
30 #define DEBUG_GFXFRAME_Y 9
32 /* EXPERIMENTAL STUFF */
33 #define USE_NEW_AMOEBA_CODE FALSE
35 /* EXPERIMENTAL STUFF */
36 #define USE_QUICKSAND_BD_ROCK_BUGFIX 0
37 #define USE_QUICKSAND_IMPACT_BUGFIX 0
38 #define USE_DELAYED_GFX_REDRAW 0
39 #define USE_NEW_PLAYER_ASSIGNMENTS 1
41 #if USE_DELAYED_GFX_REDRAW
42 #define TEST_DrawLevelField(x, y) \
43 GfxRedraw[x][y] |= GFX_REDRAW_TILE
44 #define TEST_DrawLevelFieldCrumbled(x, y) \
45 GfxRedraw[x][y] |= GFX_REDRAW_TILE_CRUMBLED
46 #define TEST_DrawLevelFieldCrumbledNeighbours(x, y) \
47 GfxRedraw[x][y] |= GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS
48 #define TEST_DrawTwinkleOnField(x, y) \
49 GfxRedraw[x][y] |= GFX_REDRAW_TILE_TWINKLED
51 #define TEST_DrawLevelField(x, y) \
53 #define TEST_DrawLevelFieldCrumbled(x, y) \
54 DrawLevelFieldCrumbled(x, y)
55 #define TEST_DrawLevelFieldCrumbledNeighbours(x, y) \
56 DrawLevelFieldCrumbledNeighbours(x, y)
57 #define TEST_DrawTwinkleOnField(x, y) \
58 DrawTwinkleOnField(x, y)
67 /* for MovePlayer() */
68 #define MP_NO_ACTION 0
71 #define MP_DONT_RUN_INTO (MP_MOVING | MP_ACTION)
73 /* for ScrollPlayer() */
75 #define SCROLL_GO_ON 1
77 /* for Bang()/Explode() */
78 #define EX_PHASE_START 0
79 #define EX_TYPE_NONE 0
80 #define EX_TYPE_NORMAL (1 << 0)
81 #define EX_TYPE_CENTER (1 << 1)
82 #define EX_TYPE_BORDER (1 << 2)
83 #define EX_TYPE_CROSS (1 << 3)
84 #define EX_TYPE_DYNA (1 << 4)
85 #define EX_TYPE_SINGLE_TILE (EX_TYPE_CENTER | EX_TYPE_BORDER)
87 #define PANEL_OFF() (local_player->LevelSolved_PanelOff)
88 #define PANEL_DEACTIVATED(p) ((p)->x < 0 || (p)->y < 0 || PANEL_OFF())
89 #define PANEL_XPOS(p) (DX + ALIGNED_TEXT_XPOS(p))
90 #define PANEL_YPOS(p) (DY + ALIGNED_TEXT_YPOS(p))
92 /* game panel display and control definitions */
93 #define GAME_PANEL_LEVEL_NUMBER 0
94 #define GAME_PANEL_GEMS 1
95 #define GAME_PANEL_INVENTORY_COUNT 2
96 #define GAME_PANEL_INVENTORY_FIRST_1 3
97 #define GAME_PANEL_INVENTORY_FIRST_2 4
98 #define GAME_PANEL_INVENTORY_FIRST_3 5
99 #define GAME_PANEL_INVENTORY_FIRST_4 6
100 #define GAME_PANEL_INVENTORY_FIRST_5 7
101 #define GAME_PANEL_INVENTORY_FIRST_6 8
102 #define GAME_PANEL_INVENTORY_FIRST_7 9
103 #define GAME_PANEL_INVENTORY_FIRST_8 10
104 #define GAME_PANEL_INVENTORY_LAST_1 11
105 #define GAME_PANEL_INVENTORY_LAST_2 12
106 #define GAME_PANEL_INVENTORY_LAST_3 13
107 #define GAME_PANEL_INVENTORY_LAST_4 14
108 #define GAME_PANEL_INVENTORY_LAST_5 15
109 #define GAME_PANEL_INVENTORY_LAST_6 16
110 #define GAME_PANEL_INVENTORY_LAST_7 17
111 #define GAME_PANEL_INVENTORY_LAST_8 18
112 #define GAME_PANEL_KEY_1 19
113 #define GAME_PANEL_KEY_2 20
114 #define GAME_PANEL_KEY_3 21
115 #define GAME_PANEL_KEY_4 22
116 #define GAME_PANEL_KEY_5 23
117 #define GAME_PANEL_KEY_6 24
118 #define GAME_PANEL_KEY_7 25
119 #define GAME_PANEL_KEY_8 26
120 #define GAME_PANEL_KEY_WHITE 27
121 #define GAME_PANEL_KEY_WHITE_COUNT 28
122 #define GAME_PANEL_SCORE 29
123 #define GAME_PANEL_HIGHSCORE 30
124 #define GAME_PANEL_TIME 31
125 #define GAME_PANEL_TIME_HH 32
126 #define GAME_PANEL_TIME_MM 33
127 #define GAME_PANEL_TIME_SS 34
128 #define GAME_PANEL_FRAME 35
129 #define GAME_PANEL_SHIELD_NORMAL 36
130 #define GAME_PANEL_SHIELD_NORMAL_TIME 37
131 #define GAME_PANEL_SHIELD_DEADLY 38
132 #define GAME_PANEL_SHIELD_DEADLY_TIME 39
133 #define GAME_PANEL_EXIT 40
134 #define GAME_PANEL_EMC_MAGIC_BALL 41
135 #define GAME_PANEL_EMC_MAGIC_BALL_SWITCH 42
136 #define GAME_PANEL_LIGHT_SWITCH 43
137 #define GAME_PANEL_LIGHT_SWITCH_TIME 44
138 #define GAME_PANEL_TIMEGATE_SWITCH 45
139 #define GAME_PANEL_TIMEGATE_SWITCH_TIME 46
140 #define GAME_PANEL_SWITCHGATE_SWITCH 47
141 #define GAME_PANEL_EMC_LENSES 48
142 #define GAME_PANEL_EMC_LENSES_TIME 49
143 #define GAME_PANEL_EMC_MAGNIFIER 50
144 #define GAME_PANEL_EMC_MAGNIFIER_TIME 51
145 #define GAME_PANEL_BALLOON_SWITCH 52
146 #define GAME_PANEL_DYNABOMB_NUMBER 53
147 #define GAME_PANEL_DYNABOMB_SIZE 54
148 #define GAME_PANEL_DYNABOMB_POWER 55
149 #define GAME_PANEL_PENGUINS 56
150 #define GAME_PANEL_SOKOBAN_OBJECTS 57
151 #define GAME_PANEL_SOKOBAN_FIELDS 58
152 #define GAME_PANEL_ROBOT_WHEEL 59
153 #define GAME_PANEL_CONVEYOR_BELT_1 60
154 #define GAME_PANEL_CONVEYOR_BELT_2 61
155 #define GAME_PANEL_CONVEYOR_BELT_3 62
156 #define GAME_PANEL_CONVEYOR_BELT_4 63
157 #define GAME_PANEL_CONVEYOR_BELT_1_SWITCH 64
158 #define GAME_PANEL_CONVEYOR_BELT_2_SWITCH 65
159 #define GAME_PANEL_CONVEYOR_BELT_3_SWITCH 66
160 #define GAME_PANEL_CONVEYOR_BELT_4_SWITCH 67
161 #define GAME_PANEL_MAGIC_WALL 68
162 #define GAME_PANEL_MAGIC_WALL_TIME 69
163 #define GAME_PANEL_GRAVITY_STATE 70
164 #define GAME_PANEL_GRAPHIC_1 71
165 #define GAME_PANEL_GRAPHIC_2 72
166 #define GAME_PANEL_GRAPHIC_3 73
167 #define GAME_PANEL_GRAPHIC_4 74
168 #define GAME_PANEL_GRAPHIC_5 75
169 #define GAME_PANEL_GRAPHIC_6 76
170 #define GAME_PANEL_GRAPHIC_7 77
171 #define GAME_PANEL_GRAPHIC_8 78
172 #define GAME_PANEL_ELEMENT_1 79
173 #define GAME_PANEL_ELEMENT_2 80
174 #define GAME_PANEL_ELEMENT_3 81
175 #define GAME_PANEL_ELEMENT_4 82
176 #define GAME_PANEL_ELEMENT_5 83
177 #define GAME_PANEL_ELEMENT_6 84
178 #define GAME_PANEL_ELEMENT_7 85
179 #define GAME_PANEL_ELEMENT_8 86
180 #define GAME_PANEL_ELEMENT_COUNT_1 87
181 #define GAME_PANEL_ELEMENT_COUNT_2 88
182 #define GAME_PANEL_ELEMENT_COUNT_3 89
183 #define GAME_PANEL_ELEMENT_COUNT_4 90
184 #define GAME_PANEL_ELEMENT_COUNT_5 91
185 #define GAME_PANEL_ELEMENT_COUNT_6 92
186 #define GAME_PANEL_ELEMENT_COUNT_7 93
187 #define GAME_PANEL_ELEMENT_COUNT_8 94
188 #define GAME_PANEL_CE_SCORE_1 95
189 #define GAME_PANEL_CE_SCORE_2 96
190 #define GAME_PANEL_CE_SCORE_3 97
191 #define GAME_PANEL_CE_SCORE_4 98
192 #define GAME_PANEL_CE_SCORE_5 99
193 #define GAME_PANEL_CE_SCORE_6 100
194 #define GAME_PANEL_CE_SCORE_7 101
195 #define GAME_PANEL_CE_SCORE_8 102
196 #define GAME_PANEL_CE_SCORE_1_ELEMENT 103
197 #define GAME_PANEL_CE_SCORE_2_ELEMENT 104
198 #define GAME_PANEL_CE_SCORE_3_ELEMENT 105
199 #define GAME_PANEL_CE_SCORE_4_ELEMENT 106
200 #define GAME_PANEL_CE_SCORE_5_ELEMENT 107
201 #define GAME_PANEL_CE_SCORE_6_ELEMENT 108
202 #define GAME_PANEL_CE_SCORE_7_ELEMENT 109
203 #define GAME_PANEL_CE_SCORE_8_ELEMENT 110
204 #define GAME_PANEL_PLAYER_NAME 111
205 #define GAME_PANEL_LEVEL_NAME 112
206 #define GAME_PANEL_LEVEL_AUTHOR 113
208 #define NUM_GAME_PANEL_CONTROLS 114
210 struct GamePanelOrderInfo
216 static struct GamePanelOrderInfo game_panel_order[NUM_GAME_PANEL_CONTROLS];
218 struct GamePanelControlInfo
222 struct TextPosInfo *pos;
225 int value, last_value;
226 int frame, last_frame;
231 static struct GamePanelControlInfo game_panel_controls[] =
234 GAME_PANEL_LEVEL_NUMBER,
235 &game.panel.level_number,
244 GAME_PANEL_INVENTORY_COUNT,
245 &game.panel.inventory_count,
249 GAME_PANEL_INVENTORY_FIRST_1,
250 &game.panel.inventory_first[0],
254 GAME_PANEL_INVENTORY_FIRST_2,
255 &game.panel.inventory_first[1],
259 GAME_PANEL_INVENTORY_FIRST_3,
260 &game.panel.inventory_first[2],
264 GAME_PANEL_INVENTORY_FIRST_4,
265 &game.panel.inventory_first[3],
269 GAME_PANEL_INVENTORY_FIRST_5,
270 &game.panel.inventory_first[4],
274 GAME_PANEL_INVENTORY_FIRST_6,
275 &game.panel.inventory_first[5],
279 GAME_PANEL_INVENTORY_FIRST_7,
280 &game.panel.inventory_first[6],
284 GAME_PANEL_INVENTORY_FIRST_8,
285 &game.panel.inventory_first[7],
289 GAME_PANEL_INVENTORY_LAST_1,
290 &game.panel.inventory_last[0],
294 GAME_PANEL_INVENTORY_LAST_2,
295 &game.panel.inventory_last[1],
299 GAME_PANEL_INVENTORY_LAST_3,
300 &game.panel.inventory_last[2],
304 GAME_PANEL_INVENTORY_LAST_4,
305 &game.panel.inventory_last[3],
309 GAME_PANEL_INVENTORY_LAST_5,
310 &game.panel.inventory_last[4],
314 GAME_PANEL_INVENTORY_LAST_6,
315 &game.panel.inventory_last[5],
319 GAME_PANEL_INVENTORY_LAST_7,
320 &game.panel.inventory_last[6],
324 GAME_PANEL_INVENTORY_LAST_8,
325 &game.panel.inventory_last[7],
369 GAME_PANEL_KEY_WHITE,
370 &game.panel.key_white,
374 GAME_PANEL_KEY_WHITE_COUNT,
375 &game.panel.key_white_count,
384 GAME_PANEL_HIGHSCORE,
385 &game.panel.highscore,
414 GAME_PANEL_SHIELD_NORMAL,
415 &game.panel.shield_normal,
419 GAME_PANEL_SHIELD_NORMAL_TIME,
420 &game.panel.shield_normal_time,
424 GAME_PANEL_SHIELD_DEADLY,
425 &game.panel.shield_deadly,
429 GAME_PANEL_SHIELD_DEADLY_TIME,
430 &game.panel.shield_deadly_time,
439 GAME_PANEL_EMC_MAGIC_BALL,
440 &game.panel.emc_magic_ball,
444 GAME_PANEL_EMC_MAGIC_BALL_SWITCH,
445 &game.panel.emc_magic_ball_switch,
449 GAME_PANEL_LIGHT_SWITCH,
450 &game.panel.light_switch,
454 GAME_PANEL_LIGHT_SWITCH_TIME,
455 &game.panel.light_switch_time,
459 GAME_PANEL_TIMEGATE_SWITCH,
460 &game.panel.timegate_switch,
464 GAME_PANEL_TIMEGATE_SWITCH_TIME,
465 &game.panel.timegate_switch_time,
469 GAME_PANEL_SWITCHGATE_SWITCH,
470 &game.panel.switchgate_switch,
474 GAME_PANEL_EMC_LENSES,
475 &game.panel.emc_lenses,
479 GAME_PANEL_EMC_LENSES_TIME,
480 &game.panel.emc_lenses_time,
484 GAME_PANEL_EMC_MAGNIFIER,
485 &game.panel.emc_magnifier,
489 GAME_PANEL_EMC_MAGNIFIER_TIME,
490 &game.panel.emc_magnifier_time,
494 GAME_PANEL_BALLOON_SWITCH,
495 &game.panel.balloon_switch,
499 GAME_PANEL_DYNABOMB_NUMBER,
500 &game.panel.dynabomb_number,
504 GAME_PANEL_DYNABOMB_SIZE,
505 &game.panel.dynabomb_size,
509 GAME_PANEL_DYNABOMB_POWER,
510 &game.panel.dynabomb_power,
515 &game.panel.penguins,
519 GAME_PANEL_SOKOBAN_OBJECTS,
520 &game.panel.sokoban_objects,
524 GAME_PANEL_SOKOBAN_FIELDS,
525 &game.panel.sokoban_fields,
529 GAME_PANEL_ROBOT_WHEEL,
530 &game.panel.robot_wheel,
534 GAME_PANEL_CONVEYOR_BELT_1,
535 &game.panel.conveyor_belt[0],
539 GAME_PANEL_CONVEYOR_BELT_2,
540 &game.panel.conveyor_belt[1],
544 GAME_PANEL_CONVEYOR_BELT_3,
545 &game.panel.conveyor_belt[2],
549 GAME_PANEL_CONVEYOR_BELT_4,
550 &game.panel.conveyor_belt[3],
554 GAME_PANEL_CONVEYOR_BELT_1_SWITCH,
555 &game.panel.conveyor_belt_switch[0],
559 GAME_PANEL_CONVEYOR_BELT_2_SWITCH,
560 &game.panel.conveyor_belt_switch[1],
564 GAME_PANEL_CONVEYOR_BELT_3_SWITCH,
565 &game.panel.conveyor_belt_switch[2],
569 GAME_PANEL_CONVEYOR_BELT_4_SWITCH,
570 &game.panel.conveyor_belt_switch[3],
574 GAME_PANEL_MAGIC_WALL,
575 &game.panel.magic_wall,
579 GAME_PANEL_MAGIC_WALL_TIME,
580 &game.panel.magic_wall_time,
584 GAME_PANEL_GRAVITY_STATE,
585 &game.panel.gravity_state,
589 GAME_PANEL_GRAPHIC_1,
590 &game.panel.graphic[0],
594 GAME_PANEL_GRAPHIC_2,
595 &game.panel.graphic[1],
599 GAME_PANEL_GRAPHIC_3,
600 &game.panel.graphic[2],
604 GAME_PANEL_GRAPHIC_4,
605 &game.panel.graphic[3],
609 GAME_PANEL_GRAPHIC_5,
610 &game.panel.graphic[4],
614 GAME_PANEL_GRAPHIC_6,
615 &game.panel.graphic[5],
619 GAME_PANEL_GRAPHIC_7,
620 &game.panel.graphic[6],
624 GAME_PANEL_GRAPHIC_8,
625 &game.panel.graphic[7],
629 GAME_PANEL_ELEMENT_1,
630 &game.panel.element[0],
634 GAME_PANEL_ELEMENT_2,
635 &game.panel.element[1],
639 GAME_PANEL_ELEMENT_3,
640 &game.panel.element[2],
644 GAME_PANEL_ELEMENT_4,
645 &game.panel.element[3],
649 GAME_PANEL_ELEMENT_5,
650 &game.panel.element[4],
654 GAME_PANEL_ELEMENT_6,
655 &game.panel.element[5],
659 GAME_PANEL_ELEMENT_7,
660 &game.panel.element[6],
664 GAME_PANEL_ELEMENT_8,
665 &game.panel.element[7],
669 GAME_PANEL_ELEMENT_COUNT_1,
670 &game.panel.element_count[0],
674 GAME_PANEL_ELEMENT_COUNT_2,
675 &game.panel.element_count[1],
679 GAME_PANEL_ELEMENT_COUNT_3,
680 &game.panel.element_count[2],
684 GAME_PANEL_ELEMENT_COUNT_4,
685 &game.panel.element_count[3],
689 GAME_PANEL_ELEMENT_COUNT_5,
690 &game.panel.element_count[4],
694 GAME_PANEL_ELEMENT_COUNT_6,
695 &game.panel.element_count[5],
699 GAME_PANEL_ELEMENT_COUNT_7,
700 &game.panel.element_count[6],
704 GAME_PANEL_ELEMENT_COUNT_8,
705 &game.panel.element_count[7],
709 GAME_PANEL_CE_SCORE_1,
710 &game.panel.ce_score[0],
714 GAME_PANEL_CE_SCORE_2,
715 &game.panel.ce_score[1],
719 GAME_PANEL_CE_SCORE_3,
720 &game.panel.ce_score[2],
724 GAME_PANEL_CE_SCORE_4,
725 &game.panel.ce_score[3],
729 GAME_PANEL_CE_SCORE_5,
730 &game.panel.ce_score[4],
734 GAME_PANEL_CE_SCORE_6,
735 &game.panel.ce_score[5],
739 GAME_PANEL_CE_SCORE_7,
740 &game.panel.ce_score[6],
744 GAME_PANEL_CE_SCORE_8,
745 &game.panel.ce_score[7],
749 GAME_PANEL_CE_SCORE_1_ELEMENT,
750 &game.panel.ce_score_element[0],
754 GAME_PANEL_CE_SCORE_2_ELEMENT,
755 &game.panel.ce_score_element[1],
759 GAME_PANEL_CE_SCORE_3_ELEMENT,
760 &game.panel.ce_score_element[2],
764 GAME_PANEL_CE_SCORE_4_ELEMENT,
765 &game.panel.ce_score_element[3],
769 GAME_PANEL_CE_SCORE_5_ELEMENT,
770 &game.panel.ce_score_element[4],
774 GAME_PANEL_CE_SCORE_6_ELEMENT,
775 &game.panel.ce_score_element[5],
779 GAME_PANEL_CE_SCORE_7_ELEMENT,
780 &game.panel.ce_score_element[6],
784 GAME_PANEL_CE_SCORE_8_ELEMENT,
785 &game.panel.ce_score_element[7],
789 GAME_PANEL_PLAYER_NAME,
790 &game.panel.player_name,
794 GAME_PANEL_LEVEL_NAME,
795 &game.panel.level_name,
799 GAME_PANEL_LEVEL_AUTHOR,
800 &game.panel.level_author,
811 /* values for delayed check of falling and moving elements and for collision */
812 #define CHECK_DELAY_MOVING 3
813 #define CHECK_DELAY_FALLING CHECK_DELAY_MOVING
814 #define CHECK_DELAY_COLLISION 2
815 #define CHECK_DELAY_IMPACT CHECK_DELAY_COLLISION
817 /* values for initial player move delay (initial delay counter value) */
818 #define INITIAL_MOVE_DELAY_OFF -1
819 #define INITIAL_MOVE_DELAY_ON 0
821 /* values for player movement speed (which is in fact a delay value) */
822 #define MOVE_DELAY_MIN_SPEED 32
823 #define MOVE_DELAY_NORMAL_SPEED 8
824 #define MOVE_DELAY_HIGH_SPEED 4
825 #define MOVE_DELAY_MAX_SPEED 1
827 #define DOUBLE_MOVE_DELAY(x) (x = (x < MOVE_DELAY_MIN_SPEED ? x * 2 : x))
828 #define HALVE_MOVE_DELAY(x) (x = (x > MOVE_DELAY_MAX_SPEED ? x / 2 : x))
830 #define DOUBLE_PLAYER_SPEED(p) (HALVE_MOVE_DELAY( (p)->move_delay_value))
831 #define HALVE_PLAYER_SPEED(p) (DOUBLE_MOVE_DELAY((p)->move_delay_value))
833 /* values for other actions */
834 #define MOVE_STEPSIZE_NORMAL (TILEX / MOVE_DELAY_NORMAL_SPEED)
835 #define MOVE_STEPSIZE_MIN (1)
836 #define MOVE_STEPSIZE_MAX (TILEX)
838 #define GET_DX_FROM_DIR(d) ((d) == MV_LEFT ? -1 : (d) == MV_RIGHT ? 1 : 0)
839 #define GET_DY_FROM_DIR(d) ((d) == MV_UP ? -1 : (d) == MV_DOWN ? 1 : 0)
841 #define INIT_GFX_RANDOM() (GetSimpleRandom(1000000))
843 #define GET_NEW_PUSH_DELAY(e) ( (element_info[e].push_delay_fixed) + \
844 RND(element_info[e].push_delay_random))
845 #define GET_NEW_DROP_DELAY(e) ( (element_info[e].drop_delay_fixed) + \
846 RND(element_info[e].drop_delay_random))
847 #define GET_NEW_MOVE_DELAY(e) ( (element_info[e].move_delay_fixed) + \
848 RND(element_info[e].move_delay_random))
849 #define GET_MAX_MOVE_DELAY(e) ( (element_info[e].move_delay_fixed) + \
850 (element_info[e].move_delay_random))
851 #define GET_NEW_CE_VALUE(e) ( (element_info[e].ce_value_fixed_initial) +\
852 RND(element_info[e].ce_value_random_initial))
853 #define GET_CE_SCORE(e) ( (element_info[e].collect_score))
854 #define GET_CHANGE_DELAY(c) ( ((c)->delay_fixed * (c)->delay_frames) + \
855 RND((c)->delay_random * (c)->delay_frames))
856 #define GET_CE_DELAY_VALUE(c) ( ((c)->delay_fixed) + \
857 RND((c)->delay_random))
860 #define GET_VALID_RUNTIME_ELEMENT(e) \
861 ((e) >= NUM_RUNTIME_ELEMENTS ? EL_UNKNOWN : (e))
863 #define RESOLVED_REFERENCE_ELEMENT(be, e) \
864 ((be) + (e) - EL_SELF < EL_CUSTOM_START ? EL_CUSTOM_START : \
865 (be) + (e) - EL_SELF > EL_CUSTOM_END ? EL_CUSTOM_END : \
866 (be) + (e) - EL_SELF)
868 #define GET_PLAYER_FROM_BITS(p) \
869 (EL_PLAYER_1 + ((p) != PLAYER_BITS_ANY ? log_2(p) : 0))
871 #define GET_TARGET_ELEMENT(be, e, ch, cv, cs) \
872 ((e) == EL_TRIGGER_PLAYER ? (ch)->actual_trigger_player : \
873 (e) == EL_TRIGGER_ELEMENT ? (ch)->actual_trigger_element : \
874 (e) == EL_TRIGGER_CE_VALUE ? (ch)->actual_trigger_ce_value : \
875 (e) == EL_TRIGGER_CE_SCORE ? (ch)->actual_trigger_ce_score : \
876 (e) == EL_CURRENT_CE_VALUE ? (cv) : \
877 (e) == EL_CURRENT_CE_SCORE ? (cs) : \
878 (e) >= EL_PREV_CE_8 && (e) <= EL_NEXT_CE_8 ? \
879 RESOLVED_REFERENCE_ELEMENT(be, e) : \
882 #define CAN_GROW_INTO(e) \
883 ((e) == EL_SAND || (IS_DIGGABLE(e) && level.grow_into_diggable))
885 #define ELEMENT_CAN_ENTER_FIELD_BASE_X(x, y, condition) \
886 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
889 #define ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, condition) \
890 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
891 (CAN_MOVE_INTO_ACID(e) && \
892 Feld[x][y] == EL_ACID) || \
895 #define ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, condition) \
896 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) || \
897 (CAN_MOVE_INTO_ACID(e) && \
898 Feld[x][y] == EL_ACID) || \
901 #define ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, condition) \
902 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
904 (CAN_MOVE_INTO_ACID(e) && \
905 Feld[x][y] == EL_ACID) || \
906 (DONT_COLLIDE_WITH(e) && \
908 !PLAYER_ENEMY_PROTECTED(x, y))))
910 #define ELEMENT_CAN_ENTER_FIELD(e, x, y) \
911 ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, 0)
913 #define SATELLITE_CAN_ENTER_FIELD(x, y) \
914 ELEMENT_CAN_ENTER_FIELD_BASE_2(EL_SATELLITE, x, y, 0)
916 #define ANDROID_CAN_ENTER_FIELD(e, x, y) \
917 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, Feld[x][y] == EL_EMC_PLANT)
919 #define ANDROID_CAN_CLONE_FIELD(x, y) \
920 (IN_LEV_FIELD(x, y) && (CAN_BE_CLONED_BY_ANDROID(Feld[x][y]) || \
921 CAN_BE_CLONED_BY_ANDROID(EL_TRIGGER_ELEMENT)))
923 #define ENEMY_CAN_ENTER_FIELD(e, x, y) \
924 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
926 #define YAMYAM_CAN_ENTER_FIELD(e, x, y) \
927 ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, Feld[x][y] == EL_DIAMOND)
929 #define DARK_YAMYAM_CAN_ENTER_FIELD(e, x, y) \
930 ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x,y, IS_FOOD_DARK_YAMYAM(Feld[x][y]))
932 #define PACMAN_CAN_ENTER_FIELD(e, x, y) \
933 ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, IS_AMOEBOID(Feld[x][y]))
935 #define PIG_CAN_ENTER_FIELD(e, x, y) \
936 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, IS_FOOD_PIG(Feld[x][y]))
938 #define PENGUIN_CAN_ENTER_FIELD(e, x, y) \
939 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (Feld[x][y] == EL_EXIT_OPEN || \
940 Feld[x][y] == EL_EM_EXIT_OPEN || \
941 Feld[x][y] == EL_STEEL_EXIT_OPEN || \
942 Feld[x][y] == EL_EM_STEEL_EXIT_OPEN || \
943 IS_FOOD_PENGUIN(Feld[x][y])))
944 #define DRAGON_CAN_ENTER_FIELD(e, x, y) \
945 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
947 #define MOLE_CAN_ENTER_FIELD(e, x, y, condition) \
948 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (condition))
950 #define SPRING_CAN_ENTER_FIELD(e, x, y) \
951 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
953 #define SPRING_CAN_BUMP_FROM_FIELD(x, y) \
954 (IN_LEV_FIELD(x, y) && (Feld[x][y] == EL_EMC_SPRING_BUMPER || \
955 Feld[x][y] == EL_EMC_SPRING_BUMPER_ACTIVE))
957 #define MOVE_ENTER_EL(e) (element_info[e].move_enter_element)
959 #define CE_ENTER_FIELD_COND(e, x, y) \
960 (!IS_PLAYER(x, y) && \
961 IS_EQUAL_OR_IN_GROUP(Feld[x][y], MOVE_ENTER_EL(e)))
963 #define CUSTOM_ELEMENT_CAN_ENTER_FIELD(e, x, y) \
964 ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, CE_ENTER_FIELD_COND(e, x, y))
966 #define IN_LEV_FIELD_AND_IS_FREE(x, y) (IN_LEV_FIELD(x, y) && IS_FREE(x, y))
967 #define IN_LEV_FIELD_AND_NOT_FREE(x, y) (IN_LEV_FIELD(x, y) && !IS_FREE(x, y))
969 #define ACCESS_FROM(e, d) (element_info[e].access_direction &(d))
970 #define IS_WALKABLE_FROM(e, d) (IS_WALKABLE(e) && ACCESS_FROM(e, d))
971 #define IS_PASSABLE_FROM(e, d) (IS_PASSABLE(e) && ACCESS_FROM(e, d))
972 #define IS_ACCESSIBLE_FROM(e, d) (IS_ACCESSIBLE(e) && ACCESS_FROM(e, d))
974 /* game button identifiers */
975 #define GAME_CTRL_ID_STOP 0
976 #define GAME_CTRL_ID_PAUSE 1
977 #define GAME_CTRL_ID_PLAY 2
978 #define GAME_CTRL_ID_UNDO 3
979 #define GAME_CTRL_ID_REDO 4
980 #define GAME_CTRL_ID_SAVE 5
981 #define GAME_CTRL_ID_PAUSE2 6
982 #define GAME_CTRL_ID_LOAD 7
983 #define SOUND_CTRL_ID_MUSIC 8
984 #define SOUND_CTRL_ID_LOOPS 9
985 #define SOUND_CTRL_ID_SIMPLE 10
987 #define NUM_GAME_BUTTONS 11
990 /* forward declaration for internal use */
992 static void CreateField(int, int, int);
994 static void ResetGfxAnimation(int, int);
996 static void SetPlayerWaiting(struct PlayerInfo *, boolean);
997 static void AdvanceFrameAndPlayerCounters(int);
999 static boolean MovePlayerOneStep(struct PlayerInfo *, int, int, int, int);
1000 static boolean MovePlayer(struct PlayerInfo *, int, int);
1001 static void ScrollPlayer(struct PlayerInfo *, int);
1002 static void ScrollScreen(struct PlayerInfo *, int);
1004 static int DigField(struct PlayerInfo *, int, int, int, int, int, int, int);
1005 static boolean DigFieldByCE(int, int, int);
1006 static boolean SnapField(struct PlayerInfo *, int, int);
1007 static boolean DropElement(struct PlayerInfo *);
1009 static void InitBeltMovement(void);
1010 static void CloseAllOpenTimegates(void);
1011 static void CheckGravityMovement(struct PlayerInfo *);
1012 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *);
1013 static void KillPlayerUnlessEnemyProtected(int, int);
1014 static void KillPlayerUnlessExplosionProtected(int, int);
1016 static void TestIfPlayerTouchesCustomElement(int, int);
1017 static void TestIfElementTouchesCustomElement(int, int);
1018 static void TestIfElementHitsCustomElement(int, int, int);
1020 static void HandleElementChange(int, int, int);
1021 static void ExecuteCustomElementAction(int, int, int, int);
1022 static boolean ChangeElement(int, int, int, int);
1024 static boolean CheckTriggeredElementChangeExt(int, int, int, int, int,int,int);
1025 #define CheckTriggeredElementChange(x, y, e, ev) \
1026 CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY,CH_SIDE_ANY, -1)
1027 #define CheckTriggeredElementChangeByPlayer(x, y, e, ev, p, s) \
1028 CheckTriggeredElementChangeExt(x, y, e, ev, p, s, -1)
1029 #define CheckTriggeredElementChangeBySide(x, y, e, ev, s) \
1030 CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, s, -1)
1031 #define CheckTriggeredElementChangeByPage(x, y, e, ev, p) \
1032 CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY, CH_SIDE_ANY, p)
1034 static boolean CheckElementChangeExt(int, int, int, int, int, int, int);
1035 #define CheckElementChange(x, y, e, te, ev) \
1036 CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, CH_SIDE_ANY)
1037 #define CheckElementChangeByPlayer(x, y, e, ev, p, s) \
1038 CheckElementChangeExt(x, y, e, EL_EMPTY, ev, p, s)
1039 #define CheckElementChangeBySide(x, y, e, te, ev, s) \
1040 CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, s)
1042 static void PlayLevelSound(int, int, int);
1043 static void PlayLevelSoundNearest(int, int, int);
1044 static void PlayLevelSoundAction(int, int, int);
1045 static void PlayLevelSoundElementAction(int, int, int, int);
1046 static void PlayLevelSoundElementActionIfLoop(int, int, int, int);
1047 static void PlayLevelSoundActionIfLoop(int, int, int);
1048 static void StopLevelSoundActionIfLoop(int, int, int);
1049 static void PlayLevelMusic();
1051 static void HandleGameButtons(struct GadgetInfo *);
1053 int AmoebeNachbarNr(int, int);
1054 void AmoebeUmwandeln(int, int);
1055 void ContinueMoving(int, int);
1056 void Bang(int, int);
1057 void InitMovDir(int, int);
1058 void InitAmoebaNr(int, int);
1059 int NewHiScore(void);
1061 void TestIfGoodThingHitsBadThing(int, int, int);
1062 void TestIfBadThingHitsGoodThing(int, int, int);
1063 void TestIfPlayerTouchesBadThing(int, int);
1064 void TestIfPlayerRunsIntoBadThing(int, int, int);
1065 void TestIfBadThingTouchesPlayer(int, int);
1066 void TestIfBadThingRunsIntoPlayer(int, int, int);
1067 void TestIfFriendTouchesBadThing(int, int);
1068 void TestIfBadThingTouchesFriend(int, int);
1069 void TestIfBadThingTouchesOtherBadThing(int, int);
1070 void TestIfGoodThingGetsHitByBadThing(int, int, int);
1072 void KillPlayer(struct PlayerInfo *);
1073 void BuryPlayer(struct PlayerInfo *);
1074 void RemovePlayer(struct PlayerInfo *);
1076 static int getInvisibleActiveFromInvisibleElement(int);
1077 static int getInvisibleFromInvisibleActiveElement(int);
1079 static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
1081 /* for detection of endless loops, caused by custom element programming */
1082 /* (using maximal playfield width x 10 is just a rough approximation) */
1083 #define MAX_ELEMENT_CHANGE_RECURSION_DEPTH (MAX_PLAYFIELD_WIDTH * 10)
1085 #define RECURSION_LOOP_DETECTION_START(e, rc) \
1087 if (recursion_loop_detected) \
1090 if (recursion_loop_depth > MAX_ELEMENT_CHANGE_RECURSION_DEPTH) \
1092 recursion_loop_detected = TRUE; \
1093 recursion_loop_element = (e); \
1096 recursion_loop_depth++; \
1099 #define RECURSION_LOOP_DETECTION_END() \
1101 recursion_loop_depth--; \
1104 static int recursion_loop_depth;
1105 static boolean recursion_loop_detected;
1106 static boolean recursion_loop_element;
1108 static int map_player_action[MAX_PLAYERS];
1111 /* ------------------------------------------------------------------------- */
1112 /* definition of elements that automatically change to other elements after */
1113 /* a specified time, eventually calling a function when changing */
1114 /* ------------------------------------------------------------------------- */
1116 /* forward declaration for changer functions */
1117 static void InitBuggyBase(int, int);
1118 static void WarnBuggyBase(int, int);
1120 static void InitTrap(int, int);
1121 static void ActivateTrap(int, int);
1122 static void ChangeActiveTrap(int, int);
1124 static void InitRobotWheel(int, int);
1125 static void RunRobotWheel(int, int);
1126 static void StopRobotWheel(int, int);
1128 static void InitTimegateWheel(int, int);
1129 static void RunTimegateWheel(int, int);
1131 static void InitMagicBallDelay(int, int);
1132 static void ActivateMagicBall(int, int);
1134 struct ChangingElementInfo
1139 void (*pre_change_function)(int x, int y);
1140 void (*change_function)(int x, int y);
1141 void (*post_change_function)(int x, int y);
1144 static struct ChangingElementInfo change_delay_list[] =
1179 EL_STEEL_EXIT_OPENING,
1187 EL_STEEL_EXIT_CLOSING,
1188 EL_STEEL_EXIT_CLOSED,
1211 EL_EM_STEEL_EXIT_OPENING,
1212 EL_EM_STEEL_EXIT_OPEN,
1219 EL_EM_STEEL_EXIT_CLOSING,
1243 EL_SWITCHGATE_OPENING,
1251 EL_SWITCHGATE_CLOSING,
1252 EL_SWITCHGATE_CLOSED,
1259 EL_TIMEGATE_OPENING,
1267 EL_TIMEGATE_CLOSING,
1276 EL_ACID_SPLASH_LEFT,
1284 EL_ACID_SPLASH_RIGHT,
1293 EL_SP_BUGGY_BASE_ACTIVATING,
1300 EL_SP_BUGGY_BASE_ACTIVATING,
1301 EL_SP_BUGGY_BASE_ACTIVE,
1308 EL_SP_BUGGY_BASE_ACTIVE,
1332 EL_ROBOT_WHEEL_ACTIVE,
1340 EL_TIMEGATE_SWITCH_ACTIVE,
1348 EL_DC_TIMEGATE_SWITCH_ACTIVE,
1349 EL_DC_TIMEGATE_SWITCH,
1356 EL_EMC_MAGIC_BALL_ACTIVE,
1357 EL_EMC_MAGIC_BALL_ACTIVE,
1364 EL_EMC_SPRING_BUMPER_ACTIVE,
1365 EL_EMC_SPRING_BUMPER,
1372 EL_DIAGONAL_SHRINKING,
1380 EL_DIAGONAL_GROWING,
1401 int push_delay_fixed, push_delay_random;
1405 { EL_SPRING, 0, 0 },
1406 { EL_BALLOON, 0, 0 },
1408 { EL_SOKOBAN_OBJECT, 2, 0 },
1409 { EL_SOKOBAN_FIELD_FULL, 2, 0 },
1410 { EL_SATELLITE, 2, 0 },
1411 { EL_SP_DISK_YELLOW, 2, 0 },
1413 { EL_UNDEFINED, 0, 0 },
1421 move_stepsize_list[] =
1423 { EL_AMOEBA_DROP, 2 },
1424 { EL_AMOEBA_DROPPING, 2 },
1425 { EL_QUICKSAND_FILLING, 1 },
1426 { EL_QUICKSAND_EMPTYING, 1 },
1427 { EL_QUICKSAND_FAST_FILLING, 2 },
1428 { EL_QUICKSAND_FAST_EMPTYING, 2 },
1429 { EL_MAGIC_WALL_FILLING, 2 },
1430 { EL_MAGIC_WALL_EMPTYING, 2 },
1431 { EL_BD_MAGIC_WALL_FILLING, 2 },
1432 { EL_BD_MAGIC_WALL_EMPTYING, 2 },
1433 { EL_DC_MAGIC_WALL_FILLING, 2 },
1434 { EL_DC_MAGIC_WALL_EMPTYING, 2 },
1436 { EL_UNDEFINED, 0 },
1444 collect_count_list[] =
1447 { EL_BD_DIAMOND, 1 },
1448 { EL_EMERALD_YELLOW, 1 },
1449 { EL_EMERALD_RED, 1 },
1450 { EL_EMERALD_PURPLE, 1 },
1452 { EL_SP_INFOTRON, 1 },
1456 { EL_UNDEFINED, 0 },
1464 access_direction_list[] =
1466 { EL_TUBE_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1467 { EL_TUBE_VERTICAL, MV_UP | MV_DOWN },
1468 { EL_TUBE_HORIZONTAL, MV_LEFT | MV_RIGHT },
1469 { EL_TUBE_VERTICAL_LEFT, MV_LEFT | MV_UP | MV_DOWN },
1470 { EL_TUBE_VERTICAL_RIGHT, MV_RIGHT | MV_UP | MV_DOWN },
1471 { EL_TUBE_HORIZONTAL_UP, MV_LEFT | MV_RIGHT | MV_UP },
1472 { EL_TUBE_HORIZONTAL_DOWN, MV_LEFT | MV_RIGHT | MV_DOWN },
1473 { EL_TUBE_LEFT_UP, MV_LEFT | MV_UP },
1474 { EL_TUBE_LEFT_DOWN, MV_LEFT | MV_DOWN },
1475 { EL_TUBE_RIGHT_UP, MV_RIGHT | MV_UP },
1476 { EL_TUBE_RIGHT_DOWN, MV_RIGHT | MV_DOWN },
1478 { EL_SP_PORT_LEFT, MV_RIGHT },
1479 { EL_SP_PORT_RIGHT, MV_LEFT },
1480 { EL_SP_PORT_UP, MV_DOWN },
1481 { EL_SP_PORT_DOWN, MV_UP },
1482 { EL_SP_PORT_HORIZONTAL, MV_LEFT | MV_RIGHT },
1483 { EL_SP_PORT_VERTICAL, MV_UP | MV_DOWN },
1484 { EL_SP_PORT_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1485 { EL_SP_GRAVITY_PORT_LEFT, MV_RIGHT },
1486 { EL_SP_GRAVITY_PORT_RIGHT, MV_LEFT },
1487 { EL_SP_GRAVITY_PORT_UP, MV_DOWN },
1488 { EL_SP_GRAVITY_PORT_DOWN, MV_UP },
1489 { EL_SP_GRAVITY_ON_PORT_LEFT, MV_RIGHT },
1490 { EL_SP_GRAVITY_ON_PORT_RIGHT, MV_LEFT },
1491 { EL_SP_GRAVITY_ON_PORT_UP, MV_DOWN },
1492 { EL_SP_GRAVITY_ON_PORT_DOWN, MV_UP },
1493 { EL_SP_GRAVITY_OFF_PORT_LEFT, MV_RIGHT },
1494 { EL_SP_GRAVITY_OFF_PORT_RIGHT, MV_LEFT },
1495 { EL_SP_GRAVITY_OFF_PORT_UP, MV_DOWN },
1496 { EL_SP_GRAVITY_OFF_PORT_DOWN, MV_UP },
1498 { EL_UNDEFINED, MV_NONE }
1501 static boolean trigger_events[MAX_NUM_ELEMENTS][NUM_CHANGE_EVENTS];
1503 #define IS_AUTO_CHANGING(e) (element_info[e].has_change_event[CE_DELAY])
1504 #define IS_JUST_CHANGING(x, y) (ChangeDelay[x][y] != 0)
1505 #define IS_CHANGING(x, y) (IS_AUTO_CHANGING(Feld[x][y]) || \
1506 IS_JUST_CHANGING(x, y))
1508 #define CE_PAGE(e, ce) (element_info[e].event_page[ce])
1510 /* static variables for playfield scan mode (scanning forward or backward) */
1511 static int playfield_scan_start_x = 0;
1512 static int playfield_scan_start_y = 0;
1513 static int playfield_scan_delta_x = 1;
1514 static int playfield_scan_delta_y = 1;
1516 #define SCAN_PLAYFIELD(x, y) for ((y) = playfield_scan_start_y; \
1517 (y) >= 0 && (y) <= lev_fieldy - 1; \
1518 (y) += playfield_scan_delta_y) \
1519 for ((x) = playfield_scan_start_x; \
1520 (x) >= 0 && (x) <= lev_fieldx - 1; \
1521 (x) += playfield_scan_delta_x)
1524 void DEBUG_SetMaximumDynamite()
1528 for (i = 0; i < MAX_INVENTORY_SIZE; i++)
1529 if (local_player->inventory_size < MAX_INVENTORY_SIZE)
1530 local_player->inventory_element[local_player->inventory_size++] =
1535 static void InitPlayfieldScanModeVars()
1537 if (game.use_reverse_scan_direction)
1539 playfield_scan_start_x = lev_fieldx - 1;
1540 playfield_scan_start_y = lev_fieldy - 1;
1542 playfield_scan_delta_x = -1;
1543 playfield_scan_delta_y = -1;
1547 playfield_scan_start_x = 0;
1548 playfield_scan_start_y = 0;
1550 playfield_scan_delta_x = 1;
1551 playfield_scan_delta_y = 1;
1555 static void InitPlayfieldScanMode(int mode)
1557 game.use_reverse_scan_direction =
1558 (mode == CA_ARG_SCAN_MODE_REVERSE ? TRUE : FALSE);
1560 InitPlayfieldScanModeVars();
1563 static int get_move_delay_from_stepsize(int move_stepsize)
1566 MIN(MAX(MOVE_STEPSIZE_MIN, move_stepsize), MOVE_STEPSIZE_MAX);
1568 /* make sure that stepsize value is always a power of 2 */
1569 move_stepsize = (1 << log_2(move_stepsize));
1571 return TILEX / move_stepsize;
1574 static void SetPlayerMoveSpeed(struct PlayerInfo *player, int move_stepsize,
1577 int player_nr = player->index_nr;
1578 int move_delay = get_move_delay_from_stepsize(move_stepsize);
1579 boolean cannot_move = (move_stepsize == STEPSIZE_NOT_MOVING ? TRUE : FALSE);
1581 /* do no immediately change move delay -- the player might just be moving */
1582 player->move_delay_value_next = move_delay;
1584 /* information if player can move must be set separately */
1585 player->cannot_move = cannot_move;
1589 player->move_delay = game.initial_move_delay[player_nr];
1590 player->move_delay_value = game.initial_move_delay_value[player_nr];
1592 player->move_delay_value_next = -1;
1594 player->move_delay_reset_counter = 0;
1598 void GetPlayerConfig()
1600 GameFrameDelay = setup.game_frame_delay;
1602 if (!audio.sound_available)
1603 setup.sound_simple = FALSE;
1605 if (!audio.loops_available)
1606 setup.sound_loops = FALSE;
1608 if (!audio.music_available)
1609 setup.sound_music = FALSE;
1611 if (!video.fullscreen_available)
1612 setup.fullscreen = FALSE;
1614 setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
1616 SetAudioMode(setup.sound);
1620 int GetElementFromGroupElement(int element)
1622 if (IS_GROUP_ELEMENT(element))
1624 struct ElementGroupInfo *group = element_info[element].group;
1625 int last_anim_random_frame = gfx.anim_random_frame;
1628 if (group->choice_mode == ANIM_RANDOM)
1629 gfx.anim_random_frame = RND(group->num_elements_resolved);
1631 element_pos = getAnimationFrame(group->num_elements_resolved, 1,
1632 group->choice_mode, 0,
1635 if (group->choice_mode == ANIM_RANDOM)
1636 gfx.anim_random_frame = last_anim_random_frame;
1638 group->choice_pos++;
1640 element = group->element_resolved[element_pos];
1646 static void InitPlayerField(int x, int y, int element, boolean init_game)
1648 if (element == EL_SP_MURPHY)
1652 if (stored_player[0].present)
1654 Feld[x][y] = EL_SP_MURPHY_CLONE;
1660 stored_player[0].initial_element = element;
1661 stored_player[0].use_murphy = TRUE;
1663 if (!level.use_artwork_element[0])
1664 stored_player[0].artwork_element = EL_SP_MURPHY;
1667 Feld[x][y] = EL_PLAYER_1;
1673 struct PlayerInfo *player = &stored_player[Feld[x][y] - EL_PLAYER_1];
1674 int jx = player->jx, jy = player->jy;
1676 player->present = TRUE;
1678 player->block_last_field = (element == EL_SP_MURPHY ?
1679 level.sp_block_last_field :
1680 level.block_last_field);
1682 /* ---------- initialize player's last field block delay --------------- */
1684 /* always start with reliable default value (no adjustment needed) */
1685 player->block_delay_adjustment = 0;
1687 /* special case 1: in Supaplex, Murphy blocks last field one more frame */
1688 if (player->block_last_field && element == EL_SP_MURPHY)
1689 player->block_delay_adjustment = 1;
1691 /* special case 2: in game engines before 3.1.1, blocking was different */
1692 if (game.use_block_last_field_bug)
1693 player->block_delay_adjustment = (player->block_last_field ? -1 : 1);
1695 if (!options.network || player->connected)
1697 player->active = TRUE;
1699 /* remove potentially duplicate players */
1700 if (StorePlayer[jx][jy] == Feld[x][y])
1701 StorePlayer[jx][jy] = 0;
1703 StorePlayer[x][y] = Feld[x][y];
1705 #if DEBUG_INIT_PLAYER
1708 printf("- player element %d activated", player->element_nr);
1709 printf(" (local player is %d and currently %s)\n",
1710 local_player->element_nr,
1711 local_player->active ? "active" : "not active");
1716 Feld[x][y] = EL_EMPTY;
1718 player->jx = player->last_jx = x;
1719 player->jy = player->last_jy = y;
1724 int player_nr = GET_PLAYER_NR(element);
1725 struct PlayerInfo *player = &stored_player[player_nr];
1727 if (player->active && player->killed)
1728 player->reanimated = TRUE; /* if player was just killed, reanimate him */
1732 static void InitField(int x, int y, boolean init_game)
1734 int element = Feld[x][y];
1743 InitPlayerField(x, y, element, init_game);
1746 case EL_SOKOBAN_FIELD_PLAYER:
1747 element = Feld[x][y] = EL_PLAYER_1;
1748 InitField(x, y, init_game);
1750 element = Feld[x][y] = EL_SOKOBAN_FIELD_EMPTY;
1751 InitField(x, y, init_game);
1754 case EL_SOKOBAN_FIELD_EMPTY:
1755 local_player->sokobanfields_still_needed++;
1759 if (x < lev_fieldx-1 && Feld[x+1][y] == EL_ACID)
1760 Feld[x][y] = EL_ACID_POOL_TOPLEFT;
1761 else if (x > 0 && Feld[x-1][y] == EL_ACID)
1762 Feld[x][y] = EL_ACID_POOL_TOPRIGHT;
1763 else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPLEFT)
1764 Feld[x][y] = EL_ACID_POOL_BOTTOMLEFT;
1765 else if (y > 0 && Feld[x][y-1] == EL_ACID)
1766 Feld[x][y] = EL_ACID_POOL_BOTTOM;
1767 else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPRIGHT)
1768 Feld[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
1777 case EL_SPACESHIP_RIGHT:
1778 case EL_SPACESHIP_UP:
1779 case EL_SPACESHIP_LEFT:
1780 case EL_SPACESHIP_DOWN:
1781 case EL_BD_BUTTERFLY:
1782 case EL_BD_BUTTERFLY_RIGHT:
1783 case EL_BD_BUTTERFLY_UP:
1784 case EL_BD_BUTTERFLY_LEFT:
1785 case EL_BD_BUTTERFLY_DOWN:
1787 case EL_BD_FIREFLY_RIGHT:
1788 case EL_BD_FIREFLY_UP:
1789 case EL_BD_FIREFLY_LEFT:
1790 case EL_BD_FIREFLY_DOWN:
1791 case EL_PACMAN_RIGHT:
1793 case EL_PACMAN_LEFT:
1794 case EL_PACMAN_DOWN:
1796 case EL_YAMYAM_LEFT:
1797 case EL_YAMYAM_RIGHT:
1799 case EL_YAMYAM_DOWN:
1800 case EL_DARK_YAMYAM:
1803 case EL_SP_SNIKSNAK:
1804 case EL_SP_ELECTRON:
1813 case EL_AMOEBA_FULL:
1818 case EL_AMOEBA_DROP:
1819 if (y == lev_fieldy - 1)
1821 Feld[x][y] = EL_AMOEBA_GROWING;
1822 Store[x][y] = EL_AMOEBA_WET;
1826 case EL_DYNAMITE_ACTIVE:
1827 case EL_SP_DISK_RED_ACTIVE:
1828 case EL_DYNABOMB_PLAYER_1_ACTIVE:
1829 case EL_DYNABOMB_PLAYER_2_ACTIVE:
1830 case EL_DYNABOMB_PLAYER_3_ACTIVE:
1831 case EL_DYNABOMB_PLAYER_4_ACTIVE:
1832 MovDelay[x][y] = 96;
1835 case EL_EM_DYNAMITE_ACTIVE:
1836 MovDelay[x][y] = 32;
1840 local_player->lights_still_needed++;
1844 local_player->friends_still_needed++;
1849 GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
1852 case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
1853 case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
1854 case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
1855 case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
1856 case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
1857 case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
1858 case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
1859 case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
1860 case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
1861 case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
1862 case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
1863 case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
1866 int belt_nr = getBeltNrFromBeltSwitchElement(Feld[x][y]);
1867 int belt_dir = getBeltDirFromBeltSwitchElement(Feld[x][y]);
1868 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Feld[x][y]);
1870 if (game.belt_dir_nr[belt_nr] == 3) /* initial value */
1872 game.belt_dir[belt_nr] = belt_dir;
1873 game.belt_dir_nr[belt_nr] = belt_dir_nr;
1875 else /* more than one switch -- set it like the first switch */
1877 Feld[x][y] = Feld[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
1882 case EL_LIGHT_SWITCH_ACTIVE:
1884 game.light_time_left = level.time_light * FRAMES_PER_SECOND;
1887 case EL_INVISIBLE_STEELWALL:
1888 case EL_INVISIBLE_WALL:
1889 case EL_INVISIBLE_SAND:
1890 if (game.light_time_left > 0 ||
1891 game.lenses_time_left > 0)
1892 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
1895 case EL_EMC_MAGIC_BALL:
1896 if (game.ball_state)
1897 Feld[x][y] = EL_EMC_MAGIC_BALL_ACTIVE;
1900 case EL_EMC_MAGIC_BALL_SWITCH:
1901 if (game.ball_state)
1902 Feld[x][y] = EL_EMC_MAGIC_BALL_SWITCH_ACTIVE;
1905 case EL_TRIGGER_PLAYER:
1906 case EL_TRIGGER_ELEMENT:
1907 case EL_TRIGGER_CE_VALUE:
1908 case EL_TRIGGER_CE_SCORE:
1910 case EL_ANY_ELEMENT:
1911 case EL_CURRENT_CE_VALUE:
1912 case EL_CURRENT_CE_SCORE:
1929 /* reference elements should not be used on the playfield */
1930 Feld[x][y] = EL_EMPTY;
1934 if (IS_CUSTOM_ELEMENT(element))
1936 if (CAN_MOVE(element))
1939 if (!element_info[element].use_last_ce_value || init_game)
1940 CustomValue[x][y] = GET_NEW_CE_VALUE(Feld[x][y]);
1942 else if (IS_GROUP_ELEMENT(element))
1944 Feld[x][y] = GetElementFromGroupElement(element);
1946 InitField(x, y, init_game);
1953 CheckTriggeredElementChange(x, y, element, CE_CREATION_OF_X);
1956 inline static void InitField_WithBug1(int x, int y, boolean init_game)
1958 InitField(x, y, init_game);
1960 /* not needed to call InitMovDir() -- already done by InitField()! */
1961 if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1962 CAN_MOVE(Feld[x][y]))
1966 inline static void InitField_WithBug2(int x, int y, boolean init_game)
1968 int old_element = Feld[x][y];
1970 InitField(x, y, init_game);
1972 /* not needed to call InitMovDir() -- already done by InitField()! */
1973 if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1974 CAN_MOVE(old_element) &&
1975 (old_element < EL_MOLE_LEFT || old_element > EL_MOLE_DOWN))
1978 /* this case is in fact a combination of not less than three bugs:
1979 first, it calls InitMovDir() for elements that can move, although this is
1980 already done by InitField(); then, it checks the element that was at this
1981 field _before_ the call to InitField() (which can change it); lastly, it
1982 was not called for "mole with direction" elements, which were treated as
1983 "cannot move" due to (fixed) wrong element initialization in "src/init.c"
1987 static int get_key_element_from_nr(int key_nr)
1989 int key_base_element = (key_nr >= STD_NUM_KEYS ? EL_EMC_KEY_5 - STD_NUM_KEYS :
1990 level.game_engine_type == GAME_ENGINE_TYPE_EM ?
1991 EL_EM_KEY_1 : EL_KEY_1);
1993 return key_base_element + key_nr;
1996 static int get_next_dropped_element(struct PlayerInfo *player)
1998 return (player->inventory_size > 0 ?
1999 player->inventory_element[player->inventory_size - 1] :
2000 player->inventory_infinite_element != EL_UNDEFINED ?
2001 player->inventory_infinite_element :
2002 player->dynabombs_left > 0 ?
2003 EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
2007 static int get_inventory_element_from_pos(struct PlayerInfo *player, int pos)
2009 /* pos >= 0: get element from bottom of the stack;
2010 pos < 0: get element from top of the stack */
2014 int min_inventory_size = -pos;
2015 int inventory_pos = player->inventory_size - min_inventory_size;
2016 int min_dynabombs_left = min_inventory_size - player->inventory_size;
2018 return (player->inventory_size >= min_inventory_size ?
2019 player->inventory_element[inventory_pos] :
2020 player->inventory_infinite_element != EL_UNDEFINED ?
2021 player->inventory_infinite_element :
2022 player->dynabombs_left >= min_dynabombs_left ?
2023 EL_DYNABOMB_PLAYER_1 + player->index_nr :
2028 int min_dynabombs_left = pos + 1;
2029 int min_inventory_size = pos + 1 - player->dynabombs_left;
2030 int inventory_pos = pos - player->dynabombs_left;
2032 return (player->inventory_infinite_element != EL_UNDEFINED ?
2033 player->inventory_infinite_element :
2034 player->dynabombs_left >= min_dynabombs_left ?
2035 EL_DYNABOMB_PLAYER_1 + player->index_nr :
2036 player->inventory_size >= min_inventory_size ?
2037 player->inventory_element[inventory_pos] :
2042 static int compareGamePanelOrderInfo(const void *object1, const void *object2)
2044 const struct GamePanelOrderInfo *gpo1 = (struct GamePanelOrderInfo *)object1;
2045 const struct GamePanelOrderInfo *gpo2 = (struct GamePanelOrderInfo *)object2;
2048 if (gpo1->sort_priority != gpo2->sort_priority)
2049 compare_result = gpo1->sort_priority - gpo2->sort_priority;
2051 compare_result = gpo1->nr - gpo2->nr;
2053 return compare_result;
2056 void InitGameControlValues()
2060 for (i = 0; game_panel_controls[i].nr != -1; i++)
2062 struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2063 struct GamePanelOrderInfo *gpo = &game_panel_order[i];
2064 struct TextPosInfo *pos = gpc->pos;
2066 int type = gpc->type;
2070 Error(ERR_INFO, "'game_panel_controls' structure corrupted at %d", i);
2071 Error(ERR_EXIT, "this should not happen -- please debug");
2074 /* force update of game controls after initialization */
2075 gpc->value = gpc->last_value = -1;
2076 gpc->frame = gpc->last_frame = -1;
2077 gpc->gfx_frame = -1;
2079 /* determine panel value width for later calculation of alignment */
2080 if (type == TYPE_INTEGER || type == TYPE_STRING)
2082 pos->width = pos->size * getFontWidth(pos->font);
2083 pos->height = getFontHeight(pos->font);
2085 else if (type == TYPE_ELEMENT)
2087 pos->width = pos->size;
2088 pos->height = pos->size;
2091 /* fill structure for game panel draw order */
2093 gpo->sort_priority = pos->sort_priority;
2096 /* sort game panel controls according to sort_priority and control number */
2097 qsort(game_panel_order, NUM_GAME_PANEL_CONTROLS,
2098 sizeof(struct GamePanelOrderInfo), compareGamePanelOrderInfo);
2101 void UpdatePlayfieldElementCount()
2103 boolean use_element_count = FALSE;
2106 /* first check if it is needed at all to calculate playfield element count */
2107 for (i = GAME_PANEL_ELEMENT_COUNT_1; i <= GAME_PANEL_ELEMENT_COUNT_8; i++)
2108 if (!PANEL_DEACTIVATED(game_panel_controls[i].pos))
2109 use_element_count = TRUE;
2111 if (!use_element_count)
2114 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2115 element_info[i].element_count = 0;
2117 SCAN_PLAYFIELD(x, y)
2119 element_info[Feld[x][y]].element_count++;
2122 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
2123 for (j = 0; j < MAX_NUM_ELEMENTS; j++)
2124 if (IS_IN_GROUP(j, i))
2125 element_info[EL_GROUP_START + i].element_count +=
2126 element_info[j].element_count;
2129 void UpdateGameControlValues()
2132 int time = (local_player->LevelSolved ?
2133 local_player->LevelSolved_CountingTime :
2134 level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2135 level.native_em_level->lev->time :
2136 level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2137 level.native_sp_level->game_sp->time_played :
2138 game.no_time_limit ? TimePlayed : TimeLeft);
2139 int score = (local_player->LevelSolved ?
2140 local_player->LevelSolved_CountingScore :
2141 level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2142 level.native_em_level->lev->score :
2143 level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2144 level.native_sp_level->game_sp->score :
2145 local_player->score);
2146 int gems = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2147 level.native_em_level->lev->required :
2148 level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2149 level.native_sp_level->game_sp->infotrons_still_needed :
2150 local_player->gems_still_needed);
2151 int exit_closed = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2152 level.native_em_level->lev->required > 0 :
2153 level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2154 level.native_sp_level->game_sp->infotrons_still_needed > 0 :
2155 local_player->gems_still_needed > 0 ||
2156 local_player->sokobanfields_still_needed > 0 ||
2157 local_player->lights_still_needed > 0);
2159 UpdatePlayfieldElementCount();
2161 /* update game panel control values */
2163 game_panel_controls[GAME_PANEL_LEVEL_NUMBER].value = level_nr;
2164 game_panel_controls[GAME_PANEL_GEMS].value = gems;
2166 game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value = 0;
2167 for (i = 0; i < MAX_NUM_KEYS; i++)
2168 game_panel_controls[GAME_PANEL_KEY_1 + i].value = EL_EMPTY;
2169 game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_EMPTY;
2170 game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value = 0;
2172 if (game.centered_player_nr == -1)
2174 for (i = 0; i < MAX_PLAYERS; i++)
2176 /* only one player in Supaplex game engine */
2177 if (level.game_engine_type == GAME_ENGINE_TYPE_SP && i > 0)
2180 for (k = 0; k < MAX_NUM_KEYS; k++)
2182 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2184 if (level.native_em_level->ply[i]->keys & (1 << k))
2185 game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2186 get_key_element_from_nr(k);
2188 else if (stored_player[i].key[k])
2189 game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2190 get_key_element_from_nr(k);
2193 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2194 game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2195 level.native_em_level->ply[i]->dynamite;
2196 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
2197 game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2198 level.native_sp_level->game_sp->red_disk_count;
2200 game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2201 stored_player[i].inventory_size;
2203 if (stored_player[i].num_white_keys > 0)
2204 game_panel_controls[GAME_PANEL_KEY_WHITE].value =
2207 game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2208 stored_player[i].num_white_keys;
2213 int player_nr = game.centered_player_nr;
2215 for (k = 0; k < MAX_NUM_KEYS; k++)
2217 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2219 if (level.native_em_level->ply[player_nr]->keys & (1 << k))
2220 game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2221 get_key_element_from_nr(k);
2223 else if (stored_player[player_nr].key[k])
2224 game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2225 get_key_element_from_nr(k);
2228 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2229 game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2230 level.native_em_level->ply[player_nr]->dynamite;
2231 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
2232 game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2233 level.native_sp_level->game_sp->red_disk_count;
2235 game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2236 stored_player[player_nr].inventory_size;
2238 if (stored_player[player_nr].num_white_keys > 0)
2239 game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_DC_KEY_WHITE;
2241 game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2242 stored_player[player_nr].num_white_keys;
2245 for (i = 0; i < NUM_PANEL_INVENTORY; i++)
2247 game_panel_controls[GAME_PANEL_INVENTORY_FIRST_1 + i].value =
2248 get_inventory_element_from_pos(local_player, i);
2249 game_panel_controls[GAME_PANEL_INVENTORY_LAST_1 + i].value =
2250 get_inventory_element_from_pos(local_player, -i - 1);
2253 game_panel_controls[GAME_PANEL_SCORE].value = score;
2254 game_panel_controls[GAME_PANEL_HIGHSCORE].value = highscore[0].Score;
2256 game_panel_controls[GAME_PANEL_TIME].value = time;
2258 game_panel_controls[GAME_PANEL_TIME_HH].value = time / 3600;
2259 game_panel_controls[GAME_PANEL_TIME_MM].value = (time / 60) % 60;
2260 game_panel_controls[GAME_PANEL_TIME_SS].value = time % 60;
2262 game_panel_controls[GAME_PANEL_FRAME].value = FrameCounter;
2264 game_panel_controls[GAME_PANEL_SHIELD_NORMAL].value =
2265 (local_player->shield_normal_time_left > 0 ? EL_SHIELD_NORMAL_ACTIVE :
2267 game_panel_controls[GAME_PANEL_SHIELD_NORMAL_TIME].value =
2268 local_player->shield_normal_time_left;
2269 game_panel_controls[GAME_PANEL_SHIELD_DEADLY].value =
2270 (local_player->shield_deadly_time_left > 0 ? EL_SHIELD_DEADLY_ACTIVE :
2272 game_panel_controls[GAME_PANEL_SHIELD_DEADLY_TIME].value =
2273 local_player->shield_deadly_time_left;
2275 game_panel_controls[GAME_PANEL_EXIT].value =
2276 (exit_closed ? EL_EXIT_CLOSED : EL_EXIT_OPEN);
2278 game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL].value =
2279 (game.ball_state ? EL_EMC_MAGIC_BALL_ACTIVE : EL_EMC_MAGIC_BALL);
2280 game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL_SWITCH].value =
2281 (game.ball_state ? EL_EMC_MAGIC_BALL_SWITCH_ACTIVE :
2282 EL_EMC_MAGIC_BALL_SWITCH);
2284 game_panel_controls[GAME_PANEL_LIGHT_SWITCH].value =
2285 (game.light_time_left > 0 ? EL_LIGHT_SWITCH_ACTIVE : EL_LIGHT_SWITCH);
2286 game_panel_controls[GAME_PANEL_LIGHT_SWITCH_TIME].value =
2287 game.light_time_left;
2289 game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH].value =
2290 (game.timegate_time_left > 0 ? EL_TIMEGATE_OPEN : EL_TIMEGATE_CLOSED);
2291 game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH_TIME].value =
2292 game.timegate_time_left;
2294 game_panel_controls[GAME_PANEL_SWITCHGATE_SWITCH].value =
2295 EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
2297 game_panel_controls[GAME_PANEL_EMC_LENSES].value =
2298 (game.lenses_time_left > 0 ? EL_EMC_LENSES : EL_EMPTY);
2299 game_panel_controls[GAME_PANEL_EMC_LENSES_TIME].value =
2300 game.lenses_time_left;
2302 game_panel_controls[GAME_PANEL_EMC_MAGNIFIER].value =
2303 (game.magnify_time_left > 0 ? EL_EMC_MAGNIFIER : EL_EMPTY);
2304 game_panel_controls[GAME_PANEL_EMC_MAGNIFIER_TIME].value =
2305 game.magnify_time_left;
2307 game_panel_controls[GAME_PANEL_BALLOON_SWITCH].value =
2308 (game.wind_direction == MV_LEFT ? EL_BALLOON_SWITCH_LEFT :
2309 game.wind_direction == MV_RIGHT ? EL_BALLOON_SWITCH_RIGHT :
2310 game.wind_direction == MV_UP ? EL_BALLOON_SWITCH_UP :
2311 game.wind_direction == MV_DOWN ? EL_BALLOON_SWITCH_DOWN :
2312 EL_BALLOON_SWITCH_NONE);
2314 game_panel_controls[GAME_PANEL_DYNABOMB_NUMBER].value =
2315 local_player->dynabomb_count;
2316 game_panel_controls[GAME_PANEL_DYNABOMB_SIZE].value =
2317 local_player->dynabomb_size;
2318 game_panel_controls[GAME_PANEL_DYNABOMB_POWER].value =
2319 (local_player->dynabomb_xl ? EL_DYNABOMB_INCREASE_POWER : EL_EMPTY);
2321 game_panel_controls[GAME_PANEL_PENGUINS].value =
2322 local_player->friends_still_needed;
2324 game_panel_controls[GAME_PANEL_SOKOBAN_OBJECTS].value =
2325 local_player->sokobanfields_still_needed;
2326 game_panel_controls[GAME_PANEL_SOKOBAN_FIELDS].value =
2327 local_player->sokobanfields_still_needed;
2329 game_panel_controls[GAME_PANEL_ROBOT_WHEEL].value =
2330 (game.robot_wheel_active ? EL_ROBOT_WHEEL_ACTIVE : EL_ROBOT_WHEEL);
2332 for (i = 0; i < NUM_BELTS; i++)
2334 game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1 + i].value =
2335 (game.belt_dir[i] != MV_NONE ? EL_CONVEYOR_BELT_1_MIDDLE_ACTIVE :
2336 EL_CONVEYOR_BELT_1_MIDDLE) + i;
2337 game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1_SWITCH + i].value =
2338 getBeltSwitchElementFromBeltNrAndBeltDir(i, game.belt_dir[i]);
2341 game_panel_controls[GAME_PANEL_MAGIC_WALL].value =
2342 (game.magic_wall_active ? EL_MAGIC_WALL_ACTIVE : EL_MAGIC_WALL);
2343 game_panel_controls[GAME_PANEL_MAGIC_WALL_TIME].value =
2344 game.magic_wall_time_left;
2346 game_panel_controls[GAME_PANEL_GRAVITY_STATE].value =
2347 local_player->gravity;
2349 for (i = 0; i < NUM_PANEL_GRAPHICS; i++)
2350 game_panel_controls[GAME_PANEL_GRAPHIC_1 + i].value = EL_GRAPHIC_1 + i;
2352 for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2353 game_panel_controls[GAME_PANEL_ELEMENT_1 + i].value =
2354 (IS_DRAWABLE_ELEMENT(game.panel.element[i].id) ?
2355 game.panel.element[i].id : EL_UNDEFINED);
2357 for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2358 game_panel_controls[GAME_PANEL_ELEMENT_COUNT_1 + i].value =
2359 (IS_VALID_ELEMENT(game.panel.element_count[i].id) ?
2360 element_info[game.panel.element_count[i].id].element_count : 0);
2362 for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2363 game_panel_controls[GAME_PANEL_CE_SCORE_1 + i].value =
2364 (IS_CUSTOM_ELEMENT(game.panel.ce_score[i].id) ?
2365 element_info[game.panel.ce_score[i].id].collect_score : 0);
2367 for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2368 game_panel_controls[GAME_PANEL_CE_SCORE_1_ELEMENT + i].value =
2369 (IS_CUSTOM_ELEMENT(game.panel.ce_score_element[i].id) ?
2370 element_info[game.panel.ce_score_element[i].id].collect_score :
2373 game_panel_controls[GAME_PANEL_PLAYER_NAME].value = 0;
2374 game_panel_controls[GAME_PANEL_LEVEL_NAME].value = 0;
2375 game_panel_controls[GAME_PANEL_LEVEL_AUTHOR].value = 0;
2377 /* update game panel control frames */
2379 for (i = 0; game_panel_controls[i].nr != -1; i++)
2381 struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2383 if (gpc->type == TYPE_ELEMENT)
2385 if (gpc->value != EL_UNDEFINED && gpc->value != EL_EMPTY)
2387 int last_anim_random_frame = gfx.anim_random_frame;
2388 int element = gpc->value;
2389 int graphic = el2panelimg(element);
2391 if (gpc->value != gpc->last_value)
2394 gpc->gfx_random = INIT_GFX_RANDOM();
2400 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2401 IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2402 gpc->gfx_random = INIT_GFX_RANDOM();
2405 if (ANIM_MODE(graphic) == ANIM_RANDOM)
2406 gfx.anim_random_frame = gpc->gfx_random;
2408 if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
2409 gpc->gfx_frame = element_info[element].collect_score;
2411 gpc->frame = getGraphicAnimationFrame(el2panelimg(gpc->value),
2414 if (ANIM_MODE(graphic) == ANIM_RANDOM)
2415 gfx.anim_random_frame = last_anim_random_frame;
2421 void DisplayGameControlValues()
2423 boolean redraw_panel = FALSE;
2426 for (i = 0; game_panel_controls[i].nr != -1; i++)
2428 struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2430 if (PANEL_DEACTIVATED(gpc->pos))
2433 if (gpc->value == gpc->last_value &&
2434 gpc->frame == gpc->last_frame)
2437 redraw_panel = TRUE;
2443 /* copy default game door content to main double buffer */
2445 /* !!! CHECK AGAIN !!! */
2446 SetPanelBackground();
2447 // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
2448 DrawBackground(DX, DY, DXSIZE, DYSIZE);
2450 /* redraw game control buttons */
2451 RedrawGameButtons();
2453 game_status = GAME_MODE_PSEUDO_PANEL;
2455 for (i = 0; i < NUM_GAME_PANEL_CONTROLS; i++)
2457 int nr = game_panel_order[i].nr;
2458 struct GamePanelControlInfo *gpc = &game_panel_controls[nr];
2459 struct TextPosInfo *pos = gpc->pos;
2460 int type = gpc->type;
2461 int value = gpc->value;
2462 int frame = gpc->frame;
2463 int size = pos->size;
2464 int font = pos->font;
2465 boolean draw_masked = pos->draw_masked;
2466 int mask_mode = (draw_masked ? BLIT_MASKED : BLIT_OPAQUE);
2468 if (PANEL_DEACTIVATED(pos))
2471 gpc->last_value = value;
2472 gpc->last_frame = frame;
2474 if (type == TYPE_INTEGER)
2476 if (nr == GAME_PANEL_LEVEL_NUMBER ||
2477 nr == GAME_PANEL_TIME)
2479 boolean use_dynamic_size = (size == -1 ? TRUE : FALSE);
2481 if (use_dynamic_size) /* use dynamic number of digits */
2483 int value_change = (nr == GAME_PANEL_LEVEL_NUMBER ? 100 : 1000);
2484 int size1 = (nr == GAME_PANEL_LEVEL_NUMBER ? 2 : 3);
2485 int size2 = size1 + 1;
2486 int font1 = pos->font;
2487 int font2 = pos->font_alt;
2489 size = (value < value_change ? size1 : size2);
2490 font = (value < value_change ? font1 : font2);
2494 /* correct text size if "digits" is zero or less */
2496 size = strlen(int2str(value, size));
2498 /* dynamically correct text alignment */
2499 pos->width = size * getFontWidth(font);
2501 DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2502 int2str(value, size), font, mask_mode);
2504 else if (type == TYPE_ELEMENT)
2506 int element, graphic;
2510 int dst_x = PANEL_XPOS(pos);
2511 int dst_y = PANEL_YPOS(pos);
2513 if (value != EL_UNDEFINED && value != EL_EMPTY)
2516 graphic = el2panelimg(value);
2518 // printf("::: %d, '%s' [%d]\n", element, EL_NAME(element), size);
2520 if (element >= EL_GRAPHIC_1 && element <= EL_GRAPHIC_8 && size == 0)
2523 getSizedGraphicSource(graphic, frame, size, &src_bitmap,
2526 width = graphic_info[graphic].width * size / TILESIZE;
2527 height = graphic_info[graphic].height * size / TILESIZE;
2530 BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2533 BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2537 else if (type == TYPE_STRING)
2539 boolean active = (value != 0);
2540 char *state_normal = "off";
2541 char *state_active = "on";
2542 char *state = (active ? state_active : state_normal);
2543 char *s = (nr == GAME_PANEL_GRAVITY_STATE ? state :
2544 nr == GAME_PANEL_PLAYER_NAME ? setup.player_name :
2545 nr == GAME_PANEL_LEVEL_NAME ? level.name :
2546 nr == GAME_PANEL_LEVEL_AUTHOR ? level.author : NULL);
2548 if (nr == GAME_PANEL_GRAVITY_STATE)
2550 int font1 = pos->font; /* (used for normal state) */
2551 int font2 = pos->font_alt; /* (used for active state) */
2553 font = (active ? font2 : font1);
2562 /* don't truncate output if "chars" is zero or less */
2565 /* dynamically correct text alignment */
2566 pos->width = size * getFontWidth(font);
2569 s_cut = getStringCopyN(s, size);
2571 DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2572 s_cut, font, mask_mode);
2578 redraw_mask |= REDRAW_DOOR_1;
2581 game_status = GAME_MODE_PLAYING;
2584 void UpdateAndDisplayGameControlValues()
2586 if (tape.deactivate_display)
2589 UpdateGameControlValues();
2590 DisplayGameControlValues();
2593 void UpdateGameDoorValues()
2595 UpdateGameControlValues();
2598 void DrawGameDoorValues()
2600 DisplayGameControlValues();
2605 =============================================================================
2607 -----------------------------------------------------------------------------
2608 initialize game engine due to level / tape version number
2609 =============================================================================
2612 static void InitGameEngine()
2614 int i, j, k, l, x, y;
2616 /* set game engine from tape file when re-playing, else from level file */
2617 game.engine_version = (tape.playing ? tape.engine_version :
2618 level.game_version);
2620 /* set single or multi-player game mode (needed for re-playing tapes) */
2621 game.team_mode = setup.team_mode;
2625 int num_players = 0;
2627 for (i = 0; i < MAX_PLAYERS; i++)
2628 if (tape.player_participates[i])
2631 /* multi-player tapes contain input data for more than one player */
2632 game.team_mode = (num_players > 1);
2635 /* ---------------------------------------------------------------------- */
2636 /* set flags for bugs and changes according to active game engine version */
2637 /* ---------------------------------------------------------------------- */
2640 Summary of bugfix/change:
2641 Fixed handling for custom elements that change when pushed by the player.
2643 Fixed/changed in version:
2647 Before 3.1.0, custom elements that "change when pushing" changed directly
2648 after the player started pushing them (until then handled in "DigField()").
2649 Since 3.1.0, these custom elements are not changed until the "pushing"
2650 move of the element is finished (now handled in "ContinueMoving()").
2652 Affected levels/tapes:
2653 The first condition is generally needed for all levels/tapes before version
2654 3.1.0, which might use the old behaviour before it was changed; known tapes
2655 that are affected are some tapes from the level set "Walpurgis Gardens" by
2657 The second condition is an exception from the above case and is needed for
2658 the special case of tapes recorded with game (not engine!) version 3.1.0 or
2659 above (including some development versions of 3.1.0), but before it was
2660 known that this change would break tapes like the above and was fixed in
2661 3.1.1, so that the changed behaviour was active although the engine version
2662 while recording maybe was before 3.1.0. There is at least one tape that is
2663 affected by this exception, which is the tape for the one-level set "Bug
2664 Machine" by Juergen Bonhagen.
2667 game.use_change_when_pushing_bug =
2668 (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2670 tape.game_version >= VERSION_IDENT(3,1,0,0) &&
2671 tape.game_version < VERSION_IDENT(3,1,1,0)));
2674 Summary of bugfix/change:
2675 Fixed handling for blocking the field the player leaves when moving.
2677 Fixed/changed in version:
2681 Before 3.1.1, when "block last field when moving" was enabled, the field
2682 the player is leaving when moving was blocked for the time of the move,
2683 and was directly unblocked afterwards. This resulted in the last field
2684 being blocked for exactly one less than the number of frames of one player
2685 move. Additionally, even when blocking was disabled, the last field was
2686 blocked for exactly one frame.
2687 Since 3.1.1, due to changes in player movement handling, the last field
2688 is not blocked at all when blocking is disabled. When blocking is enabled,
2689 the last field is blocked for exactly the number of frames of one player
2690 move. Additionally, if the player is Murphy, the hero of Supaplex, the
2691 last field is blocked for exactly one more than the number of frames of
2694 Affected levels/tapes:
2695 (!!! yet to be determined -- probably many !!!)
2698 game.use_block_last_field_bug =
2699 (game.engine_version < VERSION_IDENT(3,1,1,0));
2701 /* ---------------------------------------------------------------------- */
2703 /* set maximal allowed number of custom element changes per game frame */
2704 game.max_num_changes_per_frame = 1;
2706 /* default scan direction: scan playfield from top/left to bottom/right */
2707 InitPlayfieldScanMode(CA_ARG_SCAN_MODE_NORMAL);
2709 /* dynamically adjust element properties according to game engine version */
2710 InitElementPropertiesEngine(game.engine_version);
2713 printf("level %d: level version == %06d\n", level_nr, level.game_version);
2714 printf(" tape version == %06d [%s] [file: %06d]\n",
2715 tape.engine_version, (tape.playing ? "PLAYING" : "RECORDING"),
2717 printf(" => game.engine_version == %06d\n", game.engine_version);
2720 /* ---------- initialize player's initial move delay --------------------- */
2722 /* dynamically adjust player properties according to level information */
2723 for (i = 0; i < MAX_PLAYERS; i++)
2724 game.initial_move_delay_value[i] =
2725 get_move_delay_from_stepsize(level.initial_player_stepsize[i]);
2727 /* dynamically adjust player properties according to game engine version */
2728 for (i = 0; i < MAX_PLAYERS; i++)
2729 game.initial_move_delay[i] =
2730 (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
2731 game.initial_move_delay_value[i] : 0);
2733 /* ---------- initialize player's initial push delay --------------------- */
2735 /* dynamically adjust player properties according to game engine version */
2736 game.initial_push_delay_value =
2737 (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
2739 /* ---------- initialize changing elements ------------------------------- */
2741 /* initialize changing elements information */
2742 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2744 struct ElementInfo *ei = &element_info[i];
2746 /* this pointer might have been changed in the level editor */
2747 ei->change = &ei->change_page[0];
2749 if (!IS_CUSTOM_ELEMENT(i))
2751 ei->change->target_element = EL_EMPTY_SPACE;
2752 ei->change->delay_fixed = 0;
2753 ei->change->delay_random = 0;
2754 ei->change->delay_frames = 1;
2757 for (j = 0; j < NUM_CHANGE_EVENTS; j++)
2759 ei->has_change_event[j] = FALSE;
2761 ei->event_page_nr[j] = 0;
2762 ei->event_page[j] = &ei->change_page[0];
2766 /* add changing elements from pre-defined list */
2767 for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
2769 struct ChangingElementInfo *ch_delay = &change_delay_list[i];
2770 struct ElementInfo *ei = &element_info[ch_delay->element];
2772 ei->change->target_element = ch_delay->target_element;
2773 ei->change->delay_fixed = ch_delay->change_delay;
2775 ei->change->pre_change_function = ch_delay->pre_change_function;
2776 ei->change->change_function = ch_delay->change_function;
2777 ei->change->post_change_function = ch_delay->post_change_function;
2779 ei->change->can_change = TRUE;
2780 ei->change->can_change_or_has_action = TRUE;
2782 ei->has_change_event[CE_DELAY] = TRUE;
2784 SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
2785 SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE_OR_HAS_ACTION, TRUE);
2788 /* ---------- initialize internal run-time variables --------------------- */
2790 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2792 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
2794 for (j = 0; j < ei->num_change_pages; j++)
2796 ei->change_page[j].can_change_or_has_action =
2797 (ei->change_page[j].can_change |
2798 ei->change_page[j].has_action);
2802 /* add change events from custom element configuration */
2803 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2805 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
2807 for (j = 0; j < ei->num_change_pages; j++)
2809 if (!ei->change_page[j].can_change_or_has_action)
2812 for (k = 0; k < NUM_CHANGE_EVENTS; k++)
2814 /* only add event page for the first page found with this event */
2815 if (ei->change_page[j].has_event[k] && !(ei->has_change_event[k]))
2817 ei->has_change_event[k] = TRUE;
2819 ei->event_page_nr[k] = j;
2820 ei->event_page[k] = &ei->change_page[j];
2826 /* ---------- initialize reference elements in change conditions --------- */
2828 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2830 int element = EL_CUSTOM_START + i;
2831 struct ElementInfo *ei = &element_info[element];
2833 for (j = 0; j < ei->num_change_pages; j++)
2835 int trigger_element = ei->change_page[j].initial_trigger_element;
2837 if (trigger_element >= EL_PREV_CE_8 &&
2838 trigger_element <= EL_NEXT_CE_8)
2839 trigger_element = RESOLVED_REFERENCE_ELEMENT(element, trigger_element);
2841 ei->change_page[j].trigger_element = trigger_element;
2845 /* ---------- initialize run-time trigger player and element ------------- */
2847 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2849 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
2851 for (j = 0; j < ei->num_change_pages; j++)
2853 ei->change_page[j].actual_trigger_element = EL_EMPTY;
2854 ei->change_page[j].actual_trigger_player = EL_EMPTY;
2855 ei->change_page[j].actual_trigger_player_bits = CH_PLAYER_NONE;
2856 ei->change_page[j].actual_trigger_side = CH_SIDE_NONE;
2857 ei->change_page[j].actual_trigger_ce_value = 0;
2858 ei->change_page[j].actual_trigger_ce_score = 0;
2862 /* ---------- initialize trigger events ---------------------------------- */
2864 /* initialize trigger events information */
2865 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2866 for (j = 0; j < NUM_CHANGE_EVENTS; j++)
2867 trigger_events[i][j] = FALSE;
2869 /* add trigger events from element change event properties */
2870 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2872 struct ElementInfo *ei = &element_info[i];
2874 for (j = 0; j < ei->num_change_pages; j++)
2876 if (!ei->change_page[j].can_change_or_has_action)
2879 if (ei->change_page[j].has_event[CE_BY_OTHER_ACTION])
2881 int trigger_element = ei->change_page[j].trigger_element;
2883 for (k = 0; k < NUM_CHANGE_EVENTS; k++)
2885 if (ei->change_page[j].has_event[k])
2887 if (IS_GROUP_ELEMENT(trigger_element))
2889 struct ElementGroupInfo *group =
2890 element_info[trigger_element].group;
2892 for (l = 0; l < group->num_elements_resolved; l++)
2893 trigger_events[group->element_resolved[l]][k] = TRUE;
2895 else if (trigger_element == EL_ANY_ELEMENT)
2896 for (l = 0; l < MAX_NUM_ELEMENTS; l++)
2897 trigger_events[l][k] = TRUE;
2899 trigger_events[trigger_element][k] = TRUE;
2906 /* ---------- initialize push delay -------------------------------------- */
2908 /* initialize push delay values to default */
2909 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2911 if (!IS_CUSTOM_ELEMENT(i))
2913 /* set default push delay values (corrected since version 3.0.7-1) */
2914 if (game.engine_version < VERSION_IDENT(3,0,7,1))
2916 element_info[i].push_delay_fixed = 2;
2917 element_info[i].push_delay_random = 8;
2921 element_info[i].push_delay_fixed = 8;
2922 element_info[i].push_delay_random = 8;
2927 /* set push delay value for certain elements from pre-defined list */
2928 for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
2930 int e = push_delay_list[i].element;
2932 element_info[e].push_delay_fixed = push_delay_list[i].push_delay_fixed;
2933 element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
2936 /* set push delay value for Supaplex elements for newer engine versions */
2937 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
2939 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2941 if (IS_SP_ELEMENT(i))
2943 /* set SP push delay to just enough to push under a falling zonk */
2944 int delay = (game.engine_version >= VERSION_IDENT(3,1,1,0) ? 8 : 6);
2946 element_info[i].push_delay_fixed = delay;
2947 element_info[i].push_delay_random = 0;
2952 /* ---------- initialize move stepsize ----------------------------------- */
2954 /* initialize move stepsize values to default */
2955 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2956 if (!IS_CUSTOM_ELEMENT(i))
2957 element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
2959 /* set move stepsize value for certain elements from pre-defined list */
2960 for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
2962 int e = move_stepsize_list[i].element;
2964 element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
2967 /* ---------- initialize collect score ----------------------------------- */
2969 /* initialize collect score values for custom elements from initial value */
2970 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2971 if (IS_CUSTOM_ELEMENT(i))
2972 element_info[i].collect_score = element_info[i].collect_score_initial;
2974 /* ---------- initialize collect count ----------------------------------- */
2976 /* initialize collect count values for non-custom elements */
2977 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2978 if (!IS_CUSTOM_ELEMENT(i))
2979 element_info[i].collect_count_initial = 0;
2981 /* add collect count values for all elements from pre-defined list */
2982 for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
2983 element_info[collect_count_list[i].element].collect_count_initial =
2984 collect_count_list[i].count;
2986 /* ---------- initialize access direction -------------------------------- */
2988 /* initialize access direction values to default (access from every side) */
2989 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2990 if (!IS_CUSTOM_ELEMENT(i))
2991 element_info[i].access_direction = MV_ALL_DIRECTIONS;
2993 /* set access direction value for certain elements from pre-defined list */
2994 for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
2995 element_info[access_direction_list[i].element].access_direction =
2996 access_direction_list[i].direction;
2998 /* ---------- initialize explosion content ------------------------------- */
2999 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3001 if (IS_CUSTOM_ELEMENT(i))
3004 for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
3006 /* (content for EL_YAMYAM set at run-time with game.yamyam_content_nr) */
3008 element_info[i].content.e[x][y] =
3009 (i == EL_PLAYER_1 ? EL_EMERALD_YELLOW :
3010 i == EL_PLAYER_2 ? EL_EMERALD_RED :
3011 i == EL_PLAYER_3 ? EL_EMERALD :
3012 i == EL_PLAYER_4 ? EL_EMERALD_PURPLE :
3013 i == EL_MOLE ? EL_EMERALD_RED :
3014 i == EL_PENGUIN ? EL_EMERALD_PURPLE :
3015 i == EL_BUG ? (x == 1 && y == 1 ? EL_DIAMOND : EL_EMERALD) :
3016 i == EL_BD_BUTTERFLY ? EL_BD_DIAMOND :
3017 i == EL_SP_ELECTRON ? EL_SP_INFOTRON :
3018 i == EL_AMOEBA_TO_DIAMOND ? level.amoeba_content :
3019 i == EL_WALL_EMERALD ? EL_EMERALD :
3020 i == EL_WALL_DIAMOND ? EL_DIAMOND :
3021 i == EL_WALL_BD_DIAMOND ? EL_BD_DIAMOND :
3022 i == EL_WALL_EMERALD_YELLOW ? EL_EMERALD_YELLOW :
3023 i == EL_WALL_EMERALD_RED ? EL_EMERALD_RED :
3024 i == EL_WALL_EMERALD_PURPLE ? EL_EMERALD_PURPLE :
3025 i == EL_WALL_PEARL ? EL_PEARL :
3026 i == EL_WALL_CRYSTAL ? EL_CRYSTAL :
3031 /* ---------- initialize recursion detection ------------------------------ */
3032 recursion_loop_depth = 0;
3033 recursion_loop_detected = FALSE;
3034 recursion_loop_element = EL_UNDEFINED;
3036 /* ---------- initialize graphics engine ---------------------------------- */
3037 game.scroll_delay_value =
3038 (game.forced_scroll_delay_value != -1 ? game.forced_scroll_delay_value :
3039 setup.scroll_delay ? setup.scroll_delay_value : 0);
3040 game.scroll_delay_value =
3041 MIN(MAX(MIN_SCROLL_DELAY, game.scroll_delay_value), MAX_SCROLL_DELAY);
3043 /* ---------- initialize game engine snapshots ---------------------------- */
3044 for (i = 0; i < MAX_PLAYERS; i++)
3045 game.snapshot.last_action[i] = 0;
3046 game.snapshot.changed_action = FALSE;
3047 game.snapshot.mode =
3048 (strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_STEP) ?
3049 SNAPSHOT_MODE_EVERY_STEP :
3050 strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_MOVE) ?
3051 SNAPSHOT_MODE_EVERY_MOVE : SNAPSHOT_MODE_OFF);
3054 int get_num_special_action(int element, int action_first, int action_last)
3056 int num_special_action = 0;
3059 for (i = action_first; i <= action_last; i++)
3061 boolean found = FALSE;
3063 for (j = 0; j < NUM_DIRECTIONS; j++)
3064 if (el_act_dir2img(element, i, j) !=
3065 el_act_dir2img(element, ACTION_DEFAULT, j))
3069 num_special_action++;
3074 return num_special_action;
3079 =============================================================================
3081 -----------------------------------------------------------------------------
3082 initialize and start new game
3083 =============================================================================
3088 int full_lev_fieldx = lev_fieldx + (BorderElement != EL_EMPTY ? 2 : 0);
3089 int full_lev_fieldy = lev_fieldy + (BorderElement != EL_EMPTY ? 2 : 0);
3090 int fade_mask = REDRAW_FIELD;
3092 boolean emulate_bd = TRUE; /* unless non-BOULDERDASH elements found */
3093 boolean emulate_sb = TRUE; /* unless non-SOKOBAN elements found */
3094 boolean emulate_sp = TRUE; /* unless non-SUPAPLEX elements found */
3095 int initial_move_dir = MV_DOWN;
3099 printf("::: game.graphics_engine_version == %d\n",
3100 game.graphics_engine_version);
3103 // required here to update video display before fading (FIX THIS)
3104 DrawMaskedBorder(REDRAW_DOOR_2);
3106 game_status = GAME_MODE_PLAYING;
3108 if (!game.restart_level)
3109 CloseDoor(DOOR_CLOSE_1);
3111 /* needed if different viewport properties defined for playing */
3112 ChangeViewportPropertiesIfNeeded();
3114 if (level_editor_test_game)
3115 FadeSkipNextFadeIn();
3117 FadeSetEnterScreen();
3119 if (CheckIfGlobalBorderHasChanged())
3120 fade_mask = REDRAW_ALL;
3124 OpenDoor(GetDoorState() | DOOR_NO_DELAY | DOOR_FORCE_REDRAW);
3128 DrawCompleteVideoDisplay();
3131 InitGameControlValues();
3133 /* don't play tapes over network */
3134 network_playing = (options.network && !tape.playing);
3136 for (i = 0; i < MAX_PLAYERS; i++)
3138 struct PlayerInfo *player = &stored_player[i];
3140 player->index_nr = i;
3141 player->index_bit = (1 << i);
3142 player->element_nr = EL_PLAYER_1 + i;
3144 player->present = FALSE;
3145 player->active = FALSE;
3146 player->mapped = FALSE;
3148 player->killed = FALSE;
3149 player->reanimated = FALSE;
3152 player->effective_action = 0;
3153 player->programmed_action = 0;
3156 player->score_final = 0;
3158 player->gems_still_needed = level.gems_needed;
3159 player->sokobanfields_still_needed = 0;
3160 player->lights_still_needed = 0;
3161 player->friends_still_needed = 0;
3163 for (j = 0; j < MAX_NUM_KEYS; j++)
3164 player->key[j] = FALSE;
3166 player->num_white_keys = 0;
3168 player->dynabomb_count = 0;
3169 player->dynabomb_size = 1;
3170 player->dynabombs_left = 0;
3171 player->dynabomb_xl = FALSE;
3173 player->MovDir = initial_move_dir;
3176 player->GfxDir = initial_move_dir;
3177 player->GfxAction = ACTION_DEFAULT;
3179 player->StepFrame = 0;
3181 player->initial_element = player->element_nr;
3182 player->artwork_element =
3183 (level.use_artwork_element[i] ? level.artwork_element[i] :
3184 player->element_nr);
3185 player->use_murphy = FALSE;
3187 player->block_last_field = FALSE; /* initialized in InitPlayerField() */
3188 player->block_delay_adjustment = 0; /* initialized in InitPlayerField() */
3190 player->gravity = level.initial_player_gravity[i];
3192 player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
3194 player->actual_frame_counter = 0;
3196 player->step_counter = 0;
3198 player->last_move_dir = initial_move_dir;
3200 player->is_active = FALSE;
3202 player->is_waiting = FALSE;
3203 player->is_moving = FALSE;
3204 player->is_auto_moving = FALSE;
3205 player->is_digging = FALSE;
3206 player->is_snapping = FALSE;
3207 player->is_collecting = FALSE;
3208 player->is_pushing = FALSE;
3209 player->is_switching = FALSE;
3210 player->is_dropping = FALSE;
3211 player->is_dropping_pressed = FALSE;
3213 player->is_bored = FALSE;
3214 player->is_sleeping = FALSE;
3216 player->frame_counter_bored = -1;
3217 player->frame_counter_sleeping = -1;
3219 player->anim_delay_counter = 0;
3220 player->post_delay_counter = 0;
3222 player->dir_waiting = initial_move_dir;
3223 player->action_waiting = ACTION_DEFAULT;
3224 player->last_action_waiting = ACTION_DEFAULT;
3225 player->special_action_bored = ACTION_DEFAULT;
3226 player->special_action_sleeping = ACTION_DEFAULT;
3228 player->switch_x = -1;
3229 player->switch_y = -1;
3231 player->drop_x = -1;
3232 player->drop_y = -1;
3234 player->show_envelope = 0;
3236 SetPlayerMoveSpeed(player, level.initial_player_stepsize[i], TRUE);
3238 player->push_delay = -1; /* initialized when pushing starts */
3239 player->push_delay_value = game.initial_push_delay_value;
3241 player->drop_delay = 0;
3242 player->drop_pressed_delay = 0;
3244 player->last_jx = -1;
3245 player->last_jy = -1;
3249 player->shield_normal_time_left = 0;
3250 player->shield_deadly_time_left = 0;
3252 player->inventory_infinite_element = EL_UNDEFINED;
3253 player->inventory_size = 0;
3255 if (level.use_initial_inventory[i])
3257 for (j = 0; j < level.initial_inventory_size[i]; j++)
3259 int element = level.initial_inventory_content[i][j];
3260 int collect_count = element_info[element].collect_count_initial;
3263 if (!IS_CUSTOM_ELEMENT(element))
3266 if (collect_count == 0)
3267 player->inventory_infinite_element = element;
3269 for (k = 0; k < collect_count; k++)
3270 if (player->inventory_size < MAX_INVENTORY_SIZE)
3271 player->inventory_element[player->inventory_size++] = element;
3275 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
3276 SnapField(player, 0, 0);
3278 player->LevelSolved = FALSE;
3279 player->GameOver = FALSE;
3281 player->LevelSolved_GameWon = FALSE;
3282 player->LevelSolved_GameEnd = FALSE;
3283 player->LevelSolved_PanelOff = FALSE;
3284 player->LevelSolved_SaveTape = FALSE;
3285 player->LevelSolved_SaveScore = FALSE;
3286 player->LevelSolved_CountingTime = 0;
3287 player->LevelSolved_CountingScore = 0;
3289 map_player_action[i] = i;
3292 network_player_action_received = FALSE;
3294 #if defined(NETWORK_AVALIABLE)
3295 /* initial null action */
3296 if (network_playing)
3297 SendToServer_MovePlayer(MV_NONE);
3306 TimeLeft = level.time;
3309 ScreenMovDir = MV_NONE;
3313 ScrollStepSize = 0; /* will be correctly initialized by ScrollScreen() */
3315 AllPlayersGone = FALSE;
3317 game.no_time_limit = (level.time == 0);
3319 game.yamyam_content_nr = 0;
3320 game.robot_wheel_active = FALSE;
3321 game.magic_wall_active = FALSE;
3322 game.magic_wall_time_left = 0;
3323 game.light_time_left = 0;
3324 game.timegate_time_left = 0;
3325 game.switchgate_pos = 0;
3326 game.wind_direction = level.wind_direction_initial;
3328 game.lenses_time_left = 0;
3329 game.magnify_time_left = 0;
3331 game.ball_state = level.ball_state_initial;
3332 game.ball_content_nr = 0;
3334 game.envelope_active = FALSE;
3336 /* set focus to local player for network games, else to all players */
3337 game.centered_player_nr = (network_playing ? local_player->index_nr : -1);
3338 game.centered_player_nr_next = game.centered_player_nr;
3339 game.set_centered_player = FALSE;
3341 if (network_playing && tape.recording)
3343 /* store client dependent player focus when recording network games */
3344 tape.centered_player_nr_next = game.centered_player_nr_next;
3345 tape.set_centered_player = TRUE;
3348 for (i = 0; i < NUM_BELTS; i++)
3350 game.belt_dir[i] = MV_NONE;
3351 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
3354 for (i = 0; i < MAX_NUM_AMOEBA; i++)
3355 AmoebaCnt[i] = AmoebaCnt2[i] = 0;
3357 #if DEBUG_INIT_PLAYER
3360 printf("Player status at level initialization:\n");
3364 SCAN_PLAYFIELD(x, y)
3366 Feld[x][y] = level.field[x][y];
3367 MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
3368 ChangeDelay[x][y] = 0;
3369 ChangePage[x][y] = -1;
3370 CustomValue[x][y] = 0; /* initialized in InitField() */
3371 Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
3373 WasJustMoving[x][y] = 0;
3374 WasJustFalling[x][y] = 0;
3375 CheckCollision[x][y] = 0;
3376 CheckImpact[x][y] = 0;
3378 Pushed[x][y] = FALSE;
3380 ChangeCount[x][y] = 0;
3381 ChangeEvent[x][y] = -1;
3383 ExplodePhase[x][y] = 0;
3384 ExplodeDelay[x][y] = 0;
3385 ExplodeField[x][y] = EX_TYPE_NONE;
3387 RunnerVisit[x][y] = 0;
3388 PlayerVisit[x][y] = 0;
3391 GfxRandom[x][y] = INIT_GFX_RANDOM();
3392 GfxElement[x][y] = EL_UNDEFINED;
3393 GfxAction[x][y] = ACTION_DEFAULT;
3394 GfxDir[x][y] = MV_NONE;
3395 GfxRedraw[x][y] = GFX_REDRAW_NONE;
3399 printf("::: INIT GAME");
3402 SCAN_PLAYFIELD(x, y)
3404 if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
3406 if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
3408 if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
3411 InitField(x, y, TRUE);
3413 ResetGfxAnimation(x, y);
3417 printf(" -> %d\n", GfxFrame[DEBUG_GFXFRAME_X][DEBUG_GFXFRAME_Y]);
3422 for (i = 0; i < MAX_PLAYERS; i++)
3424 struct PlayerInfo *player = &stored_player[i];
3426 /* set number of special actions for bored and sleeping animation */
3427 player->num_special_action_bored =
3428 get_num_special_action(player->artwork_element,
3429 ACTION_BORING_1, ACTION_BORING_LAST);
3430 player->num_special_action_sleeping =
3431 get_num_special_action(player->artwork_element,
3432 ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
3435 game.emulation = (emulate_bd ? EMU_BOULDERDASH :
3436 emulate_sb ? EMU_SOKOBAN :
3437 emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
3439 /* initialize type of slippery elements */
3440 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3442 if (!IS_CUSTOM_ELEMENT(i))
3444 /* default: elements slip down either to the left or right randomly */
3445 element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
3447 /* SP style elements prefer to slip down on the left side */
3448 if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
3449 element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3451 /* BD style elements prefer to slip down on the left side */
3452 if (game.emulation == EMU_BOULDERDASH)
3453 element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3457 /* initialize explosion and ignition delay */
3458 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3460 if (!IS_CUSTOM_ELEMENT(i))
3463 int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
3464 game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
3465 game.emulation == EMU_SUPAPLEX ? 3 : 2);
3466 int last_phase = (num_phase + 1) * delay;
3467 int half_phase = (num_phase / 2) * delay;
3469 element_info[i].explosion_delay = last_phase - 1;
3470 element_info[i].ignition_delay = half_phase;
3472 if (i == EL_BLACK_ORB)
3473 element_info[i].ignition_delay = 1;
3477 /* correct non-moving belts to start moving left */
3478 for (i = 0; i < NUM_BELTS; i++)
3479 if (game.belt_dir[i] == MV_NONE)
3480 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
3482 #if USE_NEW_PLAYER_ASSIGNMENTS
3483 /* !!! SAME AS init.c:InitPlayerInfo() -- FIX THIS !!! */
3484 /* choose default local player */
3485 local_player = &stored_player[0];
3487 for (i = 0; i < MAX_PLAYERS; i++)
3488 stored_player[i].connected = FALSE;
3490 local_player->connected = TRUE;
3491 /* !!! SAME AS init.c:InitPlayerInfo() -- FIX THIS !!! */
3495 for (i = 0; i < MAX_PLAYERS; i++)
3496 stored_player[i].connected = tape.player_participates[i];
3498 else if (game.team_mode && !options.network)
3500 /* try to guess locally connected team mode players (needed for correct
3501 assignment of player figures from level to locally playing players) */
3503 for (i = 0; i < MAX_PLAYERS; i++)
3504 if (setup.input[i].use_joystick ||
3505 setup.input[i].key.left != KSYM_UNDEFINED)
3506 stored_player[i].connected = TRUE;
3509 #if DEBUG_INIT_PLAYER
3512 printf("Player status after level initialization:\n");
3514 for (i = 0; i < MAX_PLAYERS; i++)
3516 struct PlayerInfo *player = &stored_player[i];
3518 printf("- player %d: present == %d, connected == %d, active == %d",
3524 if (local_player == player)
3525 printf(" (local player)");
3532 #if DEBUG_INIT_PLAYER
3534 printf("Reassigning players ...\n");
3537 /* check if any connected player was not found in playfield */
3538 for (i = 0; i < MAX_PLAYERS; i++)
3540 struct PlayerInfo *player = &stored_player[i];
3542 if (player->connected && !player->present)
3544 struct PlayerInfo *field_player = NULL;
3546 #if DEBUG_INIT_PLAYER
3548 printf("- looking for field player for player %d ...\n", i + 1);
3551 /* assign first free player found that is present in the playfield */
3553 /* first try: look for unmapped playfield player that is not connected */
3554 for (j = 0; j < MAX_PLAYERS; j++)
3555 if (field_player == NULL &&
3556 stored_player[j].present &&
3557 !stored_player[j].mapped &&
3558 !stored_player[j].connected)
3559 field_player = &stored_player[j];
3561 /* second try: look for *any* unmapped playfield player */
3562 for (j = 0; j < MAX_PLAYERS; j++)
3563 if (field_player == NULL &&
3564 stored_player[j].present &&
3565 !stored_player[j].mapped)
3566 field_player = &stored_player[j];
3568 if (field_player != NULL)
3570 int jx = field_player->jx, jy = field_player->jy;
3572 #if DEBUG_INIT_PLAYER
3574 printf("- found player %d\n", field_player->index_nr + 1);
3577 player->present = FALSE;
3578 player->active = FALSE;
3580 field_player->present = TRUE;
3581 field_player->active = TRUE;
3584 player->initial_element = field_player->initial_element;
3585 player->artwork_element = field_player->artwork_element;
3587 player->block_last_field = field_player->block_last_field;
3588 player->block_delay_adjustment = field_player->block_delay_adjustment;
3591 StorePlayer[jx][jy] = field_player->element_nr;
3593 field_player->jx = field_player->last_jx = jx;
3594 field_player->jy = field_player->last_jy = jy;
3596 if (local_player == player)
3597 local_player = field_player;
3599 map_player_action[field_player->index_nr] = i;
3601 field_player->mapped = TRUE;
3603 #if DEBUG_INIT_PLAYER
3605 printf("- map_player_action[%d] == %d\n",
3606 field_player->index_nr + 1, i + 1);
3611 if (player->connected && player->present)
3612 player->mapped = TRUE;
3615 #if DEBUG_INIT_PLAYER
3618 printf("Player status after player assignment (first stage):\n");
3620 for (i = 0; i < MAX_PLAYERS; i++)
3622 struct PlayerInfo *player = &stored_player[i];
3624 printf("- player %d: present == %d, connected == %d, active == %d",
3630 if (local_player == player)
3631 printf(" (local player)");
3640 /* check if any connected player was not found in playfield */
3641 for (i = 0; i < MAX_PLAYERS; i++)
3643 struct PlayerInfo *player = &stored_player[i];
3645 if (player->connected && !player->present)
3647 for (j = 0; j < MAX_PLAYERS; j++)
3649 struct PlayerInfo *field_player = &stored_player[j];
3650 int jx = field_player->jx, jy = field_player->jy;
3652 /* assign first free player found that is present in the playfield */
3653 if (field_player->present && !field_player->connected)
3655 player->present = TRUE;
3656 player->active = TRUE;
3658 field_player->present = FALSE;
3659 field_player->active = FALSE;
3661 player->initial_element = field_player->initial_element;
3662 player->artwork_element = field_player->artwork_element;
3664 player->block_last_field = field_player->block_last_field;
3665 player->block_delay_adjustment = field_player->block_delay_adjustment;
3667 StorePlayer[jx][jy] = player->element_nr;
3669 player->jx = player->last_jx = jx;
3670 player->jy = player->last_jy = jy;
3680 printf("::: local_player->present == %d\n", local_player->present);
3685 /* when playing a tape, eliminate all players who do not participate */
3687 #if USE_NEW_PLAYER_ASSIGNMENTS
3689 if (!game.team_mode)
3691 for (i = 0; i < MAX_PLAYERS; i++)
3693 if (stored_player[i].active &&
3694 !tape.player_participates[map_player_action[i]])
3696 struct PlayerInfo *player = &stored_player[i];
3697 int jx = player->jx, jy = player->jy;
3699 #if DEBUG_INIT_PLAYER
3701 printf("Removing player %d at (%d, %d)\n", i + 1, jx, jy);
3704 player->active = FALSE;
3705 StorePlayer[jx][jy] = 0;
3706 Feld[jx][jy] = EL_EMPTY;
3713 for (i = 0; i < MAX_PLAYERS; i++)
3715 if (stored_player[i].active &&
3716 !tape.player_participates[i])
3718 struct PlayerInfo *player = &stored_player[i];
3719 int jx = player->jx, jy = player->jy;
3721 player->active = FALSE;
3722 StorePlayer[jx][jy] = 0;
3723 Feld[jx][jy] = EL_EMPTY;
3728 else if (!options.network && !game.team_mode) /* && !tape.playing */
3730 /* when in single player mode, eliminate all but the first active player */
3732 for (i = 0; i < MAX_PLAYERS; i++)
3734 if (stored_player[i].active)
3736 for (j = i + 1; j < MAX_PLAYERS; j++)
3738 if (stored_player[j].active)
3740 struct PlayerInfo *player = &stored_player[j];
3741 int jx = player->jx, jy = player->jy;
3743 player->active = FALSE;
3744 player->present = FALSE;
3746 StorePlayer[jx][jy] = 0;
3747 Feld[jx][jy] = EL_EMPTY;
3754 /* when recording the game, store which players take part in the game */
3757 #if USE_NEW_PLAYER_ASSIGNMENTS
3758 for (i = 0; i < MAX_PLAYERS; i++)
3759 if (stored_player[i].connected)
3760 tape.player_participates[i] = TRUE;
3762 for (i = 0; i < MAX_PLAYERS; i++)
3763 if (stored_player[i].active)
3764 tape.player_participates[i] = TRUE;
3768 #if DEBUG_INIT_PLAYER
3771 printf("Player status after player assignment (final stage):\n");
3773 for (i = 0; i < MAX_PLAYERS; i++)
3775 struct PlayerInfo *player = &stored_player[i];
3777 printf("- player %d: present == %d, connected == %d, active == %d",
3783 if (local_player == player)
3784 printf(" (local player)");
3791 if (BorderElement == EL_EMPTY)
3794 SBX_Right = lev_fieldx - SCR_FIELDX;
3796 SBY_Lower = lev_fieldy - SCR_FIELDY;
3801 SBX_Right = lev_fieldx - SCR_FIELDX + 1;
3803 SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
3806 if (full_lev_fieldx <= SCR_FIELDX)
3807 SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
3808 if (full_lev_fieldy <= SCR_FIELDY)
3809 SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
3811 if (EVEN(SCR_FIELDX) && full_lev_fieldx > SCR_FIELDX)
3813 if (EVEN(SCR_FIELDY) && full_lev_fieldy > SCR_FIELDY)
3816 /* if local player not found, look for custom element that might create
3817 the player (make some assumptions about the right custom element) */
3818 if (!local_player->present)
3820 int start_x = 0, start_y = 0;
3821 int found_rating = 0;
3822 int found_element = EL_UNDEFINED;
3823 int player_nr = local_player->index_nr;
3825 SCAN_PLAYFIELD(x, y)
3827 int element = Feld[x][y];
3832 if (level.use_start_element[player_nr] &&
3833 level.start_element[player_nr] == element &&
3840 found_element = element;
3843 if (!IS_CUSTOM_ELEMENT(element))
3846 if (CAN_CHANGE(element))
3848 for (i = 0; i < element_info[element].num_change_pages; i++)
3850 /* check for player created from custom element as single target */
3851 content = element_info[element].change_page[i].target_element;
3852 is_player = ELEM_IS_PLAYER(content);
3854 if (is_player && (found_rating < 3 ||
3855 (found_rating == 3 && element < found_element)))
3861 found_element = element;
3866 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
3868 /* check for player created from custom element as explosion content */
3869 content = element_info[element].content.e[xx][yy];
3870 is_player = ELEM_IS_PLAYER(content);
3872 if (is_player && (found_rating < 2 ||
3873 (found_rating == 2 && element < found_element)))
3875 start_x = x + xx - 1;
3876 start_y = y + yy - 1;
3879 found_element = element;
3882 if (!CAN_CHANGE(element))
3885 for (i = 0; i < element_info[element].num_change_pages; i++)
3887 /* check for player created from custom element as extended target */
3889 element_info[element].change_page[i].target_content.e[xx][yy];
3891 is_player = ELEM_IS_PLAYER(content);
3893 if (is_player && (found_rating < 1 ||
3894 (found_rating == 1 && element < found_element)))
3896 start_x = x + xx - 1;
3897 start_y = y + yy - 1;
3900 found_element = element;
3906 scroll_x = (start_x < SBX_Left + MIDPOSX ? SBX_Left :
3907 start_x > SBX_Right + MIDPOSX ? SBX_Right :
3910 scroll_y = (start_y < SBY_Upper + MIDPOSY ? SBY_Upper :
3911 start_y > SBY_Lower + MIDPOSY ? SBY_Lower :
3916 scroll_x = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
3917 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
3918 local_player->jx - MIDPOSX);
3920 scroll_y = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
3921 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
3922 local_player->jy - MIDPOSY);
3925 /* !!! FIX THIS (START) !!! */
3926 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
3928 InitGameEngine_EM();
3930 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
3932 InitGameEngine_SP();
3936 DrawLevel(REDRAW_FIELD);
3939 /* after drawing the level, correct some elements */
3940 if (game.timegate_time_left == 0)
3941 CloseAllOpenTimegates();
3944 /* blit playfield from scroll buffer to normal back buffer for fading in */
3945 BlitScreenToBitmap(backbuffer);
3946 /* !!! FIX THIS (END) !!! */
3948 DrawMaskedBorder(fade_mask);
3953 // full screen redraw is required at this point in the following cases:
3954 // - special editor door undrawn when game was started from level editor
3955 // - drawing area (playfield) was changed and has to be removed completely
3956 redraw_mask = REDRAW_ALL;
3960 if (!game.restart_level)
3962 /* copy default game door content to main double buffer */
3964 /* !!! CHECK AGAIN !!! */
3965 SetPanelBackground();
3966 // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
3967 DrawBackground(DX, DY, DXSIZE, DYSIZE);
3970 SetPanelBackground();
3971 SetDrawBackgroundMask(REDRAW_DOOR_1);
3973 UpdateAndDisplayGameControlValues();
3975 if (!game.restart_level)
3981 CreateGameButtons();
3983 game_gadget[SOUND_CTRL_ID_MUSIC]->checked = setup.sound_music;
3984 game_gadget[SOUND_CTRL_ID_LOOPS]->checked = setup.sound_loops;
3985 game_gadget[SOUND_CTRL_ID_SIMPLE]->checked = setup.sound_simple;
3990 /* copy actual game door content to door double buffer for OpenDoor() */
3991 BlitBitmap(drawto, bitmap_db_door_1, DX, DY, DXSIZE, DYSIZE, 0, 0);
3993 OpenDoor(DOOR_OPEN_ALL);
3995 PlaySound(SND_GAME_STARTING);
3997 if (setup.sound_music)
4000 KeyboardAutoRepeatOffUnlessAutoplay();
4002 #if DEBUG_INIT_PLAYER
4005 printf("Player status (final):\n");
4007 for (i = 0; i < MAX_PLAYERS; i++)
4009 struct PlayerInfo *player = &stored_player[i];
4011 printf("- player %d: present == %d, connected == %d, active == %d",
4017 if (local_player == player)
4018 printf(" (local player)");
4031 if (!game.restart_level && !tape.playing)
4033 LevelStats_incPlayed(level_nr);
4035 SaveLevelSetup_SeriesInfo();
4038 game.restart_level = FALSE;
4040 SaveEngineSnapshotToListInitial();
4043 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y)
4045 /* this is used for non-R'n'D game engines to update certain engine values */
4047 /* needed to determine if sounds are played within the visible screen area */
4048 scroll_x = actual_scroll_x;
4049 scroll_y = actual_scroll_y;
4052 void InitMovDir(int x, int y)
4054 int i, element = Feld[x][y];
4055 static int xy[4][2] =
4062 static int direction[3][4] =
4064 { MV_RIGHT, MV_UP, MV_LEFT, MV_DOWN },
4065 { MV_LEFT, MV_DOWN, MV_RIGHT, MV_UP },
4066 { MV_LEFT, MV_RIGHT, MV_UP, MV_DOWN }
4075 Feld[x][y] = EL_BUG;
4076 MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
4079 case EL_SPACESHIP_RIGHT:
4080 case EL_SPACESHIP_UP:
4081 case EL_SPACESHIP_LEFT:
4082 case EL_SPACESHIP_DOWN:
4083 Feld[x][y] = EL_SPACESHIP;
4084 MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
4087 case EL_BD_BUTTERFLY_RIGHT:
4088 case EL_BD_BUTTERFLY_UP:
4089 case EL_BD_BUTTERFLY_LEFT:
4090 case EL_BD_BUTTERFLY_DOWN:
4091 Feld[x][y] = EL_BD_BUTTERFLY;
4092 MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
4095 case EL_BD_FIREFLY_RIGHT:
4096 case EL_BD_FIREFLY_UP:
4097 case EL_BD_FIREFLY_LEFT:
4098 case EL_BD_FIREFLY_DOWN:
4099 Feld[x][y] = EL_BD_FIREFLY;
4100 MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
4103 case EL_PACMAN_RIGHT:
4105 case EL_PACMAN_LEFT:
4106 case EL_PACMAN_DOWN:
4107 Feld[x][y] = EL_PACMAN;
4108 MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
4111 case EL_YAMYAM_LEFT:
4112 case EL_YAMYAM_RIGHT:
4114 case EL_YAMYAM_DOWN:
4115 Feld[x][y] = EL_YAMYAM;
4116 MovDir[x][y] = direction[2][element - EL_YAMYAM_LEFT];
4119 case EL_SP_SNIKSNAK:
4120 MovDir[x][y] = MV_UP;
4123 case EL_SP_ELECTRON:
4124 MovDir[x][y] = MV_LEFT;
4131 Feld[x][y] = EL_MOLE;
4132 MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
4136 if (IS_CUSTOM_ELEMENT(element))
4138 struct ElementInfo *ei = &element_info[element];
4139 int move_direction_initial = ei->move_direction_initial;
4140 int move_pattern = ei->move_pattern;
4142 if (move_direction_initial == MV_START_PREVIOUS)
4144 if (MovDir[x][y] != MV_NONE)
4147 move_direction_initial = MV_START_AUTOMATIC;
4150 if (move_direction_initial == MV_START_RANDOM)
4151 MovDir[x][y] = 1 << RND(4);
4152 else if (move_direction_initial & MV_ANY_DIRECTION)
4153 MovDir[x][y] = move_direction_initial;
4154 else if (move_pattern == MV_ALL_DIRECTIONS ||
4155 move_pattern == MV_TURNING_LEFT ||
4156 move_pattern == MV_TURNING_RIGHT ||
4157 move_pattern == MV_TURNING_LEFT_RIGHT ||
4158 move_pattern == MV_TURNING_RIGHT_LEFT ||
4159 move_pattern == MV_TURNING_RANDOM)
4160 MovDir[x][y] = 1 << RND(4);
4161 else if (move_pattern == MV_HORIZONTAL)
4162 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
4163 else if (move_pattern == MV_VERTICAL)
4164 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
4165 else if (move_pattern & MV_ANY_DIRECTION)
4166 MovDir[x][y] = element_info[element].move_pattern;
4167 else if (move_pattern == MV_ALONG_LEFT_SIDE ||
4168 move_pattern == MV_ALONG_RIGHT_SIDE)
4170 /* use random direction as default start direction */
4171 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
4172 MovDir[x][y] = 1 << RND(4);
4174 for (i = 0; i < NUM_DIRECTIONS; i++)
4176 int x1 = x + xy[i][0];
4177 int y1 = y + xy[i][1];
4179 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4181 if (move_pattern == MV_ALONG_RIGHT_SIDE)
4182 MovDir[x][y] = direction[0][i];
4184 MovDir[x][y] = direction[1][i];
4193 MovDir[x][y] = 1 << RND(4);
4195 if (element != EL_BUG &&
4196 element != EL_SPACESHIP &&
4197 element != EL_BD_BUTTERFLY &&
4198 element != EL_BD_FIREFLY)
4201 for (i = 0; i < NUM_DIRECTIONS; i++)
4203 int x1 = x + xy[i][0];
4204 int y1 = y + xy[i][1];
4206 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4208 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
4210 MovDir[x][y] = direction[0][i];
4213 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
4214 element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4216 MovDir[x][y] = direction[1][i];
4225 GfxDir[x][y] = MovDir[x][y];
4228 void InitAmoebaNr(int x, int y)
4231 int group_nr = AmoebeNachbarNr(x, y);
4235 for (i = 1; i < MAX_NUM_AMOEBA; i++)
4237 if (AmoebaCnt[i] == 0)
4245 AmoebaNr[x][y] = group_nr;
4246 AmoebaCnt[group_nr]++;
4247 AmoebaCnt2[group_nr]++;
4250 static void PlayerWins(struct PlayerInfo *player)
4252 player->LevelSolved = TRUE;
4253 player->GameOver = TRUE;
4255 player->score_final = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
4256 level.native_em_level->lev->score : player->score);
4258 player->LevelSolved_CountingTime = (game.no_time_limit ? TimePlayed :
4260 player->LevelSolved_CountingScore = player->score_final;
4265 static int time, time_final;
4266 static int score, score_final;
4267 static int game_over_delay_1 = 0;
4268 static int game_over_delay_2 = 0;
4269 int game_over_delay_value_1 = 50;
4270 int game_over_delay_value_2 = 50;
4272 if (!local_player->LevelSolved_GameWon)
4276 /* do not start end game actions before the player stops moving (to exit) */
4277 if (local_player->MovPos)
4280 local_player->LevelSolved_GameWon = TRUE;
4281 local_player->LevelSolved_SaveTape = tape.recording;
4282 local_player->LevelSolved_SaveScore = !tape.playing;
4286 LevelStats_incSolved(level_nr);
4288 SaveLevelSetup_SeriesInfo();
4291 if (tape.auto_play) /* tape might already be stopped here */
4292 tape.auto_play_level_solved = TRUE;
4296 game_over_delay_1 = game_over_delay_value_1;
4297 game_over_delay_2 = game_over_delay_value_2;
4299 time = time_final = (game.no_time_limit ? TimePlayed : TimeLeft);
4300 score = score_final = local_player->score_final;
4305 score_final += TimeLeft * level.score[SC_TIME_BONUS];
4307 else if (game.no_time_limit && TimePlayed < 999)
4310 score_final += (999 - TimePlayed) * level.score[SC_TIME_BONUS];
4313 local_player->score_final = score_final;
4315 if (level_editor_test_game)
4318 score = score_final;
4320 local_player->LevelSolved_CountingTime = time;
4321 local_player->LevelSolved_CountingScore = score;
4323 game_panel_controls[GAME_PANEL_TIME].value = time;
4324 game_panel_controls[GAME_PANEL_SCORE].value = score;
4326 DisplayGameControlValues();
4329 if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
4331 if (ExitX >= 0 && ExitY >= 0) /* local player has left the level */
4333 /* close exit door after last player */
4334 if ((AllPlayersGone &&
4335 (Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
4336 Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN ||
4337 Feld[ExitX][ExitY] == EL_STEEL_EXIT_OPEN)) ||
4338 Feld[ExitX][ExitY] == EL_EM_EXIT_OPEN ||
4339 Feld[ExitX][ExitY] == EL_EM_STEEL_EXIT_OPEN)
4341 int element = Feld[ExitX][ExitY];
4343 Feld[ExitX][ExitY] =
4344 (element == EL_EXIT_OPEN ? EL_EXIT_CLOSING :
4345 element == EL_EM_EXIT_OPEN ? EL_EM_EXIT_CLOSING :
4346 element == EL_SP_EXIT_OPEN ? EL_SP_EXIT_CLOSING:
4347 element == EL_STEEL_EXIT_OPEN ? EL_STEEL_EXIT_CLOSING:
4348 EL_EM_STEEL_EXIT_CLOSING);
4350 PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING);
4353 /* player disappears */
4354 DrawLevelField(ExitX, ExitY);
4357 for (i = 0; i < MAX_PLAYERS; i++)
4359 struct PlayerInfo *player = &stored_player[i];
4361 if (player->present)
4363 RemovePlayer(player);
4365 /* player disappears */
4366 DrawLevelField(player->jx, player->jy);
4371 PlaySound(SND_GAME_WINNING);
4374 if (game_over_delay_1 > 0)
4376 game_over_delay_1--;
4381 if (time != time_final)
4383 int time_to_go = ABS(time_final - time);
4384 int time_count_dir = (time < time_final ? +1 : -1);
4385 int time_count_steps = (time_to_go > 100 && time_to_go % 10 == 0 ? 10 : 1);
4387 time += time_count_steps * time_count_dir;
4388 score += time_count_steps * level.score[SC_TIME_BONUS];
4390 local_player->LevelSolved_CountingTime = time;
4391 local_player->LevelSolved_CountingScore = score;
4393 game_panel_controls[GAME_PANEL_TIME].value = time;
4394 game_panel_controls[GAME_PANEL_SCORE].value = score;
4396 DisplayGameControlValues();
4398 if (time == time_final)
4399 StopSound(SND_GAME_LEVELTIME_BONUS);
4400 else if (setup.sound_loops)
4401 PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4403 PlaySound(SND_GAME_LEVELTIME_BONUS);
4408 local_player->LevelSolved_PanelOff = TRUE;
4410 if (game_over_delay_2 > 0)
4412 game_over_delay_2--;
4423 boolean raise_level = FALSE;
4425 local_player->LevelSolved_GameEnd = TRUE;
4427 if (!global.use_envelope_request)
4428 CloseDoor(DOOR_CLOSE_1);
4430 if (local_player->LevelSolved_SaveTape)
4432 SaveTapeChecked(tape.level_nr); /* ask to save tape */
4435 CloseDoor(DOOR_CLOSE_ALL);
4437 if (level_editor_test_game)
4439 game_status = GAME_MODE_MAIN;
4446 if (!local_player->LevelSolved_SaveScore)
4448 FadeOut(REDRAW_FIELD);
4450 game_status = GAME_MODE_MAIN;
4457 if (level_nr == leveldir_current->handicap_level)
4459 leveldir_current->handicap_level++;
4461 SaveLevelSetup_SeriesInfo();
4464 if (level_nr < leveldir_current->last_level)
4465 raise_level = TRUE; /* advance to next level */
4467 if ((hi_pos = NewHiScore()) >= 0)
4469 game_status = GAME_MODE_SCORES;
4471 DrawHallOfFame(hi_pos);
4481 FadeOut(REDRAW_FIELD);
4483 game_status = GAME_MODE_MAIN;
4500 LoadScore(level_nr);
4502 if (strEqual(setup.player_name, EMPTY_PLAYER_NAME) ||
4503 local_player->score_final < highscore[MAX_SCORE_ENTRIES - 1].Score)
4506 for (k = 0; k < MAX_SCORE_ENTRIES; k++)
4508 if (local_player->score_final > highscore[k].Score)
4510 /* player has made it to the hall of fame */
4512 if (k < MAX_SCORE_ENTRIES - 1)
4514 int m = MAX_SCORE_ENTRIES - 1;
4517 for (l = k; l < MAX_SCORE_ENTRIES; l++)
4518 if (strEqual(setup.player_name, highscore[l].Name))
4520 if (m == k) /* player's new highscore overwrites his old one */
4524 for (l = m; l > k; l--)
4526 strcpy(highscore[l].Name, highscore[l - 1].Name);
4527 highscore[l].Score = highscore[l - 1].Score;
4534 strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
4535 highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
4536 highscore[k].Score = local_player->score_final;
4542 else if (!strncmp(setup.player_name, highscore[k].Name,
4543 MAX_PLAYER_NAME_LEN))
4544 break; /* player already there with a higher score */
4550 SaveScore(level_nr);
4555 inline static int getElementMoveStepsizeExt(int x, int y, int direction)
4557 int element = Feld[x][y];
4558 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
4559 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
4560 int horiz_move = (dx != 0);
4561 int sign = (horiz_move ? dx : dy);
4562 int step = sign * element_info[element].move_stepsize;
4564 /* special values for move stepsize for spring and things on conveyor belt */
4567 if (CAN_FALL(element) &&
4568 y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
4569 step = sign * MOVE_STEPSIZE_NORMAL / 2;
4570 else if (element == EL_SPRING)
4571 step = sign * MOVE_STEPSIZE_NORMAL * 2;
4577 inline static int getElementMoveStepsize(int x, int y)
4579 return getElementMoveStepsizeExt(x, y, MovDir[x][y]);
4582 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
4584 if (player->GfxAction != action || player->GfxDir != dir)
4586 player->GfxAction = action;
4587 player->GfxDir = dir;
4589 player->StepFrame = 0;
4593 static void ResetGfxFrame(int x, int y, boolean redraw)
4595 int element = Feld[x][y];
4596 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
4597 int last_gfx_frame = GfxFrame[x][y];
4599 if (graphic_info[graphic].anim_global_sync)
4600 GfxFrame[x][y] = FrameCounter;
4601 else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
4602 GfxFrame[x][y] = CustomValue[x][y];
4603 else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
4604 GfxFrame[x][y] = element_info[element].collect_score;
4605 else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
4606 GfxFrame[x][y] = ChangeDelay[x][y];
4608 if (redraw && GfxFrame[x][y] != last_gfx_frame)
4609 DrawLevelGraphicAnimation(x, y, graphic);
4612 static void ResetGfxAnimation(int x, int y)
4614 GfxAction[x][y] = ACTION_DEFAULT;
4615 GfxDir[x][y] = MovDir[x][y];
4618 ResetGfxFrame(x, y, FALSE);
4621 if (x == DEBUG_GFXFRAME_X && y == DEBUG_GFXFRAME_Y)
4622 printf(" (RESET_GFX_ANIM)");
4626 static void ResetRandomAnimationValue(int x, int y)
4628 GfxRandom[x][y] = INIT_GFX_RANDOM();
4631 void InitMovingField(int x, int y, int direction)
4633 int element = Feld[x][y];
4634 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
4635 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
4638 boolean is_moving_before, is_moving_after;
4640 /* check if element was/is moving or being moved before/after mode change */
4641 is_moving_before = (WasJustMoving[x][y] != 0);
4642 is_moving_after = (getElementMoveStepsizeExt(x, y, direction) != 0);
4644 /* reset animation only for moving elements which change direction of moving
4645 or which just started or stopped moving
4646 (else CEs with property "can move" / "not moving" are reset each frame) */
4647 if (is_moving_before != is_moving_after ||
4648 direction != MovDir[x][y])
4649 ResetGfxAnimation(x, y);
4651 MovDir[x][y] = direction;
4652 GfxDir[x][y] = direction;
4654 GfxAction[x][y] = (!is_moving_after ? ACTION_WAITING :
4655 direction == MV_DOWN && CAN_FALL(element) ?
4656 ACTION_FALLING : ACTION_MOVING);
4658 /* this is needed for CEs with property "can move" / "not moving" */
4660 if (is_moving_after)
4662 if (Feld[newx][newy] == EL_EMPTY)
4663 Feld[newx][newy] = EL_BLOCKED;
4665 MovDir[newx][newy] = MovDir[x][y];
4667 CustomValue[newx][newy] = CustomValue[x][y];
4669 GfxFrame[newx][newy] = GfxFrame[x][y];
4670 GfxRandom[newx][newy] = GfxRandom[x][y];
4671 GfxAction[newx][newy] = GfxAction[x][y];
4672 GfxDir[newx][newy] = GfxDir[x][y];
4676 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
4678 int direction = MovDir[x][y];
4679 int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0);
4680 int newy = y + (direction & MV_UP ? -1 : direction & MV_DOWN ? +1 : 0);
4686 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
4688 int oldx = x, oldy = y;
4689 int direction = MovDir[x][y];
4691 if (direction == MV_LEFT)
4693 else if (direction == MV_RIGHT)
4695 else if (direction == MV_UP)
4697 else if (direction == MV_DOWN)
4700 *comes_from_x = oldx;
4701 *comes_from_y = oldy;
4704 int MovingOrBlocked2Element(int x, int y)
4706 int element = Feld[x][y];
4708 if (element == EL_BLOCKED)
4712 Blocked2Moving(x, y, &oldx, &oldy);
4713 return Feld[oldx][oldy];
4719 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
4721 /* like MovingOrBlocked2Element(), but if element is moving
4722 and (x,y) is the field the moving element is just leaving,
4723 return EL_BLOCKED instead of the element value */
4724 int element = Feld[x][y];
4726 if (IS_MOVING(x, y))
4728 if (element == EL_BLOCKED)
4732 Blocked2Moving(x, y, &oldx, &oldy);
4733 return Feld[oldx][oldy];
4742 static void RemoveField(int x, int y)
4744 Feld[x][y] = EL_EMPTY;
4750 CustomValue[x][y] = 0;
4753 ChangeDelay[x][y] = 0;
4754 ChangePage[x][y] = -1;
4755 Pushed[x][y] = FALSE;
4757 GfxElement[x][y] = EL_UNDEFINED;
4758 GfxAction[x][y] = ACTION_DEFAULT;
4759 GfxDir[x][y] = MV_NONE;
4762 void RemoveMovingField(int x, int y)
4764 int oldx = x, oldy = y, newx = x, newy = y;
4765 int element = Feld[x][y];
4766 int next_element = EL_UNDEFINED;
4768 if (element != EL_BLOCKED && !IS_MOVING(x, y))
4771 if (IS_MOVING(x, y))
4773 Moving2Blocked(x, y, &newx, &newy);
4775 if (Feld[newx][newy] != EL_BLOCKED)
4777 /* element is moving, but target field is not free (blocked), but
4778 already occupied by something different (example: acid pool);
4779 in this case, only remove the moving field, but not the target */
4781 RemoveField(oldx, oldy);
4783 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
4785 TEST_DrawLevelField(oldx, oldy);
4790 else if (element == EL_BLOCKED)
4792 Blocked2Moving(x, y, &oldx, &oldy);
4793 if (!IS_MOVING(oldx, oldy))
4797 if (element == EL_BLOCKED &&
4798 (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
4799 Feld[oldx][oldy] == EL_QUICKSAND_FAST_EMPTYING ||
4800 Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
4801 Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
4802 Feld[oldx][oldy] == EL_DC_MAGIC_WALL_EMPTYING ||
4803 Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
4804 next_element = get_next_element(Feld[oldx][oldy]);
4806 RemoveField(oldx, oldy);
4807 RemoveField(newx, newy);
4809 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
4811 if (next_element != EL_UNDEFINED)
4812 Feld[oldx][oldy] = next_element;
4814 TEST_DrawLevelField(oldx, oldy);
4815 TEST_DrawLevelField(newx, newy);
4818 void DrawDynamite(int x, int y)
4820 int sx = SCREENX(x), sy = SCREENY(y);
4821 int graphic = el2img(Feld[x][y]);
4824 if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
4827 if (IS_WALKABLE_INSIDE(Back[x][y]))
4831 DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
4832 else if (Store[x][y])
4833 DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
4835 frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
4837 if (Back[x][y] || Store[x][y])
4838 DrawGraphicThruMask(sx, sy, graphic, frame);
4840 DrawGraphic(sx, sy, graphic, frame);
4843 void CheckDynamite(int x, int y)
4845 if (MovDelay[x][y] != 0) /* dynamite is still waiting to explode */
4849 if (MovDelay[x][y] != 0)
4852 PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
4858 StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
4863 static void setMinimalPlayerBoundaries(int *sx1, int *sy1, int *sx2, int *sy2)
4865 boolean num_checked_players = 0;
4868 for (i = 0; i < MAX_PLAYERS; i++)
4870 if (stored_player[i].active)
4872 int sx = stored_player[i].jx;
4873 int sy = stored_player[i].jy;
4875 if (num_checked_players == 0)
4882 *sx1 = MIN(*sx1, sx);
4883 *sy1 = MIN(*sy1, sy);
4884 *sx2 = MAX(*sx2, sx);
4885 *sy2 = MAX(*sy2, sy);
4888 num_checked_players++;
4893 static boolean checkIfAllPlayersFitToScreen_RND()
4895 int sx1 = 0, sy1 = 0, sx2 = 0, sy2 = 0;
4897 setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
4899 return (sx2 - sx1 < SCR_FIELDX &&
4900 sy2 - sy1 < SCR_FIELDY);
4903 static void setScreenCenteredToAllPlayers(int *sx, int *sy)
4905 int sx1 = scroll_x, sy1 = scroll_y, sx2 = scroll_x, sy2 = scroll_y;
4907 setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
4909 *sx = (sx1 + sx2) / 2;
4910 *sy = (sy1 + sy2) / 2;
4913 void DrawRelocateScreen(int old_x, int old_y, int x, int y, int move_dir,
4914 boolean center_screen, boolean quick_relocation)
4916 boolean ffwd_delay = (tape.playing && tape.fast_forward);
4917 boolean no_delay = (tape.warp_forward);
4918 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
4919 int wait_delay_value = (no_delay ? 0 : frame_delay_value);
4921 if (level.lazy_relocation && IN_VIS_FIELD(SCREENX(x), SCREENY(y)))
4925 else if (quick_relocation)
4927 if (!IN_VIS_FIELD(SCREENX(x), SCREENY(y)) || center_screen)
4929 if (!level.shifted_relocation || center_screen)
4931 /* quick relocation (without scrolling), with centering of screen */
4933 scroll_x = (x < SBX_Left + MIDPOSX ? SBX_Left :
4934 x > SBX_Right + MIDPOSX ? SBX_Right :
4937 scroll_y = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
4938 y > SBY_Lower + MIDPOSY ? SBY_Lower :
4943 /* quick relocation (without scrolling), but do not center screen */
4945 int center_scroll_x = (old_x < SBX_Left + MIDPOSX ? SBX_Left :
4946 old_x > SBX_Right + MIDPOSX ? SBX_Right :
4949 int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
4950 old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
4953 int offset_x = x + (scroll_x - center_scroll_x);
4954 int offset_y = y + (scroll_y - center_scroll_y);
4956 scroll_x = (offset_x < SBX_Left + MIDPOSX ? SBX_Left :
4957 offset_x > SBX_Right + MIDPOSX ? SBX_Right :
4958 offset_x - MIDPOSX);
4960 scroll_y = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
4961 offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
4962 offset_y - MIDPOSY);
4967 if (!level.shifted_relocation || center_screen)
4969 /* quick relocation (without scrolling), with centering of screen */
4971 scroll_x = (x < SBX_Left + MIDPOSX ? SBX_Left :
4972 x > SBX_Right + MIDPOSX ? SBX_Right :
4975 scroll_y = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
4976 y > SBY_Lower + MIDPOSY ? SBY_Lower :
4981 /* quick relocation (without scrolling), but do not center screen */
4983 int center_scroll_x = (old_x < SBX_Left + MIDPOSX ? SBX_Left :
4984 old_x > SBX_Right + MIDPOSX ? SBX_Right :
4987 int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
4988 old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
4991 int offset_x = x + (scroll_x - center_scroll_x);
4992 int offset_y = y + (scroll_y - center_scroll_y);
4994 scroll_x = (offset_x < SBX_Left + MIDPOSX ? SBX_Left :
4995 offset_x > SBX_Right + MIDPOSX ? SBX_Right :
4996 offset_x - MIDPOSX);
4998 scroll_y = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
4999 offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5000 offset_y - MIDPOSY);
5008 int scroll_xx, scroll_yy;
5010 if (!level.shifted_relocation || center_screen)
5012 /* visible relocation (with scrolling), with centering of screen */
5014 scroll_xx = (x < SBX_Left + MIDPOSX ? SBX_Left :
5015 x > SBX_Right + MIDPOSX ? SBX_Right :
5018 scroll_yy = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
5019 y > SBY_Lower + MIDPOSY ? SBY_Lower :
5024 /* visible relocation (with scrolling), but do not center screen */
5026 int center_scroll_x = (old_x < SBX_Left + MIDPOSX ? SBX_Left :
5027 old_x > SBX_Right + MIDPOSX ? SBX_Right :
5030 int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5031 old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5034 int offset_x = x + (scroll_x - center_scroll_x);
5035 int offset_y = y + (scroll_y - center_scroll_y);
5037 scroll_xx = (offset_x < SBX_Left + MIDPOSX ? SBX_Left :
5038 offset_x > SBX_Right + MIDPOSX ? SBX_Right :
5039 offset_x - MIDPOSX);
5041 scroll_yy = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5042 offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5043 offset_y - MIDPOSY);
5046 ScrollScreen(NULL, SCROLL_GO_ON); /* scroll last frame to full tile */
5048 while (scroll_x != scroll_xx || scroll_y != scroll_yy)
5051 int fx = FX, fy = FY;
5053 dx = (scroll_xx < scroll_x ? +1 : scroll_xx > scroll_x ? -1 : 0);
5054 dy = (scroll_yy < scroll_y ? +1 : scroll_yy > scroll_y ? -1 : 0);
5056 if (dx == 0 && dy == 0) /* no scrolling needed at all */
5062 fx += dx * TILEX / 2;
5063 fy += dy * TILEY / 2;
5065 ScrollLevel(dx, dy);
5068 /* scroll in two steps of half tile size to make things smoother */
5069 BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
5070 Delay(wait_delay_value);
5072 /* scroll second step to align at full tile size */
5074 Delay(wait_delay_value);
5079 Delay(wait_delay_value);
5083 void RelocatePlayer(int jx, int jy, int el_player_raw)
5085 int el_player = GET_PLAYER_ELEMENT(el_player_raw);
5086 int player_nr = GET_PLAYER_NR(el_player);
5087 struct PlayerInfo *player = &stored_player[player_nr];
5088 boolean ffwd_delay = (tape.playing && tape.fast_forward);
5089 boolean no_delay = (tape.warp_forward);
5090 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5091 int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5092 int old_jx = player->jx;
5093 int old_jy = player->jy;
5094 int old_element = Feld[old_jx][old_jy];
5095 int element = Feld[jx][jy];
5096 boolean player_relocated = (old_jx != jx || old_jy != jy);
5098 int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
5099 int move_dir_vert = (jy < old_jy ? MV_UP : jy > old_jy ? MV_DOWN : 0);
5100 int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
5101 int enter_side_vert = MV_DIR_OPPOSITE(move_dir_vert);
5102 int leave_side_horiz = move_dir_horiz;
5103 int leave_side_vert = move_dir_vert;
5104 int enter_side = enter_side_horiz | enter_side_vert;
5105 int leave_side = leave_side_horiz | leave_side_vert;
5107 if (player->GameOver) /* do not reanimate dead player */
5110 if (!player_relocated) /* no need to relocate the player */
5113 if (IS_PLAYER(jx, jy)) /* player already placed at new position */
5115 RemoveField(jx, jy); /* temporarily remove newly placed player */
5116 DrawLevelField(jx, jy);
5119 if (player->present)
5121 while (player->MovPos)
5123 ScrollPlayer(player, SCROLL_GO_ON);
5124 ScrollScreen(NULL, SCROLL_GO_ON);
5126 AdvanceFrameAndPlayerCounters(player->index_nr);
5131 Delay(wait_delay_value);
5134 DrawPlayer(player); /* needed here only to cleanup last field */
5135 DrawLevelField(player->jx, player->jy); /* remove player graphic */
5137 player->is_moving = FALSE;
5140 if (IS_CUSTOM_ELEMENT(old_element))
5141 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
5143 player->index_bit, leave_side);
5145 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
5147 player->index_bit, leave_side);
5149 Feld[jx][jy] = el_player;
5150 InitPlayerField(jx, jy, el_player, TRUE);
5152 /* "InitPlayerField()" above sets Feld[jx][jy] to EL_EMPTY, but it may be
5153 possible that the relocation target field did not contain a player element,
5154 but a walkable element, to which the new player was relocated -- in this
5155 case, restore that (already initialized!) element on the player field */
5156 if (!ELEM_IS_PLAYER(element)) /* player may be set on walkable element */
5158 Feld[jx][jy] = element; /* restore previously existing element */
5161 /* only visually relocate centered player */
5162 DrawRelocateScreen(old_jx, old_jy, player->jx, player->jy, player->MovDir,
5163 FALSE, level.instant_relocation);
5165 TestIfPlayerTouchesBadThing(jx, jy);
5166 TestIfPlayerTouchesCustomElement(jx, jy);
5168 if (IS_CUSTOM_ELEMENT(element))
5169 CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
5170 player->index_bit, enter_side);
5172 CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
5173 player->index_bit, enter_side);
5175 if (player->is_switching)
5177 /* ensure that relocation while still switching an element does not cause
5178 a new element to be treated as also switched directly after relocation
5179 (this is important for teleporter switches that teleport the player to
5180 a place where another teleporter switch is in the same direction, which
5181 would then incorrectly be treated as immediately switched before the
5182 direction key that caused the switch was released) */
5184 player->switch_x += jx - old_jx;
5185 player->switch_y += jy - old_jy;
5189 void Explode(int ex, int ey, int phase, int mode)
5195 /* !!! eliminate this variable !!! */
5196 int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
5198 if (game.explosions_delayed)
5200 ExplodeField[ex][ey] = mode;
5204 if (phase == EX_PHASE_START) /* initialize 'Store[][]' field */
5206 int center_element = Feld[ex][ey];
5207 int artwork_element, explosion_element; /* set these values later */
5209 /* remove things displayed in background while burning dynamite */
5210 if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
5213 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
5215 /* put moving element to center field (and let it explode there) */
5216 center_element = MovingOrBlocked2Element(ex, ey);
5217 RemoveMovingField(ex, ey);
5218 Feld[ex][ey] = center_element;
5221 /* now "center_element" is finally determined -- set related values now */
5222 artwork_element = center_element; /* for custom player artwork */
5223 explosion_element = center_element; /* for custom player artwork */
5225 if (IS_PLAYER(ex, ey))
5227 int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
5229 artwork_element = stored_player[player_nr].artwork_element;
5231 if (level.use_explosion_element[player_nr])
5233 explosion_element = level.explosion_element[player_nr];
5234 artwork_element = explosion_element;
5238 if (mode == EX_TYPE_NORMAL ||
5239 mode == EX_TYPE_CENTER ||
5240 mode == EX_TYPE_CROSS)
5241 PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
5243 last_phase = element_info[explosion_element].explosion_delay + 1;
5245 for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
5247 int xx = x - ex + 1;
5248 int yy = y - ey + 1;
5251 if (!IN_LEV_FIELD(x, y) ||
5252 (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
5253 (mode == EX_TYPE_CROSS && (x != ex && y != ey)))
5256 element = Feld[x][y];
5258 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
5260 element = MovingOrBlocked2Element(x, y);
5262 if (!IS_EXPLOSION_PROOF(element))
5263 RemoveMovingField(x, y);
5266 /* indestructible elements can only explode in center (but not flames) */
5267 if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
5268 mode == EX_TYPE_BORDER)) ||
5269 element == EL_FLAMES)
5272 /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
5273 behaviour, for example when touching a yamyam that explodes to rocks
5274 with active deadly shield, a rock is created under the player !!! */
5275 /* (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8) */
5277 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
5278 (game.engine_version < VERSION_IDENT(3,1,0,0) ||
5279 (x == ex && y == ey && mode != EX_TYPE_BORDER)))
5281 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
5284 if (IS_ACTIVE_BOMB(element))
5286 /* re-activate things under the bomb like gate or penguin */
5287 Feld[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
5294 /* save walkable background elements while explosion on same tile */
5295 if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
5296 (x != ex || y != ey || mode == EX_TYPE_BORDER))
5297 Back[x][y] = element;
5299 /* ignite explodable elements reached by other explosion */
5300 if (element == EL_EXPLOSION)
5301 element = Store2[x][y];
5303 if (AmoebaNr[x][y] &&
5304 (element == EL_AMOEBA_FULL ||
5305 element == EL_BD_AMOEBA ||
5306 element == EL_AMOEBA_GROWING))
5308 AmoebaCnt[AmoebaNr[x][y]]--;
5309 AmoebaCnt2[AmoebaNr[x][y]]--;
5314 if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
5316 int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
5318 Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
5320 if (PLAYERINFO(ex, ey)->use_murphy)
5321 Store[x][y] = EL_EMPTY;
5324 /* !!! check this case -- currently needed for rnd_rado_negundo_v,
5325 !!! levels 015 018 019 020 021 022 023 026 027 028 !!! */
5326 else if (ELEM_IS_PLAYER(center_element))
5327 Store[x][y] = EL_EMPTY;
5328 else if (center_element == EL_YAMYAM)
5329 Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
5330 else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
5331 Store[x][y] = element_info[center_element].content.e[xx][yy];
5333 /* needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
5334 (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
5335 otherwise) -- FIX THIS !!! */
5336 else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
5337 Store[x][y] = element_info[element].content.e[1][1];
5339 else if (!CAN_EXPLODE(element))
5340 Store[x][y] = element_info[element].content.e[1][1];
5343 Store[x][y] = EL_EMPTY;
5345 if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
5346 center_element == EL_AMOEBA_TO_DIAMOND)
5347 Store2[x][y] = element;
5349 Feld[x][y] = EL_EXPLOSION;
5350 GfxElement[x][y] = artwork_element;
5352 ExplodePhase[x][y] = 1;
5353 ExplodeDelay[x][y] = last_phase;
5358 if (center_element == EL_YAMYAM)
5359 game.yamyam_content_nr =
5360 (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
5372 GfxFrame[x][y] = 0; /* restart explosion animation */
5374 last_phase = ExplodeDelay[x][y];
5376 ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
5378 /* this can happen if the player leaves an explosion just in time */
5379 if (GfxElement[x][y] == EL_UNDEFINED)
5380 GfxElement[x][y] = EL_EMPTY;
5382 border_element = Store2[x][y];
5383 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5384 border_element = StorePlayer[x][y];
5386 if (phase == element_info[border_element].ignition_delay ||
5387 phase == last_phase)
5389 boolean border_explosion = FALSE;
5391 if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
5392 !PLAYER_EXPLOSION_PROTECTED(x, y))
5394 KillPlayerUnlessExplosionProtected(x, y);
5395 border_explosion = TRUE;
5397 else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
5399 Feld[x][y] = Store2[x][y];
5402 border_explosion = TRUE;
5404 else if (border_element == EL_AMOEBA_TO_DIAMOND)
5406 AmoebeUmwandeln(x, y);
5408 border_explosion = TRUE;
5411 /* if an element just explodes due to another explosion (chain-reaction),
5412 do not immediately end the new explosion when it was the last frame of
5413 the explosion (as it would be done in the following "if"-statement!) */
5414 if (border_explosion && phase == last_phase)
5418 if (phase == last_phase)
5422 element = Feld[x][y] = Store[x][y];
5423 Store[x][y] = Store2[x][y] = 0;
5424 GfxElement[x][y] = EL_UNDEFINED;
5426 /* player can escape from explosions and might therefore be still alive */
5427 if (element >= EL_PLAYER_IS_EXPLODING_1 &&
5428 element <= EL_PLAYER_IS_EXPLODING_4)
5430 int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
5431 int explosion_element = EL_PLAYER_1 + player_nr;
5432 int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
5433 int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
5435 if (level.use_explosion_element[player_nr])
5436 explosion_element = level.explosion_element[player_nr];
5438 Feld[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
5439 element_info[explosion_element].content.e[xx][yy]);
5442 /* restore probably existing indestructible background element */
5443 if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
5444 element = Feld[x][y] = Back[x][y];
5447 MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
5448 GfxDir[x][y] = MV_NONE;
5449 ChangeDelay[x][y] = 0;
5450 ChangePage[x][y] = -1;
5452 CustomValue[x][y] = 0;
5454 InitField_WithBug2(x, y, FALSE);
5456 TEST_DrawLevelField(x, y);
5458 TestIfElementTouchesCustomElement(x, y);
5460 if (GFX_CRUMBLED(element))
5461 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
5463 if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
5464 StorePlayer[x][y] = 0;
5466 if (ELEM_IS_PLAYER(element))
5467 RelocatePlayer(x, y, element);
5469 else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
5471 int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
5472 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5475 TEST_DrawLevelFieldCrumbled(x, y);
5477 if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
5479 DrawLevelElement(x, y, Back[x][y]);
5480 DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
5482 else if (IS_WALKABLE_UNDER(Back[x][y]))
5484 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5485 DrawLevelElementThruMask(x, y, Back[x][y]);
5487 else if (!IS_WALKABLE_INSIDE(Back[x][y]))
5488 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5492 void DynaExplode(int ex, int ey)
5495 int dynabomb_element = Feld[ex][ey];
5496 int dynabomb_size = 1;
5497 boolean dynabomb_xl = FALSE;
5498 struct PlayerInfo *player;
5499 static int xy[4][2] =
5507 if (IS_ACTIVE_BOMB(dynabomb_element))
5509 player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
5510 dynabomb_size = player->dynabomb_size;
5511 dynabomb_xl = player->dynabomb_xl;
5512 player->dynabombs_left++;
5515 Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
5517 for (i = 0; i < NUM_DIRECTIONS; i++)
5519 for (j = 1; j <= dynabomb_size; j++)
5521 int x = ex + j * xy[i][0];
5522 int y = ey + j * xy[i][1];
5525 if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
5528 element = Feld[x][y];
5530 /* do not restart explosions of fields with active bombs */
5531 if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
5534 Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
5536 if (element != EL_EMPTY && element != EL_EXPLOSION &&
5537 !IS_DIGGABLE(element) && !dynabomb_xl)
5543 void Bang(int x, int y)
5545 int element = MovingOrBlocked2Element(x, y);
5546 int explosion_type = EX_TYPE_NORMAL;
5548 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5550 struct PlayerInfo *player = PLAYERINFO(x, y);
5552 element = Feld[x][y] = player->initial_element;
5554 if (level.use_explosion_element[player->index_nr])
5556 int explosion_element = level.explosion_element[player->index_nr];
5558 if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
5559 explosion_type = EX_TYPE_CROSS;
5560 else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
5561 explosion_type = EX_TYPE_CENTER;
5569 case EL_BD_BUTTERFLY:
5572 case EL_DARK_YAMYAM:
5576 RaiseScoreElement(element);
5579 case EL_DYNABOMB_PLAYER_1_ACTIVE:
5580 case EL_DYNABOMB_PLAYER_2_ACTIVE:
5581 case EL_DYNABOMB_PLAYER_3_ACTIVE:
5582 case EL_DYNABOMB_PLAYER_4_ACTIVE:
5583 case EL_DYNABOMB_INCREASE_NUMBER:
5584 case EL_DYNABOMB_INCREASE_SIZE:
5585 case EL_DYNABOMB_INCREASE_POWER:
5586 explosion_type = EX_TYPE_DYNA;
5589 case EL_DC_LANDMINE:
5590 explosion_type = EX_TYPE_CENTER;
5595 case EL_LAMP_ACTIVE:
5596 case EL_AMOEBA_TO_DIAMOND:
5597 if (!IS_PLAYER(x, y)) /* penguin and player may be at same field */
5598 explosion_type = EX_TYPE_CENTER;
5602 if (element_info[element].explosion_type == EXPLODES_CROSS)
5603 explosion_type = EX_TYPE_CROSS;
5604 else if (element_info[element].explosion_type == EXPLODES_1X1)
5605 explosion_type = EX_TYPE_CENTER;
5609 if (explosion_type == EX_TYPE_DYNA)
5612 Explode(x, y, EX_PHASE_START, explosion_type);
5614 CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
5617 void SplashAcid(int x, int y)
5619 if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
5620 (!IN_LEV_FIELD(x - 1, y - 2) ||
5621 !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
5622 Feld[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
5624 if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
5625 (!IN_LEV_FIELD(x + 1, y - 2) ||
5626 !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
5627 Feld[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
5629 PlayLevelSound(x, y, SND_ACID_SPLASHING);
5632 static void InitBeltMovement()
5634 static int belt_base_element[4] =
5636 EL_CONVEYOR_BELT_1_LEFT,
5637 EL_CONVEYOR_BELT_2_LEFT,
5638 EL_CONVEYOR_BELT_3_LEFT,
5639 EL_CONVEYOR_BELT_4_LEFT
5641 static int belt_base_active_element[4] =
5643 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
5644 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
5645 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
5646 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
5651 /* set frame order for belt animation graphic according to belt direction */
5652 for (i = 0; i < NUM_BELTS; i++)
5656 for (j = 0; j < NUM_BELT_PARTS; j++)
5658 int element = belt_base_active_element[belt_nr] + j;
5659 int graphic_1 = el2img(element);
5660 int graphic_2 = el2panelimg(element);
5662 if (game.belt_dir[i] == MV_LEFT)
5664 graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
5665 graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
5669 graphic_info[graphic_1].anim_mode |= ANIM_REVERSE;
5670 graphic_info[graphic_2].anim_mode |= ANIM_REVERSE;
5675 SCAN_PLAYFIELD(x, y)
5677 int element = Feld[x][y];
5679 for (i = 0; i < NUM_BELTS; i++)
5681 if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
5683 int e_belt_nr = getBeltNrFromBeltElement(element);
5686 if (e_belt_nr == belt_nr)
5688 int belt_part = Feld[x][y] - belt_base_element[belt_nr];
5690 Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
5697 static void ToggleBeltSwitch(int x, int y)
5699 static int belt_base_element[4] =
5701 EL_CONVEYOR_BELT_1_LEFT,
5702 EL_CONVEYOR_BELT_2_LEFT,
5703 EL_CONVEYOR_BELT_3_LEFT,
5704 EL_CONVEYOR_BELT_4_LEFT
5706 static int belt_base_active_element[4] =
5708 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
5709 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
5710 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
5711 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
5713 static int belt_base_switch_element[4] =
5715 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
5716 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
5717 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
5718 EL_CONVEYOR_BELT_4_SWITCH_LEFT
5720 static int belt_move_dir[4] =
5728 int element = Feld[x][y];
5729 int belt_nr = getBeltNrFromBeltSwitchElement(element);
5730 int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
5731 int belt_dir = belt_move_dir[belt_dir_nr];
5734 if (!IS_BELT_SWITCH(element))
5737 game.belt_dir_nr[belt_nr] = belt_dir_nr;
5738 game.belt_dir[belt_nr] = belt_dir;
5740 if (belt_dir_nr == 3)
5743 /* set frame order for belt animation graphic according to belt direction */
5744 for (i = 0; i < NUM_BELT_PARTS; i++)
5746 int element = belt_base_active_element[belt_nr] + i;
5747 int graphic_1 = el2img(element);
5748 int graphic_2 = el2panelimg(element);
5750 if (belt_dir == MV_LEFT)
5752 graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
5753 graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
5757 graphic_info[graphic_1].anim_mode |= ANIM_REVERSE;
5758 graphic_info[graphic_2].anim_mode |= ANIM_REVERSE;
5762 SCAN_PLAYFIELD(xx, yy)
5764 int element = Feld[xx][yy];
5766 if (IS_BELT_SWITCH(element))
5768 int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
5770 if (e_belt_nr == belt_nr)
5772 Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
5773 TEST_DrawLevelField(xx, yy);
5776 else if (IS_BELT(element) && belt_dir != MV_NONE)
5778 int e_belt_nr = getBeltNrFromBeltElement(element);
5780 if (e_belt_nr == belt_nr)
5782 int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
5784 Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
5785 TEST_DrawLevelField(xx, yy);
5788 else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
5790 int e_belt_nr = getBeltNrFromBeltActiveElement(element);
5792 if (e_belt_nr == belt_nr)
5794 int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
5796 Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
5797 TEST_DrawLevelField(xx, yy);
5803 static void ToggleSwitchgateSwitch(int x, int y)
5807 game.switchgate_pos = !game.switchgate_pos;
5809 SCAN_PLAYFIELD(xx, yy)
5811 int element = Feld[xx][yy];
5813 if (element == EL_SWITCHGATE_SWITCH_UP)
5815 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_DOWN;
5816 TEST_DrawLevelField(xx, yy);
5818 else if (element == EL_SWITCHGATE_SWITCH_DOWN)
5820 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP;
5821 TEST_DrawLevelField(xx, yy);
5823 else if (element == EL_DC_SWITCHGATE_SWITCH_UP)
5825 Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_DOWN;
5826 TEST_DrawLevelField(xx, yy);
5828 else if (element == EL_DC_SWITCHGATE_SWITCH_DOWN)
5830 Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP;
5831 TEST_DrawLevelField(xx, yy);
5833 else if (element == EL_SWITCHGATE_OPEN ||
5834 element == EL_SWITCHGATE_OPENING)
5836 Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
5838 PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
5840 else if (element == EL_SWITCHGATE_CLOSED ||
5841 element == EL_SWITCHGATE_CLOSING)
5843 Feld[xx][yy] = EL_SWITCHGATE_OPENING;
5845 PlayLevelSoundAction(xx, yy, ACTION_OPENING);
5850 static int getInvisibleActiveFromInvisibleElement(int element)
5852 return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
5853 element == EL_INVISIBLE_WALL ? EL_INVISIBLE_WALL_ACTIVE :
5854 element == EL_INVISIBLE_SAND ? EL_INVISIBLE_SAND_ACTIVE :
5858 static int getInvisibleFromInvisibleActiveElement(int element)
5860 return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
5861 element == EL_INVISIBLE_WALL_ACTIVE ? EL_INVISIBLE_WALL :
5862 element == EL_INVISIBLE_SAND_ACTIVE ? EL_INVISIBLE_SAND :
5866 static void RedrawAllLightSwitchesAndInvisibleElements()
5870 SCAN_PLAYFIELD(x, y)
5872 int element = Feld[x][y];
5874 if (element == EL_LIGHT_SWITCH &&
5875 game.light_time_left > 0)
5877 Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
5878 TEST_DrawLevelField(x, y);
5880 else if (element == EL_LIGHT_SWITCH_ACTIVE &&
5881 game.light_time_left == 0)
5883 Feld[x][y] = EL_LIGHT_SWITCH;
5884 TEST_DrawLevelField(x, y);
5886 else if (element == EL_EMC_DRIPPER &&
5887 game.light_time_left > 0)
5889 Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
5890 TEST_DrawLevelField(x, y);
5892 else if (element == EL_EMC_DRIPPER_ACTIVE &&
5893 game.light_time_left == 0)
5895 Feld[x][y] = EL_EMC_DRIPPER;
5896 TEST_DrawLevelField(x, y);
5898 else if (element == EL_INVISIBLE_STEELWALL ||
5899 element == EL_INVISIBLE_WALL ||
5900 element == EL_INVISIBLE_SAND)
5902 if (game.light_time_left > 0)
5903 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
5905 TEST_DrawLevelField(x, y);
5907 /* uncrumble neighbour fields, if needed */
5908 if (element == EL_INVISIBLE_SAND)
5909 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
5911 else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
5912 element == EL_INVISIBLE_WALL_ACTIVE ||
5913 element == EL_INVISIBLE_SAND_ACTIVE)
5915 if (game.light_time_left == 0)
5916 Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
5918 TEST_DrawLevelField(x, y);
5920 /* re-crumble neighbour fields, if needed */
5921 if (element == EL_INVISIBLE_SAND)
5922 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
5927 static void RedrawAllInvisibleElementsForLenses()
5931 SCAN_PLAYFIELD(x, y)
5933 int element = Feld[x][y];
5935 if (element == EL_EMC_DRIPPER &&
5936 game.lenses_time_left > 0)
5938 Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
5939 TEST_DrawLevelField(x, y);
5941 else if (element == EL_EMC_DRIPPER_ACTIVE &&
5942 game.lenses_time_left == 0)
5944 Feld[x][y] = EL_EMC_DRIPPER;
5945 TEST_DrawLevelField(x, y);
5947 else if (element == EL_INVISIBLE_STEELWALL ||
5948 element == EL_INVISIBLE_WALL ||
5949 element == EL_INVISIBLE_SAND)
5951 if (game.lenses_time_left > 0)
5952 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
5954 TEST_DrawLevelField(x, y);
5956 /* uncrumble neighbour fields, if needed */
5957 if (element == EL_INVISIBLE_SAND)
5958 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
5960 else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
5961 element == EL_INVISIBLE_WALL_ACTIVE ||
5962 element == EL_INVISIBLE_SAND_ACTIVE)
5964 if (game.lenses_time_left == 0)
5965 Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
5967 TEST_DrawLevelField(x, y);
5969 /* re-crumble neighbour fields, if needed */
5970 if (element == EL_INVISIBLE_SAND)
5971 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
5976 static void RedrawAllInvisibleElementsForMagnifier()
5980 SCAN_PLAYFIELD(x, y)
5982 int element = Feld[x][y];
5984 if (element == EL_EMC_FAKE_GRASS &&
5985 game.magnify_time_left > 0)
5987 Feld[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
5988 TEST_DrawLevelField(x, y);
5990 else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
5991 game.magnify_time_left == 0)
5993 Feld[x][y] = EL_EMC_FAKE_GRASS;
5994 TEST_DrawLevelField(x, y);
5996 else if (IS_GATE_GRAY(element) &&
5997 game.magnify_time_left > 0)
5999 Feld[x][y] = (IS_RND_GATE_GRAY(element) ?
6000 element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
6001 IS_EM_GATE_GRAY(element) ?
6002 element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
6003 IS_EMC_GATE_GRAY(element) ?
6004 element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
6005 IS_DC_GATE_GRAY(element) ?
6006 EL_DC_GATE_WHITE_GRAY_ACTIVE :
6008 TEST_DrawLevelField(x, y);
6010 else if (IS_GATE_GRAY_ACTIVE(element) &&
6011 game.magnify_time_left == 0)
6013 Feld[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
6014 element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
6015 IS_EM_GATE_GRAY_ACTIVE(element) ?
6016 element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
6017 IS_EMC_GATE_GRAY_ACTIVE(element) ?
6018 element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
6019 IS_DC_GATE_GRAY_ACTIVE(element) ?
6020 EL_DC_GATE_WHITE_GRAY :
6022 TEST_DrawLevelField(x, y);
6027 static void ToggleLightSwitch(int x, int y)
6029 int element = Feld[x][y];
6031 game.light_time_left =
6032 (element == EL_LIGHT_SWITCH ?
6033 level.time_light * FRAMES_PER_SECOND : 0);
6035 RedrawAllLightSwitchesAndInvisibleElements();
6038 static void ActivateTimegateSwitch(int x, int y)
6042 game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
6044 SCAN_PLAYFIELD(xx, yy)
6046 int element = Feld[xx][yy];
6048 if (element == EL_TIMEGATE_CLOSED ||
6049 element == EL_TIMEGATE_CLOSING)
6051 Feld[xx][yy] = EL_TIMEGATE_OPENING;
6052 PlayLevelSound(xx, yy, SND_CLASS_TIMEGATE_OPENING);
6056 else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
6058 Feld[xx][yy] = EL_TIMEGATE_SWITCH;
6059 TEST_DrawLevelField(xx, yy);
6065 Feld[x][y] = (Feld[x][y] == EL_TIMEGATE_SWITCH ? EL_TIMEGATE_SWITCH_ACTIVE :
6066 EL_DC_TIMEGATE_SWITCH_ACTIVE);
6069 void Impact(int x, int y)
6071 boolean last_line = (y == lev_fieldy - 1);
6072 boolean object_hit = FALSE;
6073 boolean impact = (last_line || object_hit);
6074 int element = Feld[x][y];
6075 int smashed = EL_STEELWALL;
6077 if (!last_line) /* check if element below was hit */
6079 if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
6082 object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
6083 MovDir[x][y + 1] != MV_DOWN ||
6084 MovPos[x][y + 1] <= TILEY / 2));
6086 /* do not smash moving elements that left the smashed field in time */
6087 if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
6088 ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
6091 #if USE_QUICKSAND_IMPACT_BUGFIX
6092 if (Feld[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
6094 RemoveMovingField(x, y + 1);
6095 Feld[x][y + 1] = EL_QUICKSAND_EMPTY;
6096 Feld[x][y + 2] = EL_ROCK;
6097 TEST_DrawLevelField(x, y + 2);
6102 if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTYING && object_hit == FALSE)
6104 RemoveMovingField(x, y + 1);
6105 Feld[x][y + 1] = EL_QUICKSAND_FAST_EMPTY;
6106 Feld[x][y + 2] = EL_ROCK;
6107 TEST_DrawLevelField(x, y + 2);
6114 smashed = MovingOrBlocked2Element(x, y + 1);
6116 impact = (last_line || object_hit);
6119 if (!last_line && smashed == EL_ACID) /* element falls into acid */
6121 SplashAcid(x, y + 1);
6125 /* !!! not sufficient for all cases -- see EL_PEARL below !!! */
6126 /* only reset graphic animation if graphic really changes after impact */
6128 el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
6130 ResetGfxAnimation(x, y);
6131 TEST_DrawLevelField(x, y);
6134 if (impact && CAN_EXPLODE_IMPACT(element))
6139 else if (impact && element == EL_PEARL &&
6140 smashed != EL_DC_MAGIC_WALL && smashed != EL_DC_MAGIC_WALL_ACTIVE)
6142 ResetGfxAnimation(x, y);
6144 Feld[x][y] = EL_PEARL_BREAKING;
6145 PlayLevelSound(x, y, SND_PEARL_BREAKING);
6148 else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
6150 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6155 if (impact && element == EL_AMOEBA_DROP)
6157 if (object_hit && IS_PLAYER(x, y + 1))
6158 KillPlayerUnlessEnemyProtected(x, y + 1);
6159 else if (object_hit && smashed == EL_PENGUIN)
6163 Feld[x][y] = EL_AMOEBA_GROWING;
6164 Store[x][y] = EL_AMOEBA_WET;
6166 ResetRandomAnimationValue(x, y);
6171 if (object_hit) /* check which object was hit */
6173 if ((CAN_PASS_MAGIC_WALL(element) &&
6174 (smashed == EL_MAGIC_WALL ||
6175 smashed == EL_BD_MAGIC_WALL)) ||
6176 (CAN_PASS_DC_MAGIC_WALL(element) &&
6177 smashed == EL_DC_MAGIC_WALL))
6180 int activated_magic_wall =
6181 (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
6182 smashed == EL_BD_MAGIC_WALL ? EL_BD_MAGIC_WALL_ACTIVE :
6183 EL_DC_MAGIC_WALL_ACTIVE);
6185 /* activate magic wall / mill */
6186 SCAN_PLAYFIELD(xx, yy)
6188 if (Feld[xx][yy] == smashed)
6189 Feld[xx][yy] = activated_magic_wall;
6192 game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
6193 game.magic_wall_active = TRUE;
6195 PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
6196 SND_MAGIC_WALL_ACTIVATING :
6197 smashed == EL_BD_MAGIC_WALL ?
6198 SND_BD_MAGIC_WALL_ACTIVATING :
6199 SND_DC_MAGIC_WALL_ACTIVATING));
6202 if (IS_PLAYER(x, y + 1))
6204 if (CAN_SMASH_PLAYER(element))
6206 KillPlayerUnlessEnemyProtected(x, y + 1);
6210 else if (smashed == EL_PENGUIN)
6212 if (CAN_SMASH_PLAYER(element))
6218 else if (element == EL_BD_DIAMOND)
6220 if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
6226 else if (((element == EL_SP_INFOTRON ||
6227 element == EL_SP_ZONK) &&
6228 (smashed == EL_SP_SNIKSNAK ||
6229 smashed == EL_SP_ELECTRON ||
6230 smashed == EL_SP_DISK_ORANGE)) ||
6231 (element == EL_SP_INFOTRON &&
6232 smashed == EL_SP_DISK_YELLOW))
6237 else if (CAN_SMASH_EVERYTHING(element))
6239 if (IS_CLASSIC_ENEMY(smashed) ||
6240 CAN_EXPLODE_SMASHED(smashed))
6245 else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
6247 if (smashed == EL_LAMP ||
6248 smashed == EL_LAMP_ACTIVE)
6253 else if (smashed == EL_NUT)
6255 Feld[x][y + 1] = EL_NUT_BREAKING;
6256 PlayLevelSound(x, y, SND_NUT_BREAKING);
6257 RaiseScoreElement(EL_NUT);
6260 else if (smashed == EL_PEARL)
6262 ResetGfxAnimation(x, y);
6264 Feld[x][y + 1] = EL_PEARL_BREAKING;
6265 PlayLevelSound(x, y, SND_PEARL_BREAKING);
6268 else if (smashed == EL_DIAMOND)
6270 Feld[x][y + 1] = EL_DIAMOND_BREAKING;
6271 PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
6274 else if (IS_BELT_SWITCH(smashed))
6276 ToggleBeltSwitch(x, y + 1);
6278 else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
6279 smashed == EL_SWITCHGATE_SWITCH_DOWN ||
6280 smashed == EL_DC_SWITCHGATE_SWITCH_UP ||
6281 smashed == EL_DC_SWITCHGATE_SWITCH_DOWN)
6283 ToggleSwitchgateSwitch(x, y + 1);
6285 else if (smashed == EL_LIGHT_SWITCH ||
6286 smashed == EL_LIGHT_SWITCH_ACTIVE)
6288 ToggleLightSwitch(x, y + 1);
6292 CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6294 CheckElementChangeBySide(x, y + 1, smashed, element,
6295 CE_SWITCHED, CH_SIDE_TOP);
6296 CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
6302 CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6307 /* play sound of magic wall / mill */
6309 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
6310 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ||
6311 Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE))
6313 if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
6314 PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
6315 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
6316 PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
6317 else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
6318 PlayLevelSound(x, y, SND_DC_MAGIC_WALL_FILLING);
6323 /* play sound of object that hits the ground */
6324 if (last_line || object_hit)
6325 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6328 inline static void TurnRoundExt(int x, int y)
6340 { 0, 0 }, { 0, 0 }, { 0, 0 },
6345 int left, right, back;
6349 { MV_DOWN, MV_UP, MV_RIGHT },
6350 { MV_UP, MV_DOWN, MV_LEFT },
6352 { MV_LEFT, MV_RIGHT, MV_DOWN },
6356 { MV_RIGHT, MV_LEFT, MV_UP }
6359 int element = Feld[x][y];
6360 int move_pattern = element_info[element].move_pattern;
6362 int old_move_dir = MovDir[x][y];
6363 int left_dir = turn[old_move_dir].left;
6364 int right_dir = turn[old_move_dir].right;
6365 int back_dir = turn[old_move_dir].back;
6367 int left_dx = move_xy[left_dir].dx, left_dy = move_xy[left_dir].dy;
6368 int right_dx = move_xy[right_dir].dx, right_dy = move_xy[right_dir].dy;
6369 int move_dx = move_xy[old_move_dir].dx, move_dy = move_xy[old_move_dir].dy;
6370 int back_dx = move_xy[back_dir].dx, back_dy = move_xy[back_dir].dy;
6372 int left_x = x + left_dx, left_y = y + left_dy;
6373 int right_x = x + right_dx, right_y = y + right_dy;
6374 int move_x = x + move_dx, move_y = y + move_dy;
6378 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
6380 TestIfBadThingTouchesOtherBadThing(x, y);
6382 if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
6383 MovDir[x][y] = right_dir;
6384 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6385 MovDir[x][y] = left_dir;
6387 if (element == EL_BUG && MovDir[x][y] != old_move_dir)
6389 else if (element == EL_BD_BUTTERFLY) /* && MovDir[x][y] == left_dir) */
6392 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
6394 TestIfBadThingTouchesOtherBadThing(x, y);
6396 if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
6397 MovDir[x][y] = left_dir;
6398 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6399 MovDir[x][y] = right_dir;
6401 if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
6403 else if (element == EL_BD_FIREFLY) /* && MovDir[x][y] == right_dir) */
6406 else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
6408 TestIfBadThingTouchesOtherBadThing(x, y);
6410 if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
6411 MovDir[x][y] = left_dir;
6412 else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
6413 MovDir[x][y] = right_dir;
6415 if (MovDir[x][y] != old_move_dir)
6418 else if (element == EL_YAMYAM)
6420 boolean can_turn_left = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
6421 boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
6423 if (can_turn_left && can_turn_right)
6424 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6425 else if (can_turn_left)
6426 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6427 else if (can_turn_right)
6428 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6430 MovDir[x][y] = back_dir;
6432 MovDelay[x][y] = 16 + 16 * RND(3);
6434 else if (element == EL_DARK_YAMYAM)
6436 boolean can_turn_left = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6438 boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6441 if (can_turn_left && can_turn_right)
6442 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6443 else if (can_turn_left)
6444 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6445 else if (can_turn_right)
6446 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6448 MovDir[x][y] = back_dir;
6450 MovDelay[x][y] = 16 + 16 * RND(3);
6452 else if (element == EL_PACMAN)
6454 boolean can_turn_left = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
6455 boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
6457 if (can_turn_left && can_turn_right)
6458 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6459 else if (can_turn_left)
6460 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6461 else if (can_turn_right)
6462 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6464 MovDir[x][y] = back_dir;
6466 MovDelay[x][y] = 6 + RND(40);
6468 else if (element == EL_PIG)
6470 boolean can_turn_left = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
6471 boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
6472 boolean can_move_on = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
6473 boolean should_turn_left, should_turn_right, should_move_on;
6475 int rnd = RND(rnd_value);
6477 should_turn_left = (can_turn_left &&
6479 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
6480 y + back_dy + left_dy)));
6481 should_turn_right = (can_turn_right &&
6483 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
6484 y + back_dy + right_dy)));
6485 should_move_on = (can_move_on &&
6488 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
6489 y + move_dy + left_dy) ||
6490 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
6491 y + move_dy + right_dy)));
6493 if (should_turn_left || should_turn_right || should_move_on)
6495 if (should_turn_left && should_turn_right && should_move_on)
6496 MovDir[x][y] = (rnd < rnd_value / 3 ? left_dir :
6497 rnd < 2 * rnd_value / 3 ? right_dir :
6499 else if (should_turn_left && should_turn_right)
6500 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6501 else if (should_turn_left && should_move_on)
6502 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
6503 else if (should_turn_right && should_move_on)
6504 MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
6505 else if (should_turn_left)
6506 MovDir[x][y] = left_dir;
6507 else if (should_turn_right)
6508 MovDir[x][y] = right_dir;
6509 else if (should_move_on)
6510 MovDir[x][y] = old_move_dir;
6512 else if (can_move_on && rnd > rnd_value / 8)
6513 MovDir[x][y] = old_move_dir;
6514 else if (can_turn_left && can_turn_right)
6515 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6516 else if (can_turn_left && rnd > rnd_value / 8)
6517 MovDir[x][y] = left_dir;
6518 else if (can_turn_right && rnd > rnd_value/8)
6519 MovDir[x][y] = right_dir;
6521 MovDir[x][y] = back_dir;
6523 xx = x + move_xy[MovDir[x][y]].dx;
6524 yy = y + move_xy[MovDir[x][y]].dy;
6526 if (!IN_LEV_FIELD(xx, yy) ||
6527 (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy])))
6528 MovDir[x][y] = old_move_dir;
6532 else if (element == EL_DRAGON)
6534 boolean can_turn_left = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
6535 boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
6536 boolean can_move_on = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
6538 int rnd = RND(rnd_value);
6540 if (can_move_on && rnd > rnd_value / 8)
6541 MovDir[x][y] = old_move_dir;
6542 else if (can_turn_left && can_turn_right)
6543 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6544 else if (can_turn_left && rnd > rnd_value / 8)
6545 MovDir[x][y] = left_dir;
6546 else if (can_turn_right && rnd > rnd_value / 8)
6547 MovDir[x][y] = right_dir;
6549 MovDir[x][y] = back_dir;
6551 xx = x + move_xy[MovDir[x][y]].dx;
6552 yy = y + move_xy[MovDir[x][y]].dy;
6554 if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
6555 MovDir[x][y] = old_move_dir;
6559 else if (element == EL_MOLE)
6561 boolean can_move_on =
6562 (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
6563 IS_AMOEBOID(Feld[move_x][move_y]) ||
6564 Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
6567 boolean can_turn_left =
6568 (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
6569 IS_AMOEBOID(Feld[left_x][left_y])));
6571 boolean can_turn_right =
6572 (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
6573 IS_AMOEBOID(Feld[right_x][right_y])));
6575 if (can_turn_left && can_turn_right)
6576 MovDir[x][y] = (RND(2) ? left_dir : right_dir);
6577 else if (can_turn_left)
6578 MovDir[x][y] = left_dir;
6580 MovDir[x][y] = right_dir;
6583 if (MovDir[x][y] != old_move_dir)
6586 else if (element == EL_BALLOON)
6588 MovDir[x][y] = game.wind_direction;
6591 else if (element == EL_SPRING)
6593 if (MovDir[x][y] & MV_HORIZONTAL)
6595 if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
6596 !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
6598 Feld[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
6599 ResetGfxAnimation(move_x, move_y);
6600 TEST_DrawLevelField(move_x, move_y);
6602 MovDir[x][y] = back_dir;
6604 else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
6605 SPRING_CAN_ENTER_FIELD(element, x, y + 1))
6606 MovDir[x][y] = MV_NONE;
6611 else if (element == EL_ROBOT ||
6612 element == EL_SATELLITE ||
6613 element == EL_PENGUIN ||
6614 element == EL_EMC_ANDROID)
6616 int attr_x = -1, attr_y = -1;
6627 for (i = 0; i < MAX_PLAYERS; i++)
6629 struct PlayerInfo *player = &stored_player[i];
6630 int jx = player->jx, jy = player->jy;
6632 if (!player->active)
6636 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
6644 if (element == EL_ROBOT && ZX >= 0 && ZY >= 0 &&
6645 (Feld[ZX][ZY] == EL_ROBOT_WHEEL_ACTIVE ||
6646 game.engine_version < VERSION_IDENT(3,1,0,0)))
6652 if (element == EL_PENGUIN)
6655 static int xy[4][2] =
6663 for (i = 0; i < NUM_DIRECTIONS; i++)
6665 int ex = x + xy[i][0];
6666 int ey = y + xy[i][1];
6668 if (IN_LEV_FIELD(ex, ey) && (Feld[ex][ey] == EL_EXIT_OPEN ||
6669 Feld[ex][ey] == EL_EM_EXIT_OPEN ||
6670 Feld[ex][ey] == EL_STEEL_EXIT_OPEN ||
6671 Feld[ex][ey] == EL_EM_STEEL_EXIT_OPEN))
6680 MovDir[x][y] = MV_NONE;
6682 MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
6683 else if (attr_x > x)
6684 MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
6686 MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
6687 else if (attr_y > y)
6688 MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
6690 if (element == EL_ROBOT)
6694 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6695 MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
6696 Moving2Blocked(x, y, &newx, &newy);
6698 if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
6699 MovDelay[x][y] = 8 + 8 * !RND(3);
6701 MovDelay[x][y] = 16;
6703 else if (element == EL_PENGUIN)
6709 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6711 boolean first_horiz = RND(2);
6712 int new_move_dir = MovDir[x][y];
6715 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6716 Moving2Blocked(x, y, &newx, &newy);
6718 if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
6722 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6723 Moving2Blocked(x, y, &newx, &newy);
6725 if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
6728 MovDir[x][y] = old_move_dir;
6732 else if (element == EL_SATELLITE)
6738 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6740 boolean first_horiz = RND(2);
6741 int new_move_dir = MovDir[x][y];
6744 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6745 Moving2Blocked(x, y, &newx, &newy);
6747 if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
6751 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6752 Moving2Blocked(x, y, &newx, &newy);
6754 if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
6757 MovDir[x][y] = old_move_dir;
6761 else if (element == EL_EMC_ANDROID)
6763 static int check_pos[16] =
6765 -1, /* 0 => (invalid) */
6766 7, /* 1 => MV_LEFT */
6767 3, /* 2 => MV_RIGHT */
6768 -1, /* 3 => (invalid) */
6770 0, /* 5 => MV_LEFT | MV_UP */
6771 2, /* 6 => MV_RIGHT | MV_UP */
6772 -1, /* 7 => (invalid) */
6773 5, /* 8 => MV_DOWN */
6774 6, /* 9 => MV_LEFT | MV_DOWN */
6775 4, /* 10 => MV_RIGHT | MV_DOWN */
6776 -1, /* 11 => (invalid) */
6777 -1, /* 12 => (invalid) */
6778 -1, /* 13 => (invalid) */
6779 -1, /* 14 => (invalid) */
6780 -1, /* 15 => (invalid) */
6788 { -1, -1, MV_LEFT | MV_UP },
6790 { +1, -1, MV_RIGHT | MV_UP },
6791 { +1, 0, MV_RIGHT },
6792 { +1, +1, MV_RIGHT | MV_DOWN },
6794 { -1, +1, MV_LEFT | MV_DOWN },
6797 int start_pos, check_order;
6798 boolean can_clone = FALSE;
6801 /* check if there is any free field around current position */
6802 for (i = 0; i < 8; i++)
6804 int newx = x + check_xy[i].dx;
6805 int newy = y + check_xy[i].dy;
6807 if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
6815 if (can_clone) /* randomly find an element to clone */
6819 start_pos = check_pos[RND(8)];
6820 check_order = (RND(2) ? -1 : +1);
6822 for (i = 0; i < 8; i++)
6824 int pos_raw = start_pos + i * check_order;
6825 int pos = (pos_raw + 8) % 8;
6826 int newx = x + check_xy[pos].dx;
6827 int newy = y + check_xy[pos].dy;
6829 if (ANDROID_CAN_CLONE_FIELD(newx, newy))
6831 element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
6832 element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
6834 Store[x][y] = Feld[newx][newy];
6843 if (can_clone) /* randomly find a direction to move */
6847 start_pos = check_pos[RND(8)];
6848 check_order = (RND(2) ? -1 : +1);
6850 for (i = 0; i < 8; i++)
6852 int pos_raw = start_pos + i * check_order;
6853 int pos = (pos_raw + 8) % 8;
6854 int newx = x + check_xy[pos].dx;
6855 int newy = y + check_xy[pos].dy;
6856 int new_move_dir = check_xy[pos].dir;
6858 if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
6860 MovDir[x][y] = new_move_dir;
6861 MovDelay[x][y] = level.android_clone_time * 8 + 1;
6870 if (can_clone) /* cloning and moving successful */
6873 /* cannot clone -- try to move towards player */
6875 start_pos = check_pos[MovDir[x][y] & 0x0f];
6876 check_order = (RND(2) ? -1 : +1);
6878 for (i = 0; i < 3; i++)
6880 /* first check start_pos, then previous/next or (next/previous) pos */
6881 int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
6882 int pos = (pos_raw + 8) % 8;
6883 int newx = x + check_xy[pos].dx;
6884 int newy = y + check_xy[pos].dy;
6885 int new_move_dir = check_xy[pos].dir;
6887 if (IS_PLAYER(newx, newy))
6890 if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
6892 MovDir[x][y] = new_move_dir;
6893 MovDelay[x][y] = level.android_move_time * 8 + 1;
6900 else if (move_pattern == MV_TURNING_LEFT ||
6901 move_pattern == MV_TURNING_RIGHT ||
6902 move_pattern == MV_TURNING_LEFT_RIGHT ||
6903 move_pattern == MV_TURNING_RIGHT_LEFT ||
6904 move_pattern == MV_TURNING_RANDOM ||
6905 move_pattern == MV_ALL_DIRECTIONS)
6907 boolean can_turn_left =
6908 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
6909 boolean can_turn_right =
6910 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
6912 if (element_info[element].move_stepsize == 0) /* "not moving" */
6915 if (move_pattern == MV_TURNING_LEFT)
6916 MovDir[x][y] = left_dir;
6917 else if (move_pattern == MV_TURNING_RIGHT)
6918 MovDir[x][y] = right_dir;
6919 else if (move_pattern == MV_TURNING_LEFT_RIGHT)
6920 MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
6921 else if (move_pattern == MV_TURNING_RIGHT_LEFT)
6922 MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
6923 else if (move_pattern == MV_TURNING_RANDOM)
6924 MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
6925 can_turn_right && !can_turn_left ? right_dir :
6926 RND(2) ? left_dir : right_dir);
6927 else if (can_turn_left && can_turn_right)
6928 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6929 else if (can_turn_left)
6930 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6931 else if (can_turn_right)
6932 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6934 MovDir[x][y] = back_dir;
6936 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6938 else if (move_pattern == MV_HORIZONTAL ||
6939 move_pattern == MV_VERTICAL)
6941 if (move_pattern & old_move_dir)
6942 MovDir[x][y] = back_dir;
6943 else if (move_pattern == MV_HORIZONTAL)
6944 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
6945 else if (move_pattern == MV_VERTICAL)
6946 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
6948 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6950 else if (move_pattern & MV_ANY_DIRECTION)
6952 MovDir[x][y] = move_pattern;
6953 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6955 else if (move_pattern & MV_WIND_DIRECTION)
6957 MovDir[x][y] = game.wind_direction;
6958 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6960 else if (move_pattern == MV_ALONG_LEFT_SIDE)
6962 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
6963 MovDir[x][y] = left_dir;
6964 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
6965 MovDir[x][y] = right_dir;
6967 if (MovDir[x][y] != old_move_dir)
6968 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6970 else if (move_pattern == MV_ALONG_RIGHT_SIDE)
6972 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
6973 MovDir[x][y] = right_dir;
6974 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
6975 MovDir[x][y] = left_dir;
6977 if (MovDir[x][y] != old_move_dir)
6978 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6980 else if (move_pattern == MV_TOWARDS_PLAYER ||
6981 move_pattern == MV_AWAY_FROM_PLAYER)
6983 int attr_x = -1, attr_y = -1;
6985 boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
6996 for (i = 0; i < MAX_PLAYERS; i++)
6998 struct PlayerInfo *player = &stored_player[i];
6999 int jx = player->jx, jy = player->jy;
7001 if (!player->active)
7005 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7013 MovDir[x][y] = MV_NONE;
7015 MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
7016 else if (attr_x > x)
7017 MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
7019 MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
7020 else if (attr_y > y)
7021 MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
7023 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7025 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7027 boolean first_horiz = RND(2);
7028 int new_move_dir = MovDir[x][y];
7030 if (element_info[element].move_stepsize == 0) /* "not moving" */
7032 first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
7033 MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7039 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7040 Moving2Blocked(x, y, &newx, &newy);
7042 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7046 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7047 Moving2Blocked(x, y, &newx, &newy);
7049 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7052 MovDir[x][y] = old_move_dir;
7055 else if (move_pattern == MV_WHEN_PUSHED ||
7056 move_pattern == MV_WHEN_DROPPED)
7058 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7059 MovDir[x][y] = MV_NONE;
7063 else if (move_pattern & MV_MAZE_RUNNER_STYLE)
7065 static int test_xy[7][2] =
7075 static int test_dir[7] =
7085 boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
7086 int move_preference = -1000000; /* start with very low preference */
7087 int new_move_dir = MV_NONE;
7088 int start_test = RND(4);
7091 for (i = 0; i < NUM_DIRECTIONS; i++)
7093 int move_dir = test_dir[start_test + i];
7094 int move_dir_preference;
7096 xx = x + test_xy[start_test + i][0];
7097 yy = y + test_xy[start_test + i][1];
7099 if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
7100 (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
7102 new_move_dir = move_dir;
7107 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
7110 move_dir_preference = -1 * RunnerVisit[xx][yy];
7111 if (hunter_mode && PlayerVisit[xx][yy] > 0)
7112 move_dir_preference = PlayerVisit[xx][yy];
7114 if (move_dir_preference > move_preference)
7116 /* prefer field that has not been visited for the longest time */
7117 move_preference = move_dir_preference;
7118 new_move_dir = move_dir;
7120 else if (move_dir_preference == move_preference &&
7121 move_dir == old_move_dir)
7123 /* prefer last direction when all directions are preferred equally */
7124 move_preference = move_dir_preference;
7125 new_move_dir = move_dir;
7129 MovDir[x][y] = new_move_dir;
7130 if (old_move_dir != new_move_dir)
7131 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7135 static void TurnRound(int x, int y)
7137 int direction = MovDir[x][y];
7141 GfxDir[x][y] = MovDir[x][y];
7143 if (direction != MovDir[x][y])
7147 GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
7149 ResetGfxFrame(x, y, FALSE);
7152 static boolean JustBeingPushed(int x, int y)
7156 for (i = 0; i < MAX_PLAYERS; i++)
7158 struct PlayerInfo *player = &stored_player[i];
7160 if (player->active && player->is_pushing && player->MovPos)
7162 int next_jx = player->jx + (player->jx - player->last_jx);
7163 int next_jy = player->jy + (player->jy - player->last_jy);
7165 if (x == next_jx && y == next_jy)
7173 void StartMoving(int x, int y)
7175 boolean started_moving = FALSE; /* some elements can fall _and_ move */
7176 int element = Feld[x][y];
7181 if (MovDelay[x][y] == 0)
7182 GfxAction[x][y] = ACTION_DEFAULT;
7184 if (CAN_FALL(element) && y < lev_fieldy - 1)
7186 if ((x > 0 && IS_PLAYER(x - 1, y)) ||
7187 (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
7188 if (JustBeingPushed(x, y))
7191 if (element == EL_QUICKSAND_FULL)
7193 if (IS_FREE(x, y + 1))
7195 InitMovingField(x, y, MV_DOWN);
7196 started_moving = TRUE;
7198 Feld[x][y] = EL_QUICKSAND_EMPTYING;
7199 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7200 if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7201 Store[x][y] = EL_ROCK;
7203 Store[x][y] = EL_ROCK;
7206 PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7208 else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7210 if (!MovDelay[x][y])
7212 MovDelay[x][y] = TILEY + 1;
7214 ResetGfxAnimation(x, y);
7215 ResetGfxAnimation(x, y + 1);
7220 DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7221 DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7228 Feld[x][y] = EL_QUICKSAND_EMPTY;
7229 Feld[x][y + 1] = EL_QUICKSAND_FULL;
7230 Store[x][y + 1] = Store[x][y];
7233 PlayLevelSoundAction(x, y, ACTION_FILLING);
7235 else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7237 if (!MovDelay[x][y])
7239 MovDelay[x][y] = TILEY + 1;
7241 ResetGfxAnimation(x, y);
7242 ResetGfxAnimation(x, y + 1);
7247 DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7248 DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7255 Feld[x][y] = EL_QUICKSAND_EMPTY;
7256 Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7257 Store[x][y + 1] = Store[x][y];
7260 PlayLevelSoundAction(x, y, ACTION_FILLING);
7263 else if (element == EL_QUICKSAND_FAST_FULL)
7265 if (IS_FREE(x, y + 1))
7267 InitMovingField(x, y, MV_DOWN);
7268 started_moving = TRUE;
7270 Feld[x][y] = EL_QUICKSAND_FAST_EMPTYING;
7271 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7272 if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7273 Store[x][y] = EL_ROCK;
7275 Store[x][y] = EL_ROCK;
7278 PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7280 else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7282 if (!MovDelay[x][y])
7284 MovDelay[x][y] = TILEY + 1;
7286 ResetGfxAnimation(x, y);
7287 ResetGfxAnimation(x, y + 1);
7292 DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7293 DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7300 Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
7301 Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7302 Store[x][y + 1] = Store[x][y];
7305 PlayLevelSoundAction(x, y, ACTION_FILLING);
7307 else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7309 if (!MovDelay[x][y])
7311 MovDelay[x][y] = TILEY + 1;
7313 ResetGfxAnimation(x, y);
7314 ResetGfxAnimation(x, y + 1);
7319 DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7320 DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7327 Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
7328 Feld[x][y + 1] = EL_QUICKSAND_FULL;
7329 Store[x][y + 1] = Store[x][y];
7332 PlayLevelSoundAction(x, y, ACTION_FILLING);
7335 else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7336 Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7338 InitMovingField(x, y, MV_DOWN);
7339 started_moving = TRUE;
7341 Feld[x][y] = EL_QUICKSAND_FILLING;
7342 Store[x][y] = element;
7344 PlayLevelSoundAction(x, y, ACTION_FILLING);
7346 else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7347 Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7349 InitMovingField(x, y, MV_DOWN);
7350 started_moving = TRUE;
7352 Feld[x][y] = EL_QUICKSAND_FAST_FILLING;
7353 Store[x][y] = element;
7355 PlayLevelSoundAction(x, y, ACTION_FILLING);
7357 else if (element == EL_MAGIC_WALL_FULL)
7359 if (IS_FREE(x, y + 1))
7361 InitMovingField(x, y, MV_DOWN);
7362 started_moving = TRUE;
7364 Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
7365 Store[x][y] = EL_CHANGED(Store[x][y]);
7367 else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
7369 if (!MovDelay[x][y])
7370 MovDelay[x][y] = TILEY / 4 + 1;
7379 Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
7380 Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
7381 Store[x][y + 1] = EL_CHANGED(Store[x][y]);
7385 else if (element == EL_BD_MAGIC_WALL_FULL)
7387 if (IS_FREE(x, y + 1))
7389 InitMovingField(x, y, MV_DOWN);
7390 started_moving = TRUE;
7392 Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
7393 Store[x][y] = EL_CHANGED_BD(Store[x][y]);
7395 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
7397 if (!MovDelay[x][y])
7398 MovDelay[x][y] = TILEY / 4 + 1;
7407 Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
7408 Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
7409 Store[x][y + 1] = EL_CHANGED_BD(Store[x][y]);
7413 else if (element == EL_DC_MAGIC_WALL_FULL)
7415 if (IS_FREE(x, y + 1))
7417 InitMovingField(x, y, MV_DOWN);
7418 started_moving = TRUE;
7420 Feld[x][y] = EL_DC_MAGIC_WALL_EMPTYING;
7421 Store[x][y] = EL_CHANGED_DC(Store[x][y]);
7423 else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
7425 if (!MovDelay[x][y])
7426 MovDelay[x][y] = TILEY / 4 + 1;
7435 Feld[x][y] = EL_DC_MAGIC_WALL_ACTIVE;
7436 Feld[x][y + 1] = EL_DC_MAGIC_WALL_FULL;
7437 Store[x][y + 1] = EL_CHANGED_DC(Store[x][y]);
7441 else if ((CAN_PASS_MAGIC_WALL(element) &&
7442 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
7443 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)) ||
7444 (CAN_PASS_DC_MAGIC_WALL(element) &&
7445 (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)))
7448 InitMovingField(x, y, MV_DOWN);
7449 started_moving = TRUE;
7452 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
7453 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ? EL_BD_MAGIC_WALL_FILLING :
7454 EL_DC_MAGIC_WALL_FILLING);
7455 Store[x][y] = element;
7457 else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
7459 SplashAcid(x, y + 1);
7461 InitMovingField(x, y, MV_DOWN);
7462 started_moving = TRUE;
7464 Store[x][y] = EL_ACID;
7467 (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7468 CheckImpact[x][y] && !IS_FREE(x, y + 1)) ||
7469 (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
7470 CAN_FALL(element) && WasJustFalling[x][y] &&
7471 (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
7473 (game.engine_version < VERSION_IDENT(2,2,0,7) &&
7474 CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
7475 (Feld[x][y + 1] == EL_BLOCKED)))
7477 /* this is needed for a special case not covered by calling "Impact()"
7478 from "ContinueMoving()": if an element moves to a tile directly below
7479 another element which was just falling on that tile (which was empty
7480 in the previous frame), the falling element above would just stop
7481 instead of smashing the element below (in previous version, the above
7482 element was just checked for "moving" instead of "falling", resulting
7483 in incorrect smashes caused by horizontal movement of the above
7484 element; also, the case of the player being the element to smash was
7485 simply not covered here... :-/ ) */
7487 CheckCollision[x][y] = 0;
7488 CheckImpact[x][y] = 0;
7492 else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
7494 if (MovDir[x][y] == MV_NONE)
7496 InitMovingField(x, y, MV_DOWN);
7497 started_moving = TRUE;
7500 else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
7502 if (WasJustFalling[x][y]) /* prevent animation from being restarted */
7503 MovDir[x][y] = MV_DOWN;
7505 InitMovingField(x, y, MV_DOWN);
7506 started_moving = TRUE;
7508 else if (element == EL_AMOEBA_DROP)
7510 Feld[x][y] = EL_AMOEBA_GROWING;
7511 Store[x][y] = EL_AMOEBA_WET;
7513 else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
7514 (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
7515 !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
7516 element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
7518 boolean can_fall_left = (x > 0 && IS_FREE(x - 1, y) &&
7519 (IS_FREE(x - 1, y + 1) ||
7520 Feld[x - 1][y + 1] == EL_ACID));
7521 boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
7522 (IS_FREE(x + 1, y + 1) ||
7523 Feld[x + 1][y + 1] == EL_ACID));
7524 boolean can_fall_any = (can_fall_left || can_fall_right);
7525 boolean can_fall_both = (can_fall_left && can_fall_right);
7526 int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
7528 if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
7530 if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
7531 can_fall_right = FALSE;
7532 else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
7533 can_fall_left = FALSE;
7534 else if (slippery_type == SLIPPERY_ONLY_LEFT)
7535 can_fall_right = FALSE;
7536 else if (slippery_type == SLIPPERY_ONLY_RIGHT)
7537 can_fall_left = FALSE;
7539 can_fall_any = (can_fall_left || can_fall_right);
7540 can_fall_both = FALSE;
7545 if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
7546 can_fall_right = FALSE; /* slip down on left side */
7548 can_fall_left = !(can_fall_right = RND(2));
7550 can_fall_both = FALSE;
7555 /* if not determined otherwise, prefer left side for slipping down */
7556 InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
7557 started_moving = TRUE;
7560 else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
7562 boolean left_is_free = (x > 0 && IS_FREE(x - 1, y));
7563 boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
7564 int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
7565 int belt_dir = game.belt_dir[belt_nr];
7567 if ((belt_dir == MV_LEFT && left_is_free) ||
7568 (belt_dir == MV_RIGHT && right_is_free))
7570 int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
7572 InitMovingField(x, y, belt_dir);
7573 started_moving = TRUE;
7575 Pushed[x][y] = TRUE;
7576 Pushed[nextx][y] = TRUE;
7578 GfxAction[x][y] = ACTION_DEFAULT;
7582 MovDir[x][y] = 0; /* if element was moving, stop it */
7587 /* not "else if" because of elements that can fall and move (EL_SPRING) */
7588 if (CAN_MOVE(element) && !started_moving)
7590 int move_pattern = element_info[element].move_pattern;
7593 Moving2Blocked(x, y, &newx, &newy);
7595 if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
7598 if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7599 CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7601 WasJustMoving[x][y] = 0;
7602 CheckCollision[x][y] = 0;
7604 TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
7606 if (Feld[x][y] != element) /* element has changed */
7610 if (!MovDelay[x][y]) /* start new movement phase */
7612 /* all objects that can change their move direction after each step
7613 (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
7615 if (element != EL_YAMYAM &&
7616 element != EL_DARK_YAMYAM &&
7617 element != EL_PACMAN &&
7618 !(move_pattern & MV_ANY_DIRECTION) &&
7619 move_pattern != MV_TURNING_LEFT &&
7620 move_pattern != MV_TURNING_RIGHT &&
7621 move_pattern != MV_TURNING_LEFT_RIGHT &&
7622 move_pattern != MV_TURNING_RIGHT_LEFT &&
7623 move_pattern != MV_TURNING_RANDOM)
7627 if (MovDelay[x][y] && (element == EL_BUG ||
7628 element == EL_SPACESHIP ||
7629 element == EL_SP_SNIKSNAK ||
7630 element == EL_SP_ELECTRON ||
7631 element == EL_MOLE))
7632 TEST_DrawLevelField(x, y);
7636 if (MovDelay[x][y]) /* wait some time before next movement */
7640 if (element == EL_ROBOT ||
7641 element == EL_YAMYAM ||
7642 element == EL_DARK_YAMYAM)
7644 DrawLevelElementAnimationIfNeeded(x, y, element);
7645 PlayLevelSoundAction(x, y, ACTION_WAITING);
7647 else if (element == EL_SP_ELECTRON)
7648 DrawLevelElementAnimationIfNeeded(x, y, element);
7649 else if (element == EL_DRAGON)
7652 int dir = MovDir[x][y];
7653 int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
7654 int dy = (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
7655 int graphic = (dir == MV_LEFT ? IMG_FLAMES_1_LEFT :
7656 dir == MV_RIGHT ? IMG_FLAMES_1_RIGHT :
7657 dir == MV_UP ? IMG_FLAMES_1_UP :
7658 dir == MV_DOWN ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
7659 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
7661 GfxAction[x][y] = ACTION_ATTACKING;
7663 if (IS_PLAYER(x, y))
7664 DrawPlayerField(x, y);
7666 TEST_DrawLevelField(x, y);
7668 PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
7670 for (i = 1; i <= 3; i++)
7672 int xx = x + i * dx;
7673 int yy = y + i * dy;
7674 int sx = SCREENX(xx);
7675 int sy = SCREENY(yy);
7676 int flame_graphic = graphic + (i - 1);
7678 if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
7683 int flamed = MovingOrBlocked2Element(xx, yy);
7685 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
7688 RemoveMovingField(xx, yy);
7690 ChangeDelay[xx][yy] = 0;
7692 Feld[xx][yy] = EL_FLAMES;
7694 if (IN_SCR_FIELD(sx, sy))
7696 TEST_DrawLevelFieldCrumbled(xx, yy);
7697 DrawGraphic(sx, sy, flame_graphic, frame);
7702 if (Feld[xx][yy] == EL_FLAMES)
7703 Feld[xx][yy] = EL_EMPTY;
7704 TEST_DrawLevelField(xx, yy);
7709 if (MovDelay[x][y]) /* element still has to wait some time */
7711 PlayLevelSoundAction(x, y, ACTION_WAITING);
7717 /* now make next step */
7719 Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
7721 if (DONT_COLLIDE_WITH(element) &&
7722 IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
7723 !PLAYER_ENEMY_PROTECTED(newx, newy))
7725 TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
7730 else if (CAN_MOVE_INTO_ACID(element) &&
7731 IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID &&
7732 !IS_MV_DIAGONAL(MovDir[x][y]) &&
7733 (MovDir[x][y] == MV_DOWN ||
7734 game.engine_version >= VERSION_IDENT(3,1,0,0)))
7736 SplashAcid(newx, newy);
7737 Store[x][y] = EL_ACID;
7739 else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
7741 if (Feld[newx][newy] == EL_EXIT_OPEN ||
7742 Feld[newx][newy] == EL_EM_EXIT_OPEN ||
7743 Feld[newx][newy] == EL_STEEL_EXIT_OPEN ||
7744 Feld[newx][newy] == EL_EM_STEEL_EXIT_OPEN)
7747 TEST_DrawLevelField(x, y);
7749 PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
7750 if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
7751 DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
7753 local_player->friends_still_needed--;
7754 if (!local_player->friends_still_needed &&
7755 !local_player->GameOver && AllPlayersGone)
7756 PlayerWins(local_player);
7760 else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
7762 if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MP_MOVING)
7763 TEST_DrawLevelField(newx, newy);
7765 GfxDir[x][y] = MovDir[x][y] = MV_NONE;
7767 else if (!IS_FREE(newx, newy))
7769 GfxAction[x][y] = ACTION_WAITING;
7771 if (IS_PLAYER(x, y))
7772 DrawPlayerField(x, y);
7774 TEST_DrawLevelField(x, y);
7779 else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
7781 if (IS_FOOD_PIG(Feld[newx][newy]))
7783 if (IS_MOVING(newx, newy))
7784 RemoveMovingField(newx, newy);
7787 Feld[newx][newy] = EL_EMPTY;
7788 TEST_DrawLevelField(newx, newy);
7791 PlayLevelSound(x, y, SND_PIG_DIGGING);
7793 else if (!IS_FREE(newx, newy))
7795 if (IS_PLAYER(x, y))
7796 DrawPlayerField(x, y);
7798 TEST_DrawLevelField(x, y);
7803 else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
7805 if (Store[x][y] != EL_EMPTY)
7807 boolean can_clone = FALSE;
7810 /* check if element to clone is still there */
7811 for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
7813 if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == Store[x][y])
7821 /* cannot clone or target field not free anymore -- do not clone */
7822 if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7823 Store[x][y] = EL_EMPTY;
7826 if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7828 if (IS_MV_DIAGONAL(MovDir[x][y]))
7830 int diagonal_move_dir = MovDir[x][y];
7831 int stored = Store[x][y];
7832 int change_delay = 8;
7835 /* android is moving diagonally */
7837 CreateField(x, y, EL_DIAGONAL_SHRINKING);
7839 Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
7840 GfxElement[x][y] = EL_EMC_ANDROID;
7841 GfxAction[x][y] = ACTION_SHRINKING;
7842 GfxDir[x][y] = diagonal_move_dir;
7843 ChangeDelay[x][y] = change_delay;
7845 graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
7848 DrawLevelGraphicAnimation(x, y, graphic);
7849 PlayLevelSoundAction(x, y, ACTION_SHRINKING);
7851 if (Feld[newx][newy] == EL_ACID)
7853 SplashAcid(newx, newy);
7858 CreateField(newx, newy, EL_DIAGONAL_GROWING);
7860 Store[newx][newy] = EL_EMC_ANDROID;
7861 GfxElement[newx][newy] = EL_EMC_ANDROID;
7862 GfxAction[newx][newy] = ACTION_GROWING;
7863 GfxDir[newx][newy] = diagonal_move_dir;
7864 ChangeDelay[newx][newy] = change_delay;
7866 graphic = el_act_dir2img(GfxElement[newx][newy],
7867 GfxAction[newx][newy], GfxDir[newx][newy]);
7869 DrawLevelGraphicAnimation(newx, newy, graphic);
7870 PlayLevelSoundAction(newx, newy, ACTION_GROWING);
7876 Feld[newx][newy] = EL_EMPTY;
7877 TEST_DrawLevelField(newx, newy);
7879 PlayLevelSoundAction(x, y, ACTION_DIGGING);
7882 else if (!IS_FREE(newx, newy))
7887 else if (IS_CUSTOM_ELEMENT(element) &&
7888 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7890 if (!DigFieldByCE(newx, newy, element))
7893 if (move_pattern & MV_MAZE_RUNNER_STYLE)
7895 RunnerVisit[x][y] = FrameCounter;
7896 PlayerVisit[x][y] /= 8; /* expire player visit path */
7899 else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
7901 if (!IS_FREE(newx, newy))
7903 if (IS_PLAYER(x, y))
7904 DrawPlayerField(x, y);
7906 TEST_DrawLevelField(x, y);
7912 boolean wanna_flame = !RND(10);
7913 int dx = newx - x, dy = newy - y;
7914 int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
7915 int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
7916 int element1 = (IN_LEV_FIELD(newx1, newy1) ?
7917 MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
7918 int element2 = (IN_LEV_FIELD(newx2, newy2) ?
7919 MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
7922 IS_CLASSIC_ENEMY(element1) ||
7923 IS_CLASSIC_ENEMY(element2)) &&
7924 element1 != EL_DRAGON && element2 != EL_DRAGON &&
7925 element1 != EL_FLAMES && element2 != EL_FLAMES)
7927 ResetGfxAnimation(x, y);
7928 GfxAction[x][y] = ACTION_ATTACKING;
7930 if (IS_PLAYER(x, y))
7931 DrawPlayerField(x, y);
7933 TEST_DrawLevelField(x, y);
7935 PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
7937 MovDelay[x][y] = 50;
7939 Feld[newx][newy] = EL_FLAMES;
7940 if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
7941 Feld[newx1][newy1] = EL_FLAMES;
7942 if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
7943 Feld[newx2][newy2] = EL_FLAMES;
7949 else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
7950 Feld[newx][newy] == EL_DIAMOND)
7952 if (IS_MOVING(newx, newy))
7953 RemoveMovingField(newx, newy);
7956 Feld[newx][newy] = EL_EMPTY;
7957 TEST_DrawLevelField(newx, newy);
7960 PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
7962 else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
7963 IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
7965 if (AmoebaNr[newx][newy])
7967 AmoebaCnt2[AmoebaNr[newx][newy]]--;
7968 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
7969 Feld[newx][newy] == EL_BD_AMOEBA)
7970 AmoebaCnt[AmoebaNr[newx][newy]]--;
7973 if (IS_MOVING(newx, newy))
7975 RemoveMovingField(newx, newy);
7979 Feld[newx][newy] = EL_EMPTY;
7980 TEST_DrawLevelField(newx, newy);
7983 PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
7985 else if ((element == EL_PACMAN || element == EL_MOLE)
7986 && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
7988 if (AmoebaNr[newx][newy])
7990 AmoebaCnt2[AmoebaNr[newx][newy]]--;
7991 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
7992 Feld[newx][newy] == EL_BD_AMOEBA)
7993 AmoebaCnt[AmoebaNr[newx][newy]]--;
7996 if (element == EL_MOLE)
7998 Feld[newx][newy] = EL_AMOEBA_SHRINKING;
7999 PlayLevelSound(x, y, SND_MOLE_DIGGING);
8001 ResetGfxAnimation(x, y);
8002 GfxAction[x][y] = ACTION_DIGGING;
8003 TEST_DrawLevelField(x, y);
8005 MovDelay[newx][newy] = 0; /* start amoeba shrinking delay */
8007 return; /* wait for shrinking amoeba */
8009 else /* element == EL_PACMAN */
8011 Feld[newx][newy] = EL_EMPTY;
8012 TEST_DrawLevelField(newx, newy);
8013 PlayLevelSound(x, y, SND_PACMAN_DIGGING);
8016 else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
8017 (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
8018 (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
8020 /* wait for shrinking amoeba to completely disappear */
8023 else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
8025 /* object was running against a wall */
8029 if (GFX_ELEMENT(element) != EL_SAND) /* !!! FIX THIS (crumble) !!! */
8030 DrawLevelElementAnimation(x, y, element);
8032 if (DONT_TOUCH(element))
8033 TestIfBadThingTouchesPlayer(x, y);
8038 InitMovingField(x, y, MovDir[x][y]);
8040 PlayLevelSoundAction(x, y, ACTION_MOVING);
8044 ContinueMoving(x, y);
8047 void ContinueMoving(int x, int y)
8049 int element = Feld[x][y];
8050 struct ElementInfo *ei = &element_info[element];
8051 int direction = MovDir[x][y];
8052 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
8053 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
8054 int newx = x + dx, newy = y + dy;
8055 int stored = Store[x][y];
8056 int stored_new = Store[newx][newy];
8057 boolean pushed_by_player = (Pushed[x][y] && IS_PLAYER(x, y));
8058 boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
8059 boolean last_line = (newy == lev_fieldy - 1);
8061 MovPos[x][y] += getElementMoveStepsize(x, y);
8063 if (pushed_by_player) /* special case: moving object pushed by player */
8064 MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
8066 if (ABS(MovPos[x][y]) < TILEX)
8068 TEST_DrawLevelField(x, y);
8070 return; /* element is still moving */
8073 /* element reached destination field */
8075 Feld[x][y] = EL_EMPTY;
8076 Feld[newx][newy] = element;
8077 MovPos[x][y] = 0; /* force "not moving" for "crumbled sand" */
8079 if (Store[x][y] == EL_ACID) /* element is moving into acid pool */
8081 element = Feld[newx][newy] = EL_ACID;
8083 else if (element == EL_MOLE)
8085 Feld[x][y] = EL_SAND;
8087 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8089 else if (element == EL_QUICKSAND_FILLING)
8091 element = Feld[newx][newy] = get_next_element(element);
8092 Store[newx][newy] = Store[x][y];
8094 else if (element == EL_QUICKSAND_EMPTYING)
8096 Feld[x][y] = get_next_element(element);
8097 element = Feld[newx][newy] = Store[x][y];
8099 else if (element == EL_QUICKSAND_FAST_FILLING)
8101 element = Feld[newx][newy] = get_next_element(element);
8102 Store[newx][newy] = Store[x][y];
8104 else if (element == EL_QUICKSAND_FAST_EMPTYING)
8106 Feld[x][y] = get_next_element(element);
8107 element = Feld[newx][newy] = Store[x][y];
8109 else if (element == EL_MAGIC_WALL_FILLING)
8111 element = Feld[newx][newy] = get_next_element(element);
8112 if (!game.magic_wall_active)
8113 element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
8114 Store[newx][newy] = Store[x][y];
8116 else if (element == EL_MAGIC_WALL_EMPTYING)
8118 Feld[x][y] = get_next_element(element);
8119 if (!game.magic_wall_active)
8120 Feld[x][y] = EL_MAGIC_WALL_DEAD;
8121 element = Feld[newx][newy] = Store[x][y];
8123 InitField(newx, newy, FALSE);
8125 else if (element == EL_BD_MAGIC_WALL_FILLING)
8127 element = Feld[newx][newy] = get_next_element(element);
8128 if (!game.magic_wall_active)
8129 element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
8130 Store[newx][newy] = Store[x][y];
8132 else if (element == EL_BD_MAGIC_WALL_EMPTYING)
8134 Feld[x][y] = get_next_element(element);
8135 if (!game.magic_wall_active)
8136 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
8137 element = Feld[newx][newy] = Store[x][y];
8139 InitField(newx, newy, FALSE);
8141 else if (element == EL_DC_MAGIC_WALL_FILLING)
8143 element = Feld[newx][newy] = get_next_element(element);
8144 if (!game.magic_wall_active)
8145 element = Feld[newx][newy] = EL_DC_MAGIC_WALL_DEAD;
8146 Store[newx][newy] = Store[x][y];
8148 else if (element == EL_DC_MAGIC_WALL_EMPTYING)
8150 Feld[x][y] = get_next_element(element);
8151 if (!game.magic_wall_active)
8152 Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
8153 element = Feld[newx][newy] = Store[x][y];
8155 InitField(newx, newy, FALSE);
8157 else if (element == EL_AMOEBA_DROPPING)
8159 Feld[x][y] = get_next_element(element);
8160 element = Feld[newx][newy] = Store[x][y];
8162 else if (element == EL_SOKOBAN_OBJECT)
8165 Feld[x][y] = Back[x][y];
8167 if (Back[newx][newy])
8168 Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
8170 Back[x][y] = Back[newx][newy] = 0;
8173 Store[x][y] = EL_EMPTY;
8178 MovDelay[newx][newy] = 0;
8180 if (CAN_CHANGE_OR_HAS_ACTION(element))
8182 /* copy element change control values to new field */
8183 ChangeDelay[newx][newy] = ChangeDelay[x][y];
8184 ChangePage[newx][newy] = ChangePage[x][y];
8185 ChangeCount[newx][newy] = ChangeCount[x][y];
8186 ChangeEvent[newx][newy] = ChangeEvent[x][y];
8189 CustomValue[newx][newy] = CustomValue[x][y];
8191 ChangeDelay[x][y] = 0;
8192 ChangePage[x][y] = -1;
8193 ChangeCount[x][y] = 0;
8194 ChangeEvent[x][y] = -1;
8196 CustomValue[x][y] = 0;
8198 /* copy animation control values to new field */
8199 GfxFrame[newx][newy] = GfxFrame[x][y];
8200 GfxRandom[newx][newy] = GfxRandom[x][y]; /* keep same random value */
8201 GfxAction[newx][newy] = GfxAction[x][y]; /* keep action one frame */
8202 GfxDir[newx][newy] = GfxDir[x][y]; /* keep element direction */
8204 Pushed[x][y] = Pushed[newx][newy] = FALSE;
8206 /* some elements can leave other elements behind after moving */
8207 if (ei->move_leave_element != EL_EMPTY &&
8208 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
8209 (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
8211 int move_leave_element = ei->move_leave_element;
8213 /* this makes it possible to leave the removed element again */
8214 if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
8215 move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
8217 Feld[x][y] = move_leave_element;
8219 if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
8220 MovDir[x][y] = direction;
8222 InitField(x, y, FALSE);
8224 if (GFX_CRUMBLED(Feld[x][y]))
8225 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8227 if (ELEM_IS_PLAYER(move_leave_element))
8228 RelocatePlayer(x, y, move_leave_element);
8231 /* do this after checking for left-behind element */
8232 ResetGfxAnimation(x, y); /* reset animation values for old field */
8234 if (!CAN_MOVE(element) ||
8235 (CAN_FALL(element) && direction == MV_DOWN &&
8236 (element == EL_SPRING ||
8237 element_info[element].move_pattern == MV_WHEN_PUSHED ||
8238 element_info[element].move_pattern == MV_WHEN_DROPPED)))
8239 GfxDir[x][y] = MovDir[newx][newy] = 0;
8241 TEST_DrawLevelField(x, y);
8242 TEST_DrawLevelField(newx, newy);
8244 Stop[newx][newy] = TRUE; /* ignore this element until the next frame */
8246 /* prevent pushed element from moving on in pushed direction */
8247 if (pushed_by_player && CAN_MOVE(element) &&
8248 element_info[element].move_pattern & MV_ANY_DIRECTION &&
8249 !(element_info[element].move_pattern & direction))
8250 TurnRound(newx, newy);
8252 /* prevent elements on conveyor belt from moving on in last direction */
8253 if (pushed_by_conveyor && CAN_FALL(element) &&
8254 direction & MV_HORIZONTAL)
8255 MovDir[newx][newy] = 0;
8257 if (!pushed_by_player)
8259 int nextx = newx + dx, nexty = newy + dy;
8260 boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
8262 WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
8264 if (CAN_FALL(element) && direction == MV_DOWN)
8265 WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
8267 if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
8268 CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
8270 if (CAN_FALL(element) && direction == MV_DOWN && check_collision_again)
8271 CheckImpact[newx][newy] = CHECK_DELAY_IMPACT;
8274 if (DONT_TOUCH(element)) /* object may be nasty to player or others */
8276 TestIfBadThingTouchesPlayer(newx, newy);
8277 TestIfBadThingTouchesFriend(newx, newy);
8279 if (!IS_CUSTOM_ELEMENT(element))
8280 TestIfBadThingTouchesOtherBadThing(newx, newy);
8282 else if (element == EL_PENGUIN)
8283 TestIfFriendTouchesBadThing(newx, newy);
8285 if (DONT_GET_HIT_BY(element))
8287 TestIfGoodThingGetsHitByBadThing(newx, newy, direction);
8290 /* give the player one last chance (one more frame) to move away */
8291 if (CAN_FALL(element) && direction == MV_DOWN &&
8292 (last_line || (!IS_FREE(x, newy + 1) &&
8293 (!IS_PLAYER(x, newy + 1) ||
8294 game.engine_version < VERSION_IDENT(3,1,1,0)))))
8297 if (pushed_by_player && !game.use_change_when_pushing_bug)
8299 int push_side = MV_DIR_OPPOSITE(direction);
8300 struct PlayerInfo *player = PLAYERINFO(x, y);
8302 CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
8303 player->index_bit, push_side);
8304 CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
8305 player->index_bit, push_side);
8308 if (element == EL_EMC_ANDROID && pushed_by_player) /* make another move */
8309 MovDelay[newx][newy] = 1;
8311 CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
8313 TestIfElementTouchesCustomElement(x, y); /* empty or new element */
8314 TestIfElementHitsCustomElement(newx, newy, direction);
8315 TestIfPlayerTouchesCustomElement(newx, newy);
8316 TestIfElementTouchesCustomElement(newx, newy);
8318 if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
8319 IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
8320 CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
8321 MV_DIR_OPPOSITE(direction));
8324 int AmoebeNachbarNr(int ax, int ay)
8327 int element = Feld[ax][ay];
8329 static int xy[4][2] =
8337 for (i = 0; i < NUM_DIRECTIONS; i++)
8339 int x = ax + xy[i][0];
8340 int y = ay + xy[i][1];
8342 if (!IN_LEV_FIELD(x, y))
8345 if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
8346 group_nr = AmoebaNr[x][y];
8352 void AmoebenVereinigen(int ax, int ay)
8354 int i, x, y, xx, yy;
8355 int new_group_nr = AmoebaNr[ax][ay];
8356 static int xy[4][2] =
8364 if (new_group_nr == 0)
8367 for (i = 0; i < NUM_DIRECTIONS; i++)
8372 if (!IN_LEV_FIELD(x, y))
8375 if ((Feld[x][y] == EL_AMOEBA_FULL ||
8376 Feld[x][y] == EL_BD_AMOEBA ||
8377 Feld[x][y] == EL_AMOEBA_DEAD) &&
8378 AmoebaNr[x][y] != new_group_nr)
8380 int old_group_nr = AmoebaNr[x][y];
8382 if (old_group_nr == 0)
8385 AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
8386 AmoebaCnt[old_group_nr] = 0;
8387 AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
8388 AmoebaCnt2[old_group_nr] = 0;
8390 SCAN_PLAYFIELD(xx, yy)
8392 if (AmoebaNr[xx][yy] == old_group_nr)
8393 AmoebaNr[xx][yy] = new_group_nr;
8399 void AmoebeUmwandeln(int ax, int ay)
8403 if (Feld[ax][ay] == EL_AMOEBA_DEAD)
8405 int group_nr = AmoebaNr[ax][ay];
8410 printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
8411 printf("AmoebeUmwandeln(): This should never happen!\n");
8416 SCAN_PLAYFIELD(x, y)
8418 if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
8421 Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
8425 PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
8426 SND_AMOEBA_TURNING_TO_GEM :
8427 SND_AMOEBA_TURNING_TO_ROCK));
8432 static int xy[4][2] =
8440 for (i = 0; i < NUM_DIRECTIONS; i++)
8445 if (!IN_LEV_FIELD(x, y))
8448 if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
8450 PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
8451 SND_AMOEBA_TURNING_TO_GEM :
8452 SND_AMOEBA_TURNING_TO_ROCK));
8459 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
8462 int group_nr = AmoebaNr[ax][ay];
8463 boolean done = FALSE;
8468 printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
8469 printf("AmoebeUmwandelnBD(): This should never happen!\n");
8474 SCAN_PLAYFIELD(x, y)
8476 if (AmoebaNr[x][y] == group_nr &&
8477 (Feld[x][y] == EL_AMOEBA_DEAD ||
8478 Feld[x][y] == EL_BD_AMOEBA ||
8479 Feld[x][y] == EL_AMOEBA_GROWING))
8482 Feld[x][y] = new_element;
8483 InitField(x, y, FALSE);
8484 TEST_DrawLevelField(x, y);
8490 PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
8491 SND_BD_AMOEBA_TURNING_TO_ROCK :
8492 SND_BD_AMOEBA_TURNING_TO_GEM));
8495 void AmoebeWaechst(int x, int y)
8497 static unsigned int sound_delay = 0;
8498 static unsigned int sound_delay_value = 0;
8500 if (!MovDelay[x][y]) /* start new growing cycle */
8504 if (DelayReached(&sound_delay, sound_delay_value))
8506 PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
8507 sound_delay_value = 30;
8511 if (MovDelay[x][y]) /* wait some time before growing bigger */
8514 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8516 int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
8517 6 - MovDelay[x][y]);
8519 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
8522 if (!MovDelay[x][y])
8524 Feld[x][y] = Store[x][y];
8526 TEST_DrawLevelField(x, y);
8531 void AmoebaDisappearing(int x, int y)
8533 static unsigned int sound_delay = 0;
8534 static unsigned int sound_delay_value = 0;
8536 if (!MovDelay[x][y]) /* start new shrinking cycle */
8540 if (DelayReached(&sound_delay, sound_delay_value))
8541 sound_delay_value = 30;
8544 if (MovDelay[x][y]) /* wait some time before shrinking */
8547 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8549 int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
8550 6 - MovDelay[x][y]);
8552 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
8555 if (!MovDelay[x][y])
8557 Feld[x][y] = EL_EMPTY;
8558 TEST_DrawLevelField(x, y);
8560 /* don't let mole enter this field in this cycle;
8561 (give priority to objects falling to this field from above) */
8567 void AmoebeAbleger(int ax, int ay)
8570 int element = Feld[ax][ay];
8571 int graphic = el2img(element);
8572 int newax = ax, neway = ay;
8573 boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
8574 static int xy[4][2] =
8582 if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
8584 Feld[ax][ay] = EL_AMOEBA_DEAD;
8585 TEST_DrawLevelField(ax, ay);
8589 if (IS_ANIMATED(graphic))
8590 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
8592 if (!MovDelay[ax][ay]) /* start making new amoeba field */
8593 MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
8595 if (MovDelay[ax][ay]) /* wait some time before making new amoeba */
8598 if (MovDelay[ax][ay])
8602 if (can_drop) /* EL_AMOEBA_WET or EL_EMC_DRIPPER */
8605 int x = ax + xy[start][0];
8606 int y = ay + xy[start][1];
8608 if (!IN_LEV_FIELD(x, y))
8611 if (IS_FREE(x, y) ||
8612 CAN_GROW_INTO(Feld[x][y]) ||
8613 Feld[x][y] == EL_QUICKSAND_EMPTY ||
8614 Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
8620 if (newax == ax && neway == ay)
8623 else /* normal or "filled" (BD style) amoeba */
8626 boolean waiting_for_player = FALSE;
8628 for (i = 0; i < NUM_DIRECTIONS; i++)
8630 int j = (start + i) % 4;
8631 int x = ax + xy[j][0];
8632 int y = ay + xy[j][1];
8634 if (!IN_LEV_FIELD(x, y))
8637 if (IS_FREE(x, y) ||
8638 CAN_GROW_INTO(Feld[x][y]) ||
8639 Feld[x][y] == EL_QUICKSAND_EMPTY ||
8640 Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
8646 else if (IS_PLAYER(x, y))
8647 waiting_for_player = TRUE;
8650 if (newax == ax && neway == ay) /* amoeba cannot grow */
8652 if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
8654 Feld[ax][ay] = EL_AMOEBA_DEAD;
8655 TEST_DrawLevelField(ax, ay);
8656 AmoebaCnt[AmoebaNr[ax][ay]]--;
8658 if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0) /* amoeba is completely dead */
8660 if (element == EL_AMOEBA_FULL)
8661 AmoebeUmwandeln(ax, ay);
8662 else if (element == EL_BD_AMOEBA)
8663 AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
8668 else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
8670 /* amoeba gets larger by growing in some direction */
8672 int new_group_nr = AmoebaNr[ax][ay];
8675 if (new_group_nr == 0)
8677 printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
8678 printf("AmoebeAbleger(): This should never happen!\n");
8683 AmoebaNr[newax][neway] = new_group_nr;
8684 AmoebaCnt[new_group_nr]++;
8685 AmoebaCnt2[new_group_nr]++;
8687 /* if amoeba touches other amoeba(s) after growing, unify them */
8688 AmoebenVereinigen(newax, neway);
8690 if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
8692 AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
8698 if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
8699 (neway == lev_fieldy - 1 && newax != ax))
8701 Feld[newax][neway] = EL_AMOEBA_GROWING; /* creation of new amoeba */
8702 Store[newax][neway] = element;
8704 else if (neway == ay || element == EL_EMC_DRIPPER)
8706 Feld[newax][neway] = EL_AMOEBA_DROP; /* drop left/right of amoeba */
8708 PlayLevelSoundAction(newax, neway, ACTION_GROWING);
8712 InitMovingField(ax, ay, MV_DOWN); /* drop dripping from amoeba */
8713 Feld[ax][ay] = EL_AMOEBA_DROPPING;
8714 Store[ax][ay] = EL_AMOEBA_DROP;
8715 ContinueMoving(ax, ay);
8719 TEST_DrawLevelField(newax, neway);
8722 void Life(int ax, int ay)
8726 int element = Feld[ax][ay];
8727 int graphic = el2img(element);
8728 int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
8730 boolean changed = FALSE;
8732 if (IS_ANIMATED(graphic))
8733 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
8738 if (!MovDelay[ax][ay]) /* start new "game of life" cycle */
8739 MovDelay[ax][ay] = life_time;
8741 if (MovDelay[ax][ay]) /* wait some time before next cycle */
8744 if (MovDelay[ax][ay])
8748 for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
8750 int xx = ax+x1, yy = ay+y1;
8753 if (!IN_LEV_FIELD(xx, yy))
8756 for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
8758 int x = xx+x2, y = yy+y2;
8760 if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
8763 if (((Feld[x][y] == element ||
8764 (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
8766 (IS_FREE(x, y) && Stop[x][y]))
8770 if (xx == ax && yy == ay) /* field in the middle */
8772 if (nachbarn < life_parameter[0] ||
8773 nachbarn > life_parameter[1])
8775 Feld[xx][yy] = EL_EMPTY;
8777 TEST_DrawLevelField(xx, yy);
8778 Stop[xx][yy] = TRUE;
8782 else if (IS_FREE(xx, yy) || CAN_GROW_INTO(Feld[xx][yy]))
8783 { /* free border field */
8784 if (nachbarn >= life_parameter[2] &&
8785 nachbarn <= life_parameter[3])
8787 Feld[xx][yy] = element;
8788 MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
8790 TEST_DrawLevelField(xx, yy);
8791 Stop[xx][yy] = TRUE;
8798 PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
8799 SND_GAME_OF_LIFE_GROWING);
8802 static void InitRobotWheel(int x, int y)
8804 ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
8807 static void RunRobotWheel(int x, int y)
8809 PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
8812 static void StopRobotWheel(int x, int y)
8814 if (ZX == x && ZY == y)
8818 game.robot_wheel_active = FALSE;
8822 static void InitTimegateWheel(int x, int y)
8824 ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
8827 static void RunTimegateWheel(int x, int y)
8829 PlayLevelSound(x, y, SND_CLASS_TIMEGATE_SWITCH_ACTIVE);
8832 static void InitMagicBallDelay(int x, int y)
8834 ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
8837 static void ActivateMagicBall(int bx, int by)
8841 if (level.ball_random)
8843 int pos_border = RND(8); /* select one of the eight border elements */
8844 int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
8845 int xx = pos_content % 3;
8846 int yy = pos_content / 3;
8851 if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
8852 CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
8856 for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
8858 int xx = x - bx + 1;
8859 int yy = y - by + 1;
8861 if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
8862 CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
8866 game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
8869 void CheckExit(int x, int y)
8871 if (local_player->gems_still_needed > 0 ||
8872 local_player->sokobanfields_still_needed > 0 ||
8873 local_player->lights_still_needed > 0)
8875 int element = Feld[x][y];
8876 int graphic = el2img(element);
8878 if (IS_ANIMATED(graphic))
8879 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8884 if (AllPlayersGone) /* do not re-open exit door closed after last player */
8887 Feld[x][y] = EL_EXIT_OPENING;
8889 PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
8892 void CheckExitEM(int x, int y)
8894 if (local_player->gems_still_needed > 0 ||
8895 local_player->sokobanfields_still_needed > 0 ||
8896 local_player->lights_still_needed > 0)
8898 int element = Feld[x][y];
8899 int graphic = el2img(element);
8901 if (IS_ANIMATED(graphic))
8902 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8907 if (AllPlayersGone) /* do not re-open exit door closed after last player */
8910 Feld[x][y] = EL_EM_EXIT_OPENING;
8912 PlayLevelSoundNearest(x, y, SND_CLASS_EM_EXIT_OPENING);
8915 void CheckExitSteel(int x, int y)
8917 if (local_player->gems_still_needed > 0 ||
8918 local_player->sokobanfields_still_needed > 0 ||
8919 local_player->lights_still_needed > 0)
8921 int element = Feld[x][y];
8922 int graphic = el2img(element);
8924 if (IS_ANIMATED(graphic))
8925 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8930 if (AllPlayersGone) /* do not re-open exit door closed after last player */
8933 Feld[x][y] = EL_STEEL_EXIT_OPENING;
8935 PlayLevelSoundNearest(x, y, SND_CLASS_STEEL_EXIT_OPENING);
8938 void CheckExitSteelEM(int x, int y)
8940 if (local_player->gems_still_needed > 0 ||
8941 local_player->sokobanfields_still_needed > 0 ||
8942 local_player->lights_still_needed > 0)
8944 int element = Feld[x][y];
8945 int graphic = el2img(element);
8947 if (IS_ANIMATED(graphic))
8948 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8953 if (AllPlayersGone) /* do not re-open exit door closed after last player */
8956 Feld[x][y] = EL_EM_STEEL_EXIT_OPENING;
8958 PlayLevelSoundNearest(x, y, SND_CLASS_EM_STEEL_EXIT_OPENING);
8961 void CheckExitSP(int x, int y)
8963 if (local_player->gems_still_needed > 0)
8965 int element = Feld[x][y];
8966 int graphic = el2img(element);
8968 if (IS_ANIMATED(graphic))
8969 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8974 if (AllPlayersGone) /* do not re-open exit door closed after last player */
8977 Feld[x][y] = EL_SP_EXIT_OPENING;
8979 PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
8982 static void CloseAllOpenTimegates()
8986 SCAN_PLAYFIELD(x, y)
8988 int element = Feld[x][y];
8990 if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
8992 Feld[x][y] = EL_TIMEGATE_CLOSING;
8994 PlayLevelSoundAction(x, y, ACTION_CLOSING);
8999 void DrawTwinkleOnField(int x, int y)
9001 if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
9004 if (Feld[x][y] == EL_BD_DIAMOND)
9007 if (MovDelay[x][y] == 0) /* next animation frame */
9008 MovDelay[x][y] = 11 * !GetSimpleRandom(500);
9010 if (MovDelay[x][y] != 0) /* wait some time before next frame */
9014 DrawLevelElementAnimation(x, y, Feld[x][y]);
9016 if (MovDelay[x][y] != 0)
9018 int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
9019 10 - MovDelay[x][y]);
9021 DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
9026 void MauerWaechst(int x, int y)
9030 if (!MovDelay[x][y]) /* next animation frame */
9031 MovDelay[x][y] = 3 * delay;
9033 if (MovDelay[x][y]) /* wait some time before next frame */
9037 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9039 int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
9040 int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
9042 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
9045 if (!MovDelay[x][y])
9047 if (MovDir[x][y] == MV_LEFT)
9049 if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
9050 TEST_DrawLevelField(x - 1, y);
9052 else if (MovDir[x][y] == MV_RIGHT)
9054 if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
9055 TEST_DrawLevelField(x + 1, y);
9057 else if (MovDir[x][y] == MV_UP)
9059 if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
9060 TEST_DrawLevelField(x, y - 1);
9064 if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
9065 TEST_DrawLevelField(x, y + 1);
9068 Feld[x][y] = Store[x][y];
9070 GfxDir[x][y] = MovDir[x][y] = MV_NONE;
9071 TEST_DrawLevelField(x, y);
9076 void MauerAbleger(int ax, int ay)
9078 int element = Feld[ax][ay];
9079 int graphic = el2img(element);
9080 boolean oben_frei = FALSE, unten_frei = FALSE;
9081 boolean links_frei = FALSE, rechts_frei = FALSE;
9082 boolean oben_massiv = FALSE, unten_massiv = FALSE;
9083 boolean links_massiv = FALSE, rechts_massiv = FALSE;
9084 boolean new_wall = FALSE;
9086 if (IS_ANIMATED(graphic))
9087 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9089 if (!MovDelay[ax][ay]) /* start building new wall */
9090 MovDelay[ax][ay] = 6;
9092 if (MovDelay[ax][ay]) /* wait some time before building new wall */
9095 if (MovDelay[ax][ay])
9099 if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9101 if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9103 if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9105 if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9108 if (element == EL_EXPANDABLE_WALL_VERTICAL ||
9109 element == EL_EXPANDABLE_WALL_ANY)
9113 Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
9114 Store[ax][ay-1] = element;
9115 GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9116 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9117 DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9118 IMG_EXPANDABLE_WALL_GROWING_UP, 0);
9123 Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
9124 Store[ax][ay+1] = element;
9125 GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9126 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9127 DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9128 IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
9133 if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9134 element == EL_EXPANDABLE_WALL_ANY ||
9135 element == EL_EXPANDABLE_WALL ||
9136 element == EL_BD_EXPANDABLE_WALL)
9140 Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
9141 Store[ax-1][ay] = element;
9142 GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9143 if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9144 DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9145 IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
9151 Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
9152 Store[ax+1][ay] = element;
9153 GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9154 if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9155 DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9156 IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
9161 if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
9162 TEST_DrawLevelField(ax, ay);
9164 if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
9166 if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
9167 unten_massiv = TRUE;
9168 if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
9169 links_massiv = TRUE;
9170 if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
9171 rechts_massiv = TRUE;
9173 if (((oben_massiv && unten_massiv) ||
9174 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9175 element == EL_EXPANDABLE_WALL) &&
9176 ((links_massiv && rechts_massiv) ||
9177 element == EL_EXPANDABLE_WALL_VERTICAL))
9178 Feld[ax][ay] = EL_WALL;
9181 PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9184 void MauerAblegerStahl(int ax, int ay)
9186 int element = Feld[ax][ay];
9187 int graphic = el2img(element);
9188 boolean oben_frei = FALSE, unten_frei = FALSE;
9189 boolean links_frei = FALSE, rechts_frei = FALSE;
9190 boolean oben_massiv = FALSE, unten_massiv = FALSE;
9191 boolean links_massiv = FALSE, rechts_massiv = FALSE;
9192 boolean new_wall = FALSE;
9194 if (IS_ANIMATED(graphic))
9195 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9197 if (!MovDelay[ax][ay]) /* start building new wall */
9198 MovDelay[ax][ay] = 6;
9200 if (MovDelay[ax][ay]) /* wait some time before building new wall */
9203 if (MovDelay[ax][ay])
9207 if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9209 if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9211 if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9213 if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9216 if (element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
9217 element == EL_EXPANDABLE_STEELWALL_ANY)
9221 Feld[ax][ay-1] = EL_EXPANDABLE_STEELWALL_GROWING;
9222 Store[ax][ay-1] = element;
9223 GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9224 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9225 DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9226 IMG_EXPANDABLE_STEELWALL_GROWING_UP, 0);
9231 Feld[ax][ay+1] = EL_EXPANDABLE_STEELWALL_GROWING;
9232 Store[ax][ay+1] = element;
9233 GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9234 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9235 DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9236 IMG_EXPANDABLE_STEELWALL_GROWING_DOWN, 0);
9241 if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
9242 element == EL_EXPANDABLE_STEELWALL_ANY)
9246 Feld[ax-1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9247 Store[ax-1][ay] = element;
9248 GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9249 if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9250 DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9251 IMG_EXPANDABLE_STEELWALL_GROWING_LEFT, 0);
9257 Feld[ax+1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9258 Store[ax+1][ay] = element;
9259 GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9260 if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9261 DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9262 IMG_EXPANDABLE_STEELWALL_GROWING_RIGHT, 0);
9267 if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
9269 if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
9270 unten_massiv = TRUE;
9271 if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
9272 links_massiv = TRUE;
9273 if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
9274 rechts_massiv = TRUE;
9276 if (((oben_massiv && unten_massiv) ||
9277 element == EL_EXPANDABLE_STEELWALL_HORIZONTAL) &&
9278 ((links_massiv && rechts_massiv) ||
9279 element == EL_EXPANDABLE_STEELWALL_VERTICAL))
9280 Feld[ax][ay] = EL_STEELWALL;
9283 PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9286 void CheckForDragon(int x, int y)
9289 boolean dragon_found = FALSE;
9290 static int xy[4][2] =
9298 for (i = 0; i < NUM_DIRECTIONS; i++)
9300 for (j = 0; j < 4; j++)
9302 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9304 if (IN_LEV_FIELD(xx, yy) &&
9305 (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
9307 if (Feld[xx][yy] == EL_DRAGON)
9308 dragon_found = TRUE;
9317 for (i = 0; i < NUM_DIRECTIONS; i++)
9319 for (j = 0; j < 3; j++)
9321 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9323 if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
9325 Feld[xx][yy] = EL_EMPTY;
9326 TEST_DrawLevelField(xx, yy);
9335 static void InitBuggyBase(int x, int y)
9337 int element = Feld[x][y];
9338 int activating_delay = FRAMES_PER_SECOND / 4;
9341 (element == EL_SP_BUGGY_BASE ?
9342 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
9343 element == EL_SP_BUGGY_BASE_ACTIVATING ?
9345 element == EL_SP_BUGGY_BASE_ACTIVE ?
9346 1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
9349 static void WarnBuggyBase(int x, int y)
9352 static int xy[4][2] =
9360 for (i = 0; i < NUM_DIRECTIONS; i++)
9362 int xx = x + xy[i][0];
9363 int yy = y + xy[i][1];
9365 if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
9367 PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
9374 static void InitTrap(int x, int y)
9376 ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
9379 static void ActivateTrap(int x, int y)
9381 PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
9384 static void ChangeActiveTrap(int x, int y)
9386 int graphic = IMG_TRAP_ACTIVE;
9388 /* if new animation frame was drawn, correct crumbled sand border */
9389 if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
9390 TEST_DrawLevelFieldCrumbled(x, y);
9393 static int getSpecialActionElement(int element, int number, int base_element)
9395 return (element != EL_EMPTY ? element :
9396 number != -1 ? base_element + number - 1 :
9400 static int getModifiedActionNumber(int value_old, int operator, int operand,
9401 int value_min, int value_max)
9403 int value_new = (operator == CA_MODE_SET ? operand :
9404 operator == CA_MODE_ADD ? value_old + operand :
9405 operator == CA_MODE_SUBTRACT ? value_old - operand :
9406 operator == CA_MODE_MULTIPLY ? value_old * operand :
9407 operator == CA_MODE_DIVIDE ? value_old / MAX(1, operand) :
9408 operator == CA_MODE_MODULO ? value_old % MAX(1, operand) :
9411 return (value_new < value_min ? value_min :
9412 value_new > value_max ? value_max :
9416 static void ExecuteCustomElementAction(int x, int y, int element, int page)
9418 struct ElementInfo *ei = &element_info[element];
9419 struct ElementChangeInfo *change = &ei->change_page[page];
9420 int target_element = change->target_element;
9421 int action_type = change->action_type;
9422 int action_mode = change->action_mode;
9423 int action_arg = change->action_arg;
9424 int action_element = change->action_element;
9427 if (!change->has_action)
9430 /* ---------- determine action paramater values -------------------------- */
9432 int level_time_value =
9433 (level.time > 0 ? TimeLeft :
9436 int action_arg_element_raw =
9437 (action_arg == CA_ARG_PLAYER_TRIGGER ? change->actual_trigger_player :
9438 action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
9439 action_arg == CA_ARG_ELEMENT_TARGET ? change->target_element :
9440 action_arg == CA_ARG_ELEMENT_ACTION ? change->action_element :
9441 action_arg == CA_ARG_INVENTORY_RM_TRIGGER ? change->actual_trigger_element:
9442 action_arg == CA_ARG_INVENTORY_RM_TARGET ? change->target_element :
9443 action_arg == CA_ARG_INVENTORY_RM_ACTION ? change->action_element :
9445 int action_arg_element = GetElementFromGroupElement(action_arg_element_raw);
9447 int action_arg_direction =
9448 (action_arg >= CA_ARG_DIRECTION_LEFT &&
9449 action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
9450 action_arg == CA_ARG_DIRECTION_TRIGGER ?
9451 change->actual_trigger_side :
9452 action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
9453 MV_DIR_OPPOSITE(change->actual_trigger_side) :
9456 int action_arg_number_min =
9457 (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
9460 int action_arg_number_max =
9461 (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
9462 action_type == CA_SET_LEVEL_GEMS ? 999 :
9463 action_type == CA_SET_LEVEL_TIME ? 9999 :
9464 action_type == CA_SET_LEVEL_SCORE ? 99999 :
9465 action_type == CA_SET_CE_VALUE ? 9999 :
9466 action_type == CA_SET_CE_SCORE ? 9999 :
9469 int action_arg_number_reset =
9470 (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize[0] :
9471 action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
9472 action_type == CA_SET_LEVEL_TIME ? level.time :
9473 action_type == CA_SET_LEVEL_SCORE ? 0 :
9474 action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
9475 action_type == CA_SET_CE_SCORE ? 0 :
9478 int action_arg_number =
9479 (action_arg <= CA_ARG_MAX ? action_arg :
9480 action_arg >= CA_ARG_SPEED_NOT_MOVING &&
9481 action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
9482 action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
9483 action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
9484 action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
9485 action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
9486 action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
9487 action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
9488 action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
9489 action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
9490 action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? local_player->gems_still_needed :
9491 action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? local_player->score :
9492 action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
9493 action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
9494 action_arg == CA_ARG_ELEMENT_CV_ACTION ? GET_NEW_CE_VALUE(action_element):
9495 action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
9496 action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
9497 action_arg == CA_ARG_ELEMENT_CS_ACTION ? GET_CE_SCORE(action_element) :
9498 action_arg == CA_ARG_ELEMENT_NR_TARGET ? change->target_element :
9499 action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
9500 action_arg == CA_ARG_ELEMENT_NR_ACTION ? change->action_element :
9503 int action_arg_number_old =
9504 (action_type == CA_SET_LEVEL_GEMS ? local_player->gems_still_needed :
9505 action_type == CA_SET_LEVEL_TIME ? TimeLeft :
9506 action_type == CA_SET_LEVEL_SCORE ? local_player->score :
9507 action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
9508 action_type == CA_SET_CE_SCORE ? ei->collect_score :
9511 int action_arg_number_new =
9512 getModifiedActionNumber(action_arg_number_old,
9513 action_mode, action_arg_number,
9514 action_arg_number_min, action_arg_number_max);
9516 int trigger_player_bits =
9517 (change->actual_trigger_player_bits != CH_PLAYER_NONE ?
9518 change->actual_trigger_player_bits : change->trigger_player);
9520 int action_arg_player_bits =
9521 (action_arg >= CA_ARG_PLAYER_1 &&
9522 action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
9523 action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
9524 action_arg == CA_ARG_PLAYER_ACTION ? 1 << GET_PLAYER_NR(action_element) :
9527 /* ---------- execute action -------------------------------------------- */
9529 switch (action_type)
9536 /* ---------- level actions ------------------------------------------- */
9538 case CA_RESTART_LEVEL:
9540 game.restart_level = TRUE;
9545 case CA_SHOW_ENVELOPE:
9547 int element = getSpecialActionElement(action_arg_element,
9548 action_arg_number, EL_ENVELOPE_1);
9550 if (IS_ENVELOPE(element))
9551 local_player->show_envelope = element;
9556 case CA_SET_LEVEL_TIME:
9558 if (level.time > 0) /* only modify limited time value */
9560 TimeLeft = action_arg_number_new;
9562 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
9564 DisplayGameControlValues();
9566 if (!TimeLeft && setup.time_limit)
9567 for (i = 0; i < MAX_PLAYERS; i++)
9568 KillPlayer(&stored_player[i]);
9574 case CA_SET_LEVEL_SCORE:
9576 local_player->score = action_arg_number_new;
9578 game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
9580 DisplayGameControlValues();
9585 case CA_SET_LEVEL_GEMS:
9587 local_player->gems_still_needed = action_arg_number_new;
9589 game_panel_controls[GAME_PANEL_GEMS].value =
9590 local_player->gems_still_needed;
9592 DisplayGameControlValues();
9597 case CA_SET_LEVEL_WIND:
9599 game.wind_direction = action_arg_direction;
9604 case CA_SET_LEVEL_RANDOM_SEED:
9606 /* ensure that setting a new random seed while playing is predictable */
9607 InitRND(action_arg_number_new ? action_arg_number_new : RND(1000000) + 1);
9612 /* ---------- player actions ------------------------------------------ */
9614 case CA_MOVE_PLAYER:
9616 /* automatically move to the next field in specified direction */
9617 for (i = 0; i < MAX_PLAYERS; i++)
9618 if (trigger_player_bits & (1 << i))
9619 stored_player[i].programmed_action = action_arg_direction;
9624 case CA_EXIT_PLAYER:
9626 for (i = 0; i < MAX_PLAYERS; i++)
9627 if (action_arg_player_bits & (1 << i))
9628 PlayerWins(&stored_player[i]);
9633 case CA_KILL_PLAYER:
9635 for (i = 0; i < MAX_PLAYERS; i++)
9636 if (action_arg_player_bits & (1 << i))
9637 KillPlayer(&stored_player[i]);
9642 case CA_SET_PLAYER_KEYS:
9644 int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
9645 int element = getSpecialActionElement(action_arg_element,
9646 action_arg_number, EL_KEY_1);
9648 if (IS_KEY(element))
9650 for (i = 0; i < MAX_PLAYERS; i++)
9652 if (trigger_player_bits & (1 << i))
9654 stored_player[i].key[KEY_NR(element)] = key_state;
9656 DrawGameDoorValues();
9664 case CA_SET_PLAYER_SPEED:
9666 for (i = 0; i < MAX_PLAYERS; i++)
9668 if (trigger_player_bits & (1 << i))
9670 int move_stepsize = TILEX / stored_player[i].move_delay_value;
9672 if (action_arg == CA_ARG_SPEED_FASTER &&
9673 stored_player[i].cannot_move)
9675 action_arg_number = STEPSIZE_VERY_SLOW;
9677 else if (action_arg == CA_ARG_SPEED_SLOWER ||
9678 action_arg == CA_ARG_SPEED_FASTER)
9680 action_arg_number = 2;
9681 action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
9684 else if (action_arg == CA_ARG_NUMBER_RESET)
9686 action_arg_number = level.initial_player_stepsize[i];
9690 getModifiedActionNumber(move_stepsize,
9693 action_arg_number_min,
9694 action_arg_number_max);
9696 SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
9703 case CA_SET_PLAYER_SHIELD:
9705 for (i = 0; i < MAX_PLAYERS; i++)
9707 if (trigger_player_bits & (1 << i))
9709 if (action_arg == CA_ARG_SHIELD_OFF)
9711 stored_player[i].shield_normal_time_left = 0;
9712 stored_player[i].shield_deadly_time_left = 0;
9714 else if (action_arg == CA_ARG_SHIELD_NORMAL)
9716 stored_player[i].shield_normal_time_left = 999999;
9718 else if (action_arg == CA_ARG_SHIELD_DEADLY)
9720 stored_player[i].shield_normal_time_left = 999999;
9721 stored_player[i].shield_deadly_time_left = 999999;
9729 case CA_SET_PLAYER_GRAVITY:
9731 for (i = 0; i < MAX_PLAYERS; i++)
9733 if (trigger_player_bits & (1 << i))
9735 stored_player[i].gravity =
9736 (action_arg == CA_ARG_GRAVITY_OFF ? FALSE :
9737 action_arg == CA_ARG_GRAVITY_ON ? TRUE :
9738 action_arg == CA_ARG_GRAVITY_TOGGLE ? !stored_player[i].gravity :
9739 stored_player[i].gravity);
9746 case CA_SET_PLAYER_ARTWORK:
9748 for (i = 0; i < MAX_PLAYERS; i++)
9750 if (trigger_player_bits & (1 << i))
9752 int artwork_element = action_arg_element;
9754 if (action_arg == CA_ARG_ELEMENT_RESET)
9756 (level.use_artwork_element[i] ? level.artwork_element[i] :
9757 stored_player[i].element_nr);
9759 if (stored_player[i].artwork_element != artwork_element)
9760 stored_player[i].Frame = 0;
9762 stored_player[i].artwork_element = artwork_element;
9764 SetPlayerWaiting(&stored_player[i], FALSE);
9766 /* set number of special actions for bored and sleeping animation */
9767 stored_player[i].num_special_action_bored =
9768 get_num_special_action(artwork_element,
9769 ACTION_BORING_1, ACTION_BORING_LAST);
9770 stored_player[i].num_special_action_sleeping =
9771 get_num_special_action(artwork_element,
9772 ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
9779 case CA_SET_PLAYER_INVENTORY:
9781 for (i = 0; i < MAX_PLAYERS; i++)
9783 struct PlayerInfo *player = &stored_player[i];
9786 if (trigger_player_bits & (1 << i))
9788 int inventory_element = action_arg_element;
9790 if (action_arg == CA_ARG_ELEMENT_TARGET ||
9791 action_arg == CA_ARG_ELEMENT_TRIGGER ||
9792 action_arg == CA_ARG_ELEMENT_ACTION)
9794 int element = inventory_element;
9795 int collect_count = element_info[element].collect_count_initial;
9797 if (!IS_CUSTOM_ELEMENT(element))
9800 if (collect_count == 0)
9801 player->inventory_infinite_element = element;
9803 for (k = 0; k < collect_count; k++)
9804 if (player->inventory_size < MAX_INVENTORY_SIZE)
9805 player->inventory_element[player->inventory_size++] =
9808 else if (action_arg == CA_ARG_INVENTORY_RM_TARGET ||
9809 action_arg == CA_ARG_INVENTORY_RM_TRIGGER ||
9810 action_arg == CA_ARG_INVENTORY_RM_ACTION)
9812 if (player->inventory_infinite_element != EL_UNDEFINED &&
9813 IS_EQUAL_OR_IN_GROUP(player->inventory_infinite_element,
9814 action_arg_element_raw))
9815 player->inventory_infinite_element = EL_UNDEFINED;
9817 for (k = 0, j = 0; j < player->inventory_size; j++)
9819 if (!IS_EQUAL_OR_IN_GROUP(player->inventory_element[j],
9820 action_arg_element_raw))
9821 player->inventory_element[k++] = player->inventory_element[j];
9824 player->inventory_size = k;
9826 else if (action_arg == CA_ARG_INVENTORY_RM_FIRST)
9828 if (player->inventory_size > 0)
9830 for (j = 0; j < player->inventory_size - 1; j++)
9831 player->inventory_element[j] = player->inventory_element[j + 1];
9833 player->inventory_size--;
9836 else if (action_arg == CA_ARG_INVENTORY_RM_LAST)
9838 if (player->inventory_size > 0)
9839 player->inventory_size--;
9841 else if (action_arg == CA_ARG_INVENTORY_RM_ALL)
9843 player->inventory_infinite_element = EL_UNDEFINED;
9844 player->inventory_size = 0;
9846 else if (action_arg == CA_ARG_INVENTORY_RESET)
9848 player->inventory_infinite_element = EL_UNDEFINED;
9849 player->inventory_size = 0;
9851 if (level.use_initial_inventory[i])
9853 for (j = 0; j < level.initial_inventory_size[i]; j++)
9855 int element = level.initial_inventory_content[i][j];
9856 int collect_count = element_info[element].collect_count_initial;
9858 if (!IS_CUSTOM_ELEMENT(element))
9861 if (collect_count == 0)
9862 player->inventory_infinite_element = element;
9864 for (k = 0; k < collect_count; k++)
9865 if (player->inventory_size < MAX_INVENTORY_SIZE)
9866 player->inventory_element[player->inventory_size++] =
9877 /* ---------- CE actions ---------------------------------------------- */
9879 case CA_SET_CE_VALUE:
9881 int last_ce_value = CustomValue[x][y];
9883 CustomValue[x][y] = action_arg_number_new;
9885 if (CustomValue[x][y] != last_ce_value)
9887 CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_CHANGES);
9888 CheckTriggeredElementChange(x, y, element, CE_VALUE_CHANGES_OF_X);
9890 if (CustomValue[x][y] == 0)
9892 CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
9893 CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
9900 case CA_SET_CE_SCORE:
9902 int last_ce_score = ei->collect_score;
9904 ei->collect_score = action_arg_number_new;
9906 if (ei->collect_score != last_ce_score)
9908 CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
9909 CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
9911 if (ei->collect_score == 0)
9915 CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
9916 CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
9919 This is a very special case that seems to be a mixture between
9920 CheckElementChange() and CheckTriggeredElementChange(): while
9921 the first one only affects single elements that are triggered
9922 directly, the second one affects multiple elements in the playfield
9923 that are triggered indirectly by another element. This is a third
9924 case: Changing the CE score always affects multiple identical CEs,
9925 so every affected CE must be checked, not only the single CE for
9926 which the CE score was changed in the first place (as every instance
9927 of that CE shares the same CE score, and therefore also can change)!
9929 SCAN_PLAYFIELD(xx, yy)
9931 if (Feld[xx][yy] == element)
9932 CheckElementChange(xx, yy, element, EL_UNDEFINED,
9933 CE_SCORE_GETS_ZERO);
9941 case CA_SET_CE_ARTWORK:
9943 int artwork_element = action_arg_element;
9944 boolean reset_frame = FALSE;
9947 if (action_arg == CA_ARG_ELEMENT_RESET)
9948 artwork_element = (ei->use_gfx_element ? ei->gfx_element_initial :
9951 if (ei->gfx_element != artwork_element)
9954 ei->gfx_element = artwork_element;
9956 SCAN_PLAYFIELD(xx, yy)
9958 if (Feld[xx][yy] == element)
9962 ResetGfxAnimation(xx, yy);
9963 ResetRandomAnimationValue(xx, yy);
9966 TEST_DrawLevelField(xx, yy);
9973 /* ---------- engine actions ------------------------------------------ */
9975 case CA_SET_ENGINE_SCAN_MODE:
9977 InitPlayfieldScanMode(action_arg);
9987 static void CreateFieldExt(int x, int y, int element, boolean is_change)
9989 int old_element = Feld[x][y];
9990 int new_element = GetElementFromGroupElement(element);
9991 int previous_move_direction = MovDir[x][y];
9992 int last_ce_value = CustomValue[x][y];
9993 boolean player_explosion_protected = PLAYER_EXPLOSION_PROTECTED(x, y);
9994 boolean new_element_is_player = ELEM_IS_PLAYER(new_element);
9995 boolean add_player_onto_element = (new_element_is_player &&
9996 new_element != EL_SOKOBAN_FIELD_PLAYER &&
9997 IS_WALKABLE(old_element));
9999 if (!add_player_onto_element)
10001 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
10002 RemoveMovingField(x, y);
10006 Feld[x][y] = new_element;
10008 if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
10009 MovDir[x][y] = previous_move_direction;
10011 if (element_info[new_element].use_last_ce_value)
10012 CustomValue[x][y] = last_ce_value;
10014 InitField_WithBug1(x, y, FALSE);
10016 new_element = Feld[x][y]; /* element may have changed */
10018 ResetGfxAnimation(x, y);
10019 ResetRandomAnimationValue(x, y);
10022 if (x == DEBUG_GFXFRAME_X && y == DEBUG_GFXFRAME_Y)
10023 printf(" (RESET X)");
10026 TEST_DrawLevelField(x, y);
10028 if (GFX_CRUMBLED(new_element))
10029 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
10032 /* check if element under the player changes from accessible to unaccessible
10033 (needed for special case of dropping element which then changes) */
10034 /* (must be checked after creating new element for walkable group elements) */
10035 if (IS_PLAYER(x, y) && !player_explosion_protected &&
10036 IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
10043 /* "ChangeCount" not set yet to allow "entered by player" change one time */
10044 if (new_element_is_player)
10045 RelocatePlayer(x, y, new_element);
10048 ChangeCount[x][y]++; /* count number of changes in the same frame */
10050 TestIfBadThingTouchesPlayer(x, y);
10051 TestIfPlayerTouchesCustomElement(x, y);
10052 TestIfElementTouchesCustomElement(x, y);
10055 static void CreateField(int x, int y, int element)
10057 CreateFieldExt(x, y, element, FALSE);
10060 static void CreateElementFromChange(int x, int y, int element)
10062 element = GET_VALID_RUNTIME_ELEMENT(element);
10064 if (game.engine_version >= VERSION_IDENT(3,2,0,7))
10066 int old_element = Feld[x][y];
10068 /* prevent changed element from moving in same engine frame
10069 unless both old and new element can either fall or move */
10070 if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
10071 (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
10075 CreateFieldExt(x, y, element, TRUE);
10078 static boolean ChangeElement(int x, int y, int element, int page)
10080 struct ElementInfo *ei = &element_info[element];
10081 struct ElementChangeInfo *change = &ei->change_page[page];
10082 int ce_value = CustomValue[x][y];
10083 int ce_score = ei->collect_score;
10084 int target_element;
10085 int old_element = Feld[x][y];
10087 /* always use default change event to prevent running into a loop */
10088 if (ChangeEvent[x][y] == -1)
10089 ChangeEvent[x][y] = CE_DELAY;
10091 if (ChangeEvent[x][y] == CE_DELAY)
10093 /* reset actual trigger element, trigger player and action element */
10094 change->actual_trigger_element = EL_EMPTY;
10095 change->actual_trigger_player = EL_EMPTY;
10096 change->actual_trigger_player_bits = CH_PLAYER_NONE;
10097 change->actual_trigger_side = CH_SIDE_NONE;
10098 change->actual_trigger_ce_value = 0;
10099 change->actual_trigger_ce_score = 0;
10102 /* do not change elements more than a specified maximum number of changes */
10103 if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
10106 ChangeCount[x][y]++; /* count number of changes in the same frame */
10108 if (change->explode)
10115 if (change->use_target_content)
10117 boolean complete_replace = TRUE;
10118 boolean can_replace[3][3];
10121 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10124 boolean is_walkable;
10125 boolean is_diggable;
10126 boolean is_collectible;
10127 boolean is_removable;
10128 boolean is_destructible;
10129 int ex = x + xx - 1;
10130 int ey = y + yy - 1;
10131 int content_element = change->target_content.e[xx][yy];
10134 can_replace[xx][yy] = TRUE;
10136 if (ex == x && ey == y) /* do not check changing element itself */
10139 if (content_element == EL_EMPTY_SPACE)
10141 can_replace[xx][yy] = FALSE; /* do not replace border with space */
10146 if (!IN_LEV_FIELD(ex, ey))
10148 can_replace[xx][yy] = FALSE;
10149 complete_replace = FALSE;
10156 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10157 e = MovingOrBlocked2Element(ex, ey);
10159 is_empty = (IS_FREE(ex, ey) ||
10160 (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
10162 is_walkable = (is_empty || IS_WALKABLE(e));
10163 is_diggable = (is_empty || IS_DIGGABLE(e));
10164 is_collectible = (is_empty || IS_COLLECTIBLE(e));
10165 is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
10166 is_removable = (is_diggable || is_collectible);
10168 can_replace[xx][yy] =
10169 (((change->replace_when == CP_WHEN_EMPTY && is_empty) ||
10170 (change->replace_when == CP_WHEN_WALKABLE && is_walkable) ||
10171 (change->replace_when == CP_WHEN_DIGGABLE && is_diggable) ||
10172 (change->replace_when == CP_WHEN_COLLECTIBLE && is_collectible) ||
10173 (change->replace_when == CP_WHEN_REMOVABLE && is_removable) ||
10174 (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
10175 !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
10177 if (!can_replace[xx][yy])
10178 complete_replace = FALSE;
10181 if (!change->only_if_complete || complete_replace)
10183 boolean something_has_changed = FALSE;
10185 if (change->only_if_complete && change->use_random_replace &&
10186 RND(100) < change->random_percentage)
10189 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10191 int ex = x + xx - 1;
10192 int ey = y + yy - 1;
10193 int content_element;
10195 if (can_replace[xx][yy] && (!change->use_random_replace ||
10196 RND(100) < change->random_percentage))
10198 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10199 RemoveMovingField(ex, ey);
10201 ChangeEvent[ex][ey] = ChangeEvent[x][y];
10203 content_element = change->target_content.e[xx][yy];
10204 target_element = GET_TARGET_ELEMENT(element, content_element, change,
10205 ce_value, ce_score);
10207 CreateElementFromChange(ex, ey, target_element);
10209 something_has_changed = TRUE;
10211 /* for symmetry reasons, freeze newly created border elements */
10212 if (ex != x || ey != y)
10213 Stop[ex][ey] = TRUE; /* no more moving in this frame */
10217 if (something_has_changed)
10219 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10220 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10226 target_element = GET_TARGET_ELEMENT(element, change->target_element, change,
10227 ce_value, ce_score);
10229 if (element == EL_DIAGONAL_GROWING ||
10230 element == EL_DIAGONAL_SHRINKING)
10232 target_element = Store[x][y];
10234 Store[x][y] = EL_EMPTY;
10237 CreateElementFromChange(x, y, target_element);
10239 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10240 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10243 /* this uses direct change before indirect change */
10244 CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
10249 static void HandleElementChange(int x, int y, int page)
10251 int element = MovingOrBlocked2Element(x, y);
10252 struct ElementInfo *ei = &element_info[element];
10253 struct ElementChangeInfo *change = &ei->change_page[page];
10254 boolean handle_action_before_change = FALSE;
10257 if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
10258 !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
10261 printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
10262 x, y, element, element_info[element].token_name);
10263 printf("HandleElementChange(): This should never happen!\n");
10268 /* this can happen with classic bombs on walkable, changing elements */
10269 if (!CAN_CHANGE_OR_HAS_ACTION(element))
10274 if (ChangeDelay[x][y] == 0) /* initialize element change */
10276 ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
10278 if (change->can_change)
10280 /* !!! not clear why graphic animation should be reset at all here !!! */
10281 /* !!! UPDATE: but is needed for correct Snake Bite tail animation !!! */
10282 /* !!! SOLUTION: do not reset if graphics engine set to 4 or above !!! */
10285 GRAPHICAL BUG ADDRESSED BY CHECKING GRAPHICS ENGINE VERSION:
10287 When using an animation frame delay of 1 (this only happens with
10288 "sp_zonk.moving.left/right" in the classic graphics), the default
10289 (non-moving) animation shows wrong animation frames (while the
10290 moving animation, like "sp_zonk.moving.left/right", is correct,
10291 so this graphical bug never shows up with the classic graphics).
10292 For an animation with 4 frames, this causes wrong frames 0,0,1,2
10293 be drawn instead of the correct frames 0,1,2,3. This is caused by
10294 "GfxFrame[][]" being reset *twice* (in two successive frames) after
10295 an element change: First when the change delay ("ChangeDelay[][]")
10296 counter has reached zero after decrementing (see "RESET 1" below),
10297 then a second time in the next frame (after "GfxFrame[][]" was
10298 already incremented) when "ChangeDelay[][]" is reset to the initial
10299 delay value again (see "RESET 2" below).
10301 This causes frame 0 to be drawn twice, while the last frame won't
10302 be drawn anymore, resulting in the wrong frame sequence 0,0,1,2.
10304 As some animations may already be cleverly designed around this bug
10305 (at least the "Snake Bite" snake tail animation does this), it cannot
10306 simply be fixed here without breaking such existing animations.
10307 Unfortunately, it cannot easily be detected if a graphics set was
10308 designed "before" or "after" the bug was fixed. As a workaround,
10309 a new graphics set option "game.graphics_engine_version" was added
10310 to be able to specify the game's major release version for which the
10311 graphics set was designed, which can then be used to decide if the
10312 bugfix should be used (version 4 and above) or not (version 3 or
10313 below, or if no version was specified at all, as with old sets).
10315 (The wrong/fixed animation frames can be tested with the test level set
10316 "test_gfxframe" and level "000", which contains a specially prepared
10317 custom element at level position (x/y) == (11/9) which uses the zonk
10318 animation mentioned above. Using "game.graphics_engine_version: 4"
10319 fixes the wrong animation frames, showing the correct frames 0,1,2,3.
10320 This can also be seen from the debug output for this test element.)
10323 /* when a custom element is about to change (for example by change delay),
10324 do not reset graphic animation when the custom element is moving */
10325 if (game.graphics_engine_version < 4 &&
10328 ResetGfxAnimation(x, y);
10329 ResetRandomAnimationValue(x, y);
10332 if (x == DEBUG_GFXFRAME_X && y == DEBUG_GFXFRAME_Y)
10333 printf(" (RESET 2)");
10338 if (change->pre_change_function)
10339 change->pre_change_function(x, y);
10343 ChangeDelay[x][y]--;
10345 if (ChangeDelay[x][y] != 0) /* continue element change */
10347 if (change->can_change)
10349 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10351 if (IS_ANIMATED(graphic))
10352 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10354 if (change->change_function)
10355 change->change_function(x, y);
10358 else /* finish element change */
10360 if (ChangePage[x][y] != -1) /* remember page from delayed change */
10362 page = ChangePage[x][y];
10363 ChangePage[x][y] = -1;
10365 change = &ei->change_page[page];
10368 if (IS_MOVING(x, y)) /* never change a running system ;-) */
10370 ChangeDelay[x][y] = 1; /* try change after next move step */
10371 ChangePage[x][y] = page; /* remember page to use for change */
10376 /* special case: set new level random seed before changing element */
10377 if (change->has_action && change->action_type == CA_SET_LEVEL_RANDOM_SEED)
10378 handle_action_before_change = TRUE;
10380 if (change->has_action && handle_action_before_change)
10381 ExecuteCustomElementAction(x, y, element, page);
10383 if (change->can_change)
10385 if (ChangeElement(x, y, element, page))
10387 if (change->post_change_function)
10388 change->post_change_function(x, y);
10392 if (x == DEBUG_GFXFRAME_X && y == DEBUG_GFXFRAME_Y)
10393 printf(" (RESET 1)");
10398 if (change->has_action && !handle_action_before_change)
10399 ExecuteCustomElementAction(x, y, element, page);
10403 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
10404 int trigger_element,
10406 int trigger_player,
10410 boolean change_done_any = FALSE;
10411 int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
10414 if (!(trigger_events[trigger_element][trigger_event]))
10417 RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
10419 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
10421 int element = EL_CUSTOM_START + i;
10422 boolean change_done = FALSE;
10425 if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
10426 !HAS_ANY_CHANGE_EVENT(element, trigger_event))
10429 for (p = 0; p < element_info[element].num_change_pages; p++)
10431 struct ElementChangeInfo *change = &element_info[element].change_page[p];
10433 if (change->can_change_or_has_action &&
10434 change->has_event[trigger_event] &&
10435 change->trigger_side & trigger_side &&
10436 change->trigger_player & trigger_player &&
10437 change->trigger_page & trigger_page_bits &&
10438 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
10440 change->actual_trigger_element = trigger_element;
10441 change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
10442 change->actual_trigger_player_bits = trigger_player;
10443 change->actual_trigger_side = trigger_side;
10444 change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
10445 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10447 if ((change->can_change && !change_done) || change->has_action)
10451 SCAN_PLAYFIELD(x, y)
10453 if (Feld[x][y] == element)
10455 if (change->can_change && !change_done)
10457 /* if element already changed in this frame, not only prevent
10458 another element change (checked in ChangeElement()), but
10459 also prevent additional element actions for this element */
10461 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
10462 !level.use_action_after_change_bug)
10465 ChangeDelay[x][y] = 1;
10466 ChangeEvent[x][y] = trigger_event;
10468 HandleElementChange(x, y, p);
10470 else if (change->has_action)
10472 /* if element already changed in this frame, not only prevent
10473 another element change (checked in ChangeElement()), but
10474 also prevent additional element actions for this element */
10476 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
10477 !level.use_action_after_change_bug)
10480 ExecuteCustomElementAction(x, y, element, p);
10481 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10486 if (change->can_change)
10488 change_done = TRUE;
10489 change_done_any = TRUE;
10496 RECURSION_LOOP_DETECTION_END();
10498 return change_done_any;
10501 static boolean CheckElementChangeExt(int x, int y,
10503 int trigger_element,
10505 int trigger_player,
10508 boolean change_done = FALSE;
10511 if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
10512 !HAS_ANY_CHANGE_EVENT(element, trigger_event))
10515 if (Feld[x][y] == EL_BLOCKED)
10517 Blocked2Moving(x, y, &x, &y);
10518 element = Feld[x][y];
10521 /* check if element has already changed or is about to change after moving */
10522 if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
10523 Feld[x][y] != element) ||
10525 (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
10526 (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
10527 ChangePage[x][y] != -1)))
10530 RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
10532 for (p = 0; p < element_info[element].num_change_pages; p++)
10534 struct ElementChangeInfo *change = &element_info[element].change_page[p];
10536 /* check trigger element for all events where the element that is checked
10537 for changing interacts with a directly adjacent element -- this is
10538 different to element changes that affect other elements to change on the
10539 whole playfield (which is handeld by CheckTriggeredElementChangeExt()) */
10540 boolean check_trigger_element =
10541 (trigger_event == CE_TOUCHING_X ||
10542 trigger_event == CE_HITTING_X ||
10543 trigger_event == CE_HIT_BY_X ||
10544 trigger_event == CE_DIGGING_X); /* this one was forgotten until 3.2.3 */
10546 if (change->can_change_or_has_action &&
10547 change->has_event[trigger_event] &&
10548 change->trigger_side & trigger_side &&
10549 change->trigger_player & trigger_player &&
10550 (!check_trigger_element ||
10551 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
10553 change->actual_trigger_element = trigger_element;
10554 change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
10555 change->actual_trigger_player_bits = trigger_player;
10556 change->actual_trigger_side = trigger_side;
10557 change->actual_trigger_ce_value = CustomValue[x][y];
10558 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10560 /* special case: trigger element not at (x,y) position for some events */
10561 if (check_trigger_element)
10573 { 0, 0 }, { 0, 0 }, { 0, 0 },
10577 int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
10578 int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
10580 change->actual_trigger_ce_value = CustomValue[xx][yy];
10581 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10584 if (change->can_change && !change_done)
10586 ChangeDelay[x][y] = 1;
10587 ChangeEvent[x][y] = trigger_event;
10589 HandleElementChange(x, y, p);
10591 change_done = TRUE;
10593 else if (change->has_action)
10595 ExecuteCustomElementAction(x, y, element, p);
10596 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10601 RECURSION_LOOP_DETECTION_END();
10603 return change_done;
10606 static void PlayPlayerSound(struct PlayerInfo *player)
10608 int jx = player->jx, jy = player->jy;
10609 int sound_element = player->artwork_element;
10610 int last_action = player->last_action_waiting;
10611 int action = player->action_waiting;
10613 if (player->is_waiting)
10615 if (action != last_action)
10616 PlayLevelSoundElementAction(jx, jy, sound_element, action);
10618 PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
10622 if (action != last_action)
10623 StopSound(element_info[sound_element].sound[last_action]);
10625 if (last_action == ACTION_SLEEPING)
10626 PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
10630 static void PlayAllPlayersSound()
10634 for (i = 0; i < MAX_PLAYERS; i++)
10635 if (stored_player[i].active)
10636 PlayPlayerSound(&stored_player[i]);
10639 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
10641 boolean last_waiting = player->is_waiting;
10642 int move_dir = player->MovDir;
10644 player->dir_waiting = move_dir;
10645 player->last_action_waiting = player->action_waiting;
10649 if (!last_waiting) /* not waiting -> waiting */
10651 player->is_waiting = TRUE;
10653 player->frame_counter_bored =
10655 game.player_boring_delay_fixed +
10656 GetSimpleRandom(game.player_boring_delay_random);
10657 player->frame_counter_sleeping =
10659 game.player_sleeping_delay_fixed +
10660 GetSimpleRandom(game.player_sleeping_delay_random);
10662 InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
10665 if (game.player_sleeping_delay_fixed +
10666 game.player_sleeping_delay_random > 0 &&
10667 player->anim_delay_counter == 0 &&
10668 player->post_delay_counter == 0 &&
10669 FrameCounter >= player->frame_counter_sleeping)
10670 player->is_sleeping = TRUE;
10671 else if (game.player_boring_delay_fixed +
10672 game.player_boring_delay_random > 0 &&
10673 FrameCounter >= player->frame_counter_bored)
10674 player->is_bored = TRUE;
10676 player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
10677 player->is_bored ? ACTION_BORING :
10680 if (player->is_sleeping && player->use_murphy)
10682 /* special case for sleeping Murphy when leaning against non-free tile */
10684 if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
10685 (Feld[player->jx - 1][player->jy] != EL_EMPTY &&
10686 !IS_MOVING(player->jx - 1, player->jy)))
10687 move_dir = MV_LEFT;
10688 else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
10689 (Feld[player->jx + 1][player->jy] != EL_EMPTY &&
10690 !IS_MOVING(player->jx + 1, player->jy)))
10691 move_dir = MV_RIGHT;
10693 player->is_sleeping = FALSE;
10695 player->dir_waiting = move_dir;
10698 if (player->is_sleeping)
10700 if (player->num_special_action_sleeping > 0)
10702 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
10704 int last_special_action = player->special_action_sleeping;
10705 int num_special_action = player->num_special_action_sleeping;
10706 int special_action =
10707 (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
10708 last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
10709 last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
10710 last_special_action + 1 : ACTION_SLEEPING);
10711 int special_graphic =
10712 el_act_dir2img(player->artwork_element, special_action, move_dir);
10714 player->anim_delay_counter =
10715 graphic_info[special_graphic].anim_delay_fixed +
10716 GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
10717 player->post_delay_counter =
10718 graphic_info[special_graphic].post_delay_fixed +
10719 GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
10721 player->special_action_sleeping = special_action;
10724 if (player->anim_delay_counter > 0)
10726 player->action_waiting = player->special_action_sleeping;
10727 player->anim_delay_counter--;
10729 else if (player->post_delay_counter > 0)
10731 player->post_delay_counter--;
10735 else if (player->is_bored)
10737 if (player->num_special_action_bored > 0)
10739 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
10741 int special_action =
10742 ACTION_BORING_1 + GetSimpleRandom(player->num_special_action_bored);
10743 int special_graphic =
10744 el_act_dir2img(player->artwork_element, special_action, move_dir);
10746 player->anim_delay_counter =
10747 graphic_info[special_graphic].anim_delay_fixed +
10748 GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
10749 player->post_delay_counter =
10750 graphic_info[special_graphic].post_delay_fixed +
10751 GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
10753 player->special_action_bored = special_action;
10756 if (player->anim_delay_counter > 0)
10758 player->action_waiting = player->special_action_bored;
10759 player->anim_delay_counter--;
10761 else if (player->post_delay_counter > 0)
10763 player->post_delay_counter--;
10768 else if (last_waiting) /* waiting -> not waiting */
10770 player->is_waiting = FALSE;
10771 player->is_bored = FALSE;
10772 player->is_sleeping = FALSE;
10774 player->frame_counter_bored = -1;
10775 player->frame_counter_sleeping = -1;
10777 player->anim_delay_counter = 0;
10778 player->post_delay_counter = 0;
10780 player->dir_waiting = player->MovDir;
10781 player->action_waiting = ACTION_DEFAULT;
10783 player->special_action_bored = ACTION_DEFAULT;
10784 player->special_action_sleeping = ACTION_DEFAULT;
10788 static void CheckSaveEngineSnapshot(struct PlayerInfo *player)
10790 static boolean player_was_moving = FALSE;
10791 static boolean player_was_snapping = FALSE;
10792 static boolean player_was_dropping = FALSE;
10794 if ((!player->is_moving && player_was_moving) ||
10795 (player->MovPos == 0 && player_was_moving) ||
10796 (player->is_snapping && !player_was_snapping) ||
10797 (player->is_dropping && !player_was_dropping))
10799 if (!SaveEngineSnapshotToList())
10802 player_was_moving = FALSE;
10803 player_was_snapping = TRUE;
10804 player_was_dropping = TRUE;
10808 if (player->is_moving)
10809 player_was_moving = TRUE;
10811 if (!player->is_snapping)
10812 player_was_snapping = FALSE;
10814 if (!player->is_dropping)
10815 player_was_dropping = FALSE;
10819 static void CheckSingleStepMode(struct PlayerInfo *player)
10821 if (tape.single_step && tape.recording && !tape.pausing)
10823 /* as it is called "single step mode", just return to pause mode when the
10824 player stopped moving after one tile (or never starts moving at all) */
10825 if (!player->is_moving && !player->is_pushing)
10827 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
10828 SnapField(player, 0, 0); /* stop snapping */
10832 CheckSaveEngineSnapshot(player);
10835 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
10837 int left = player_action & JOY_LEFT;
10838 int right = player_action & JOY_RIGHT;
10839 int up = player_action & JOY_UP;
10840 int down = player_action & JOY_DOWN;
10841 int button1 = player_action & JOY_BUTTON_1;
10842 int button2 = player_action & JOY_BUTTON_2;
10843 int dx = (left ? -1 : right ? 1 : 0);
10844 int dy = (up ? -1 : down ? 1 : 0);
10846 if (!player->active || tape.pausing)
10852 SnapField(player, dx, dy);
10856 DropElement(player);
10858 MovePlayer(player, dx, dy);
10861 CheckSingleStepMode(player);
10863 SetPlayerWaiting(player, FALSE);
10865 return player_action;
10869 /* no actions for this player (no input at player's configured device) */
10871 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
10872 SnapField(player, 0, 0);
10873 CheckGravityMovementWhenNotMoving(player);
10875 if (player->MovPos == 0)
10876 SetPlayerWaiting(player, TRUE);
10878 if (player->MovPos == 0) /* needed for tape.playing */
10879 player->is_moving = FALSE;
10881 player->is_dropping = FALSE;
10882 player->is_dropping_pressed = FALSE;
10883 player->drop_pressed_delay = 0;
10885 CheckSingleStepMode(player);
10891 static void CheckLevelTime()
10895 /* !!! SAME CODE AS IN "GameActions()" -- FIX THIS !!! */
10896 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
10898 if (level.native_em_level->lev->home == 0) /* all players at home */
10900 PlayerWins(local_player);
10902 AllPlayersGone = TRUE;
10904 level.native_em_level->lev->home = -1;
10907 if (level.native_em_level->ply[0]->alive == 0 &&
10908 level.native_em_level->ply[1]->alive == 0 &&
10909 level.native_em_level->ply[2]->alive == 0 &&
10910 level.native_em_level->ply[3]->alive == 0) /* all dead */
10911 AllPlayersGone = TRUE;
10913 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
10915 if (game_sp.LevelSolved &&
10916 !game_sp.GameOver) /* game won */
10918 PlayerWins(local_player);
10920 game_sp.GameOver = TRUE;
10922 AllPlayersGone = TRUE;
10925 if (game_sp.GameOver) /* game lost */
10926 AllPlayersGone = TRUE;
10929 if (TimeFrames >= FRAMES_PER_SECOND)
10934 for (i = 0; i < MAX_PLAYERS; i++)
10936 struct PlayerInfo *player = &stored_player[i];
10938 if (SHIELD_ON(player))
10940 player->shield_normal_time_left--;
10942 if (player->shield_deadly_time_left > 0)
10943 player->shield_deadly_time_left--;
10947 if (!local_player->LevelSolved && !level.use_step_counter)
10955 if (TimeLeft <= 10 && setup.time_limit)
10956 PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
10958 /* this does not make sense: game_panel_controls[GAME_PANEL_TIME].value
10959 is reset from other values in UpdateGameDoorValues() -- FIX THIS */
10961 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
10963 if (!TimeLeft && setup.time_limit)
10965 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
10966 level.native_em_level->lev->killed_out_of_time = TRUE;
10968 for (i = 0; i < MAX_PLAYERS; i++)
10969 KillPlayer(&stored_player[i]);
10972 else if (game.no_time_limit && !AllPlayersGone) /* level w/o time limit */
10974 game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
10977 level.native_em_level->lev->time =
10978 (game.no_time_limit ? TimePlayed : TimeLeft);
10981 if (tape.recording || tape.playing)
10982 DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
10985 if (tape.recording || tape.playing)
10986 DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
10988 UpdateAndDisplayGameControlValues();
10991 void AdvanceFrameAndPlayerCounters(int player_nr)
10995 /* advance frame counters (global frame counter and time frame counter) */
10999 /* advance player counters (counters for move delay, move animation etc.) */
11000 for (i = 0; i < MAX_PLAYERS; i++)
11002 boolean advance_player_counters = (player_nr == -1 || player_nr == i);
11003 int move_delay_value = stored_player[i].move_delay_value;
11004 int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
11006 if (!advance_player_counters) /* not all players may be affected */
11009 if (move_frames == 0) /* less than one move per game frame */
11011 int stepsize = TILEX / move_delay_value;
11012 int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
11013 int count = (stored_player[i].is_moving ?
11014 ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
11016 if (count % delay == 0)
11020 stored_player[i].Frame += move_frames;
11022 if (stored_player[i].MovPos != 0)
11023 stored_player[i].StepFrame += move_frames;
11025 if (stored_player[i].move_delay > 0)
11026 stored_player[i].move_delay--;
11028 /* due to bugs in previous versions, counter must count up, not down */
11029 if (stored_player[i].push_delay != -1)
11030 stored_player[i].push_delay++;
11032 if (stored_player[i].drop_delay > 0)
11033 stored_player[i].drop_delay--;
11035 if (stored_player[i].is_dropping_pressed)
11036 stored_player[i].drop_pressed_delay++;
11040 void StartGameActions(boolean init_network_game, boolean record_tape,
11043 unsigned int new_random_seed = InitRND(random_seed);
11046 TapeStartRecording(new_random_seed);
11048 #if defined(NETWORK_AVALIABLE)
11049 if (init_network_game)
11051 SendToServer_StartPlaying();
11062 static unsigned int game_frame_delay = 0;
11063 unsigned int game_frame_delay_value;
11064 byte *recorded_player_action;
11065 byte summarized_player_action = 0;
11066 byte tape_action[MAX_PLAYERS];
11069 /* detect endless loops, caused by custom element programming */
11070 if (recursion_loop_detected && recursion_loop_depth == 0)
11072 char *message = getStringCat3("Internal Error! Element ",
11073 EL_NAME(recursion_loop_element),
11074 " caused endless loop! Quit the game?");
11076 Error(ERR_WARN, "element '%s' caused endless loop in game engine",
11077 EL_NAME(recursion_loop_element));
11079 RequestQuitGameExt(FALSE, level_editor_test_game, message);
11081 recursion_loop_detected = FALSE; /* if game should be continued */
11088 if (game.restart_level)
11089 StartGameActions(options.network, setup.autorecord, level.random_seed);
11091 /* !!! SAME CODE AS IN "CheckLevelTime()" -- FIX THIS !!! */
11092 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11094 if (level.native_em_level->lev->home == 0) /* all players at home */
11096 PlayerWins(local_player);
11098 AllPlayersGone = TRUE;
11100 level.native_em_level->lev->home = -1;
11103 if (level.native_em_level->ply[0]->alive == 0 &&
11104 level.native_em_level->ply[1]->alive == 0 &&
11105 level.native_em_level->ply[2]->alive == 0 &&
11106 level.native_em_level->ply[3]->alive == 0) /* all dead */
11107 AllPlayersGone = TRUE;
11109 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11111 if (game_sp.LevelSolved &&
11112 !game_sp.GameOver) /* game won */
11114 PlayerWins(local_player);
11116 game_sp.GameOver = TRUE;
11118 AllPlayersGone = TRUE;
11121 if (game_sp.GameOver) /* game lost */
11122 AllPlayersGone = TRUE;
11125 if (local_player->LevelSolved && !local_player->LevelSolved_GameEnd)
11128 if (AllPlayersGone && !TAPE_IS_STOPPED(tape))
11131 if (game_status != GAME_MODE_PLAYING) /* status might have changed */
11134 game_frame_delay_value =
11135 (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
11137 if (tape.playing && tape.warp_forward && !tape.pausing)
11138 game_frame_delay_value = 0;
11141 /* ---------- main game synchronization point ---------- */
11143 int skip = WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11145 printf("::: skip == %d\n", skip);
11148 /* ---------- main game synchronization point ---------- */
11150 WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11153 if (network_playing && !network_player_action_received)
11155 /* try to get network player actions in time */
11157 #if defined(NETWORK_AVALIABLE)
11158 /* last chance to get network player actions without main loop delay */
11159 HandleNetworking();
11162 /* game was quit by network peer */
11163 if (game_status != GAME_MODE_PLAYING)
11166 if (!network_player_action_received)
11167 return; /* failed to get network player actions in time */
11169 /* do not yet reset "network_player_action_received" (for tape.pausing) */
11175 /* at this point we know that we really continue executing the game */
11177 network_player_action_received = FALSE;
11179 /* when playing tape, read previously recorded player input from tape data */
11180 recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
11182 /* TapePlayAction() may return NULL when toggling to "pause before death" */
11186 if (tape.set_centered_player)
11188 game.centered_player_nr_next = tape.centered_player_nr_next;
11189 game.set_centered_player = TRUE;
11192 for (i = 0; i < MAX_PLAYERS; i++)
11194 summarized_player_action |= stored_player[i].action;
11196 if (!network_playing && (game.team_mode || tape.playing))
11197 stored_player[i].effective_action = stored_player[i].action;
11200 #if defined(NETWORK_AVALIABLE)
11201 if (network_playing)
11202 SendToServer_MovePlayer(summarized_player_action);
11205 if (!options.network && !game.team_mode)
11206 local_player->effective_action = summarized_player_action;
11208 if (tape.recording &&
11210 setup.input_on_focus &&
11211 game.centered_player_nr != -1)
11213 for (i = 0; i < MAX_PLAYERS; i++)
11214 stored_player[i].effective_action =
11215 (i == game.centered_player_nr ? summarized_player_action : 0);
11218 if (recorded_player_action != NULL)
11219 for (i = 0; i < MAX_PLAYERS; i++)
11220 stored_player[i].effective_action = recorded_player_action[i];
11222 for (i = 0; i < MAX_PLAYERS; i++)
11224 tape_action[i] = stored_player[i].effective_action;
11226 /* (this may happen in the RND game engine if a player was not present on
11227 the playfield on level start, but appeared later from a custom element */
11228 if (setup.team_mode &&
11231 !tape.player_participates[i])
11232 tape.player_participates[i] = TRUE;
11235 /* only record actions from input devices, but not programmed actions */
11236 if (tape.recording)
11237 TapeRecordAction(tape_action);
11239 #if USE_NEW_PLAYER_ASSIGNMENTS
11240 // !!! also map player actions in single player mode !!!
11241 // if (game.team_mode)
11243 byte mapped_action[MAX_PLAYERS];
11245 #if DEBUG_PLAYER_ACTIONS
11247 for (i = 0; i < MAX_PLAYERS; i++)
11248 printf(" %d, ", stored_player[i].effective_action);
11251 for (i = 0; i < MAX_PLAYERS; i++)
11252 mapped_action[i] = stored_player[map_player_action[i]].effective_action;
11254 for (i = 0; i < MAX_PLAYERS; i++)
11255 stored_player[i].effective_action = mapped_action[i];
11257 #if DEBUG_PLAYER_ACTIONS
11259 for (i = 0; i < MAX_PLAYERS; i++)
11260 printf(" %d, ", stored_player[i].effective_action);
11264 #if DEBUG_PLAYER_ACTIONS
11268 for (i = 0; i < MAX_PLAYERS; i++)
11269 printf(" %d, ", stored_player[i].effective_action);
11275 for (i = 0; i < MAX_PLAYERS; i++)
11277 // allow engine snapshot in case of changed movement attempt
11278 if ((game.snapshot.last_action[i] & KEY_MOTION) !=
11279 (stored_player[i].effective_action & KEY_MOTION))
11280 game.snapshot.changed_action = TRUE;
11282 // allow engine snapshot in case of snapping/dropping attempt
11283 if ((game.snapshot.last_action[i] & KEY_BUTTON) == 0 &&
11284 (stored_player[i].effective_action & KEY_BUTTON) != 0)
11285 game.snapshot.changed_action = TRUE;
11287 game.snapshot.last_action[i] = stored_player[i].effective_action;
11290 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11292 GameActions_EM_Main();
11294 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11296 GameActions_SP_Main();
11300 GameActions_RND_Main();
11303 BlitScreenToBitmap(backbuffer);
11307 AdvanceFrameAndPlayerCounters(-1); /* advance counters for all players */
11309 if (options.debug) /* calculate frames per second */
11311 static unsigned int fps_counter = 0;
11312 static int fps_frames = 0;
11313 unsigned int fps_delay_ms = Counter() - fps_counter;
11317 if (fps_delay_ms >= 500) /* calculate fps every 0.5 seconds */
11319 global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
11322 fps_counter = Counter();
11325 redraw_mask |= REDRAW_FPS;
11329 void GameActions_EM_Main()
11331 byte effective_action[MAX_PLAYERS];
11332 boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11335 for (i = 0; i < MAX_PLAYERS; i++)
11336 effective_action[i] = stored_player[i].effective_action;
11338 GameActions_EM(effective_action, warp_mode);
11341 void GameActions_SP_Main()
11343 byte effective_action[MAX_PLAYERS];
11344 boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11347 for (i = 0; i < MAX_PLAYERS; i++)
11348 effective_action[i] = stored_player[i].effective_action;
11350 GameActions_SP(effective_action, warp_mode);
11353 void GameActions_RND_Main()
11358 void GameActions_RND()
11360 int magic_wall_x = 0, magic_wall_y = 0;
11361 int i, x, y, element, graphic;
11363 InitPlayfieldScanModeVars();
11365 if (game.engine_version >= VERSION_IDENT(3,2,0,7))
11367 SCAN_PLAYFIELD(x, y)
11369 ChangeCount[x][y] = 0;
11370 ChangeEvent[x][y] = -1;
11374 if (game.set_centered_player)
11376 boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
11378 /* switching to "all players" only possible if all players fit to screen */
11379 if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
11381 game.centered_player_nr_next = game.centered_player_nr;
11382 game.set_centered_player = FALSE;
11385 /* do not switch focus to non-existing (or non-active) player */
11386 if (game.centered_player_nr_next >= 0 &&
11387 !stored_player[game.centered_player_nr_next].active)
11389 game.centered_player_nr_next = game.centered_player_nr;
11390 game.set_centered_player = FALSE;
11394 if (game.set_centered_player &&
11395 ScreenMovPos == 0) /* screen currently aligned at tile position */
11399 if (game.centered_player_nr_next == -1)
11401 setScreenCenteredToAllPlayers(&sx, &sy);
11405 sx = stored_player[game.centered_player_nr_next].jx;
11406 sy = stored_player[game.centered_player_nr_next].jy;
11409 game.centered_player_nr = game.centered_player_nr_next;
11410 game.set_centered_player = FALSE;
11412 DrawRelocateScreen(0, 0, sx, sy, MV_NONE, TRUE, setup.quick_switch);
11413 DrawGameDoorValues();
11416 for (i = 0; i < MAX_PLAYERS; i++)
11418 int actual_player_action = stored_player[i].effective_action;
11421 /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
11422 - rnd_equinox_tetrachloride 048
11423 - rnd_equinox_tetrachloride_ii 096
11424 - rnd_emanuel_schmieg 002
11425 - doctor_sloan_ww 001, 020
11427 if (stored_player[i].MovPos == 0)
11428 CheckGravityMovement(&stored_player[i]);
11431 /* overwrite programmed action with tape action */
11432 if (stored_player[i].programmed_action)
11433 actual_player_action = stored_player[i].programmed_action;
11435 PlayerActions(&stored_player[i], actual_player_action);
11437 ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
11440 ScrollScreen(NULL, SCROLL_GO_ON);
11442 /* for backwards compatibility, the following code emulates a fixed bug that
11443 occured when pushing elements (causing elements that just made their last
11444 pushing step to already (if possible) make their first falling step in the
11445 same game frame, which is bad); this code is also needed to use the famous
11446 "spring push bug" which is used in older levels and might be wanted to be
11447 used also in newer levels, but in this case the buggy pushing code is only
11448 affecting the "spring" element and no other elements */
11450 if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
11452 for (i = 0; i < MAX_PLAYERS; i++)
11454 struct PlayerInfo *player = &stored_player[i];
11455 int x = player->jx;
11456 int y = player->jy;
11458 if (player->active && player->is_pushing && player->is_moving &&
11460 (game.engine_version < VERSION_IDENT(2,2,0,7) ||
11461 Feld[x][y] == EL_SPRING))
11463 ContinueMoving(x, y);
11465 /* continue moving after pushing (this is actually a bug) */
11466 if (!IS_MOVING(x, y))
11467 Stop[x][y] = FALSE;
11473 printf("::: %d", GfxFrame[DEBUG_GFXFRAME_X][DEBUG_GFXFRAME_Y]);
11476 SCAN_PLAYFIELD(x, y)
11478 ChangeCount[x][y] = 0;
11479 ChangeEvent[x][y] = -1;
11481 /* this must be handled before main playfield loop */
11482 if (Feld[x][y] == EL_PLAYER_IS_LEAVING)
11485 if (MovDelay[x][y] <= 0)
11489 if (Feld[x][y] == EL_ELEMENT_SNAPPING)
11492 if (MovDelay[x][y] <= 0)
11495 TEST_DrawLevelField(x, y);
11497 TestIfElementTouchesCustomElement(x, y); /* for empty space */
11502 if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
11504 printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
11505 printf("GameActions(): This should never happen!\n");
11507 ChangePage[x][y] = -1;
11511 Stop[x][y] = FALSE;
11512 if (WasJustMoving[x][y] > 0)
11513 WasJustMoving[x][y]--;
11514 if (WasJustFalling[x][y] > 0)
11515 WasJustFalling[x][y]--;
11516 if (CheckCollision[x][y] > 0)
11517 CheckCollision[x][y]--;
11518 if (CheckImpact[x][y] > 0)
11519 CheckImpact[x][y]--;
11523 /* reset finished pushing action (not done in ContinueMoving() to allow
11524 continuous pushing animation for elements with zero push delay) */
11525 if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
11527 ResetGfxAnimation(x, y);
11528 TEST_DrawLevelField(x, y);
11532 if (IS_BLOCKED(x, y))
11536 Blocked2Moving(x, y, &oldx, &oldy);
11537 if (!IS_MOVING(oldx, oldy))
11539 printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
11540 printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
11541 printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
11542 printf("GameActions(): This should never happen!\n");
11549 printf(" -> %d", GfxFrame[DEBUG_GFXFRAME_X][DEBUG_GFXFRAME_Y]);
11552 SCAN_PLAYFIELD(x, y)
11554 element = Feld[x][y];
11555 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11557 ResetGfxFrame(x, y, TRUE);
11559 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
11560 IS_NEXT_FRAME(GfxFrame[x][y], graphic))
11561 ResetRandomAnimationValue(x, y);
11563 SetRandomAnimationValue(x, y);
11565 PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
11567 if (IS_INACTIVE(element))
11569 if (IS_ANIMATED(graphic))
11570 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11575 /* this may take place after moving, so 'element' may have changed */
11576 if (IS_CHANGING(x, y) &&
11577 (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
11579 int page = element_info[element].event_page_nr[CE_DELAY];
11581 HandleElementChange(x, y, page);
11583 element = Feld[x][y];
11584 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11587 if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
11591 element = Feld[x][y];
11592 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11594 if (IS_ANIMATED(graphic) &&
11595 !IS_MOVING(x, y) &&
11597 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11599 if (IS_GEM(element) || element == EL_SP_INFOTRON)
11600 TEST_DrawTwinkleOnField(x, y);
11602 else if ((element == EL_ACID ||
11603 element == EL_EXIT_OPEN ||
11604 element == EL_EM_EXIT_OPEN ||
11605 element == EL_SP_EXIT_OPEN ||
11606 element == EL_STEEL_EXIT_OPEN ||
11607 element == EL_EM_STEEL_EXIT_OPEN ||
11608 element == EL_SP_TERMINAL ||
11609 element == EL_SP_TERMINAL_ACTIVE ||
11610 element == EL_EXTRA_TIME ||
11611 element == EL_SHIELD_NORMAL ||
11612 element == EL_SHIELD_DEADLY) &&
11613 IS_ANIMATED(graphic))
11614 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11615 else if (IS_MOVING(x, y))
11616 ContinueMoving(x, y);
11617 else if (IS_ACTIVE_BOMB(element))
11618 CheckDynamite(x, y);
11619 else if (element == EL_AMOEBA_GROWING)
11620 AmoebeWaechst(x, y);
11621 else if (element == EL_AMOEBA_SHRINKING)
11622 AmoebaDisappearing(x, y);
11624 #if !USE_NEW_AMOEBA_CODE
11625 else if (IS_AMOEBALIVE(element))
11626 AmoebeAbleger(x, y);
11629 else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
11631 else if (element == EL_EXIT_CLOSED)
11633 else if (element == EL_EM_EXIT_CLOSED)
11635 else if (element == EL_STEEL_EXIT_CLOSED)
11636 CheckExitSteel(x, y);
11637 else if (element == EL_EM_STEEL_EXIT_CLOSED)
11638 CheckExitSteelEM(x, y);
11639 else if (element == EL_SP_EXIT_CLOSED)
11641 else if (element == EL_EXPANDABLE_WALL_GROWING ||
11642 element == EL_EXPANDABLE_STEELWALL_GROWING)
11643 MauerWaechst(x, y);
11644 else if (element == EL_EXPANDABLE_WALL ||
11645 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
11646 element == EL_EXPANDABLE_WALL_VERTICAL ||
11647 element == EL_EXPANDABLE_WALL_ANY ||
11648 element == EL_BD_EXPANDABLE_WALL)
11649 MauerAbleger(x, y);
11650 else if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
11651 element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
11652 element == EL_EXPANDABLE_STEELWALL_ANY)
11653 MauerAblegerStahl(x, y);
11654 else if (element == EL_FLAMES)
11655 CheckForDragon(x, y);
11656 else if (element == EL_EXPLOSION)
11657 ; /* drawing of correct explosion animation is handled separately */
11658 else if (element == EL_ELEMENT_SNAPPING ||
11659 element == EL_DIAGONAL_SHRINKING ||
11660 element == EL_DIAGONAL_GROWING)
11662 graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
11664 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11666 else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
11667 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11669 if (IS_BELT_ACTIVE(element))
11670 PlayLevelSoundAction(x, y, ACTION_ACTIVE);
11672 if (game.magic_wall_active)
11674 int jx = local_player->jx, jy = local_player->jy;
11676 /* play the element sound at the position nearest to the player */
11677 if ((element == EL_MAGIC_WALL_FULL ||
11678 element == EL_MAGIC_WALL_ACTIVE ||
11679 element == EL_MAGIC_WALL_EMPTYING ||
11680 element == EL_BD_MAGIC_WALL_FULL ||
11681 element == EL_BD_MAGIC_WALL_ACTIVE ||
11682 element == EL_BD_MAGIC_WALL_EMPTYING ||
11683 element == EL_DC_MAGIC_WALL_FULL ||
11684 element == EL_DC_MAGIC_WALL_ACTIVE ||
11685 element == EL_DC_MAGIC_WALL_EMPTYING) &&
11686 ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
11695 printf(" -> %d\n", GfxFrame[DEBUG_GFXFRAME_X][DEBUG_GFXFRAME_Y]);
11698 #if USE_NEW_AMOEBA_CODE
11699 /* new experimental amoeba growth stuff */
11700 if (!(FrameCounter % 8))
11702 static unsigned int random = 1684108901;
11704 for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
11706 x = RND(lev_fieldx);
11707 y = RND(lev_fieldy);
11708 element = Feld[x][y];
11710 if (!IS_PLAYER(x,y) &&
11711 (element == EL_EMPTY ||
11712 CAN_GROW_INTO(element) ||
11713 element == EL_QUICKSAND_EMPTY ||
11714 element == EL_QUICKSAND_FAST_EMPTY ||
11715 element == EL_ACID_SPLASH_LEFT ||
11716 element == EL_ACID_SPLASH_RIGHT))
11718 if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
11719 (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
11720 (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
11721 (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
11722 Feld[x][y] = EL_AMOEBA_DROP;
11725 random = random * 129 + 1;
11730 game.explosions_delayed = FALSE;
11732 SCAN_PLAYFIELD(x, y)
11734 element = Feld[x][y];
11736 if (ExplodeField[x][y])
11737 Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
11738 else if (element == EL_EXPLOSION)
11739 Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
11741 ExplodeField[x][y] = EX_TYPE_NONE;
11744 game.explosions_delayed = TRUE;
11746 if (game.magic_wall_active)
11748 if (!(game.magic_wall_time_left % 4))
11750 int element = Feld[magic_wall_x][magic_wall_y];
11752 if (element == EL_BD_MAGIC_WALL_FULL ||
11753 element == EL_BD_MAGIC_WALL_ACTIVE ||
11754 element == EL_BD_MAGIC_WALL_EMPTYING)
11755 PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
11756 else if (element == EL_DC_MAGIC_WALL_FULL ||
11757 element == EL_DC_MAGIC_WALL_ACTIVE ||
11758 element == EL_DC_MAGIC_WALL_EMPTYING)
11759 PlayLevelSound(magic_wall_x, magic_wall_y, SND_DC_MAGIC_WALL_ACTIVE);
11761 PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
11764 if (game.magic_wall_time_left > 0)
11766 game.magic_wall_time_left--;
11768 if (!game.magic_wall_time_left)
11770 SCAN_PLAYFIELD(x, y)
11772 element = Feld[x][y];
11774 if (element == EL_MAGIC_WALL_ACTIVE ||
11775 element == EL_MAGIC_WALL_FULL)
11777 Feld[x][y] = EL_MAGIC_WALL_DEAD;
11778 TEST_DrawLevelField(x, y);
11780 else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
11781 element == EL_BD_MAGIC_WALL_FULL)
11783 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
11784 TEST_DrawLevelField(x, y);
11786 else if (element == EL_DC_MAGIC_WALL_ACTIVE ||
11787 element == EL_DC_MAGIC_WALL_FULL)
11789 Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
11790 TEST_DrawLevelField(x, y);
11794 game.magic_wall_active = FALSE;
11799 if (game.light_time_left > 0)
11801 game.light_time_left--;
11803 if (game.light_time_left == 0)
11804 RedrawAllLightSwitchesAndInvisibleElements();
11807 if (game.timegate_time_left > 0)
11809 game.timegate_time_left--;
11811 if (game.timegate_time_left == 0)
11812 CloseAllOpenTimegates();
11815 if (game.lenses_time_left > 0)
11817 game.lenses_time_left--;
11819 if (game.lenses_time_left == 0)
11820 RedrawAllInvisibleElementsForLenses();
11823 if (game.magnify_time_left > 0)
11825 game.magnify_time_left--;
11827 if (game.magnify_time_left == 0)
11828 RedrawAllInvisibleElementsForMagnifier();
11831 for (i = 0; i < MAX_PLAYERS; i++)
11833 struct PlayerInfo *player = &stored_player[i];
11835 if (SHIELD_ON(player))
11837 if (player->shield_deadly_time_left)
11838 PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
11839 else if (player->shield_normal_time_left)
11840 PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
11844 #if USE_DELAYED_GFX_REDRAW
11845 SCAN_PLAYFIELD(x, y)
11847 if (GfxRedraw[x][y] != GFX_REDRAW_NONE)
11849 /* !!! PROBLEM: THIS REDRAWS THE PLAYFIELD _AFTER_ THE SCAN, BUT TILES
11850 !!! MAY HAVE CHANGED AFTER BEING DRAWN DURING PLAYFIELD SCAN !!! */
11852 if (GfxRedraw[x][y] & GFX_REDRAW_TILE)
11853 DrawLevelField(x, y);
11855 if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED)
11856 DrawLevelFieldCrumbled(x, y);
11858 if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS)
11859 DrawLevelFieldCrumbledNeighbours(x, y);
11861 if (GfxRedraw[x][y] & GFX_REDRAW_TILE_TWINKLED)
11862 DrawTwinkleOnField(x, y);
11865 GfxRedraw[x][y] = GFX_REDRAW_NONE;
11870 PlayAllPlayersSound();
11872 if (local_player->show_envelope != 0 && local_player->MovPos == 0)
11874 ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
11876 local_player->show_envelope = 0;
11879 /* use random number generator in every frame to make it less predictable */
11880 if (game.engine_version >= VERSION_IDENT(3,1,1,0))
11884 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
11886 int min_x = x, min_y = y, max_x = x, max_y = y;
11889 for (i = 0; i < MAX_PLAYERS; i++)
11891 int jx = stored_player[i].jx, jy = stored_player[i].jy;
11893 if (!stored_player[i].active || &stored_player[i] == player)
11896 min_x = MIN(min_x, jx);
11897 min_y = MIN(min_y, jy);
11898 max_x = MAX(max_x, jx);
11899 max_y = MAX(max_y, jy);
11902 return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
11905 static boolean AllPlayersInVisibleScreen()
11909 for (i = 0; i < MAX_PLAYERS; i++)
11911 int jx = stored_player[i].jx, jy = stored_player[i].jy;
11913 if (!stored_player[i].active)
11916 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
11923 void ScrollLevel(int dx, int dy)
11925 int scroll_offset = 2 * TILEX_VAR;
11928 BlitBitmap(drawto_field, drawto_field,
11929 FX + TILEX_VAR * (dx == -1) - scroll_offset,
11930 FY + TILEY_VAR * (dy == -1) - scroll_offset,
11931 SXSIZE - TILEX_VAR * (dx != 0) + 2 * scroll_offset,
11932 SYSIZE - TILEY_VAR * (dy != 0) + 2 * scroll_offset,
11933 FX + TILEX_VAR * (dx == 1) - scroll_offset,
11934 FY + TILEY_VAR * (dy == 1) - scroll_offset);
11938 x = (dx == 1 ? BX1 : BX2);
11939 for (y = BY1; y <= BY2; y++)
11940 DrawScreenField(x, y);
11945 y = (dy == 1 ? BY1 : BY2);
11946 for (x = BX1; x <= BX2; x++)
11947 DrawScreenField(x, y);
11950 redraw_mask |= REDRAW_FIELD;
11953 static boolean canFallDown(struct PlayerInfo *player)
11955 int jx = player->jx, jy = player->jy;
11957 return (IN_LEV_FIELD(jx, jy + 1) &&
11958 (IS_FREE(jx, jy + 1) ||
11959 (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
11960 IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
11961 !IS_WALKABLE_INSIDE(Feld[jx][jy]));
11964 static boolean canPassField(int x, int y, int move_dir)
11966 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
11967 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
11968 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
11969 int nextx = x + dx;
11970 int nexty = y + dy;
11971 int element = Feld[x][y];
11973 return (IS_PASSABLE_FROM(element, opposite_dir) &&
11974 !CAN_MOVE(element) &&
11975 IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
11976 IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
11977 (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
11980 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
11982 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
11983 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
11984 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
11988 return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
11989 IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
11990 (IS_DIGGABLE(Feld[newx][newy]) ||
11991 IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
11992 canPassField(newx, newy, move_dir)));
11995 static void CheckGravityMovement(struct PlayerInfo *player)
11997 if (player->gravity && !player->programmed_action)
11999 int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
12000 int move_dir_vertical = player->effective_action & MV_VERTICAL;
12001 boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
12002 int jx = player->jx, jy = player->jy;
12003 boolean player_is_moving_to_valid_field =
12004 (!player_is_snapping &&
12005 (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
12006 canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
12007 boolean player_can_fall_down = canFallDown(player);
12009 if (player_can_fall_down &&
12010 !player_is_moving_to_valid_field)
12011 player->programmed_action = MV_DOWN;
12015 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
12017 return CheckGravityMovement(player);
12019 if (player->gravity && !player->programmed_action)
12021 int jx = player->jx, jy = player->jy;
12022 boolean field_under_player_is_free =
12023 (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
12024 boolean player_is_standing_on_valid_field =
12025 (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
12026 (IS_WALKABLE(Feld[jx][jy]) &&
12027 !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
12029 if (field_under_player_is_free && !player_is_standing_on_valid_field)
12030 player->programmed_action = MV_DOWN;
12035 MovePlayerOneStep()
12036 -----------------------------------------------------------------------------
12037 dx, dy: direction (non-diagonal) to try to move the player to
12038 real_dx, real_dy: direction as read from input device (can be diagonal)
12041 boolean MovePlayerOneStep(struct PlayerInfo *player,
12042 int dx, int dy, int real_dx, int real_dy)
12044 int jx = player->jx, jy = player->jy;
12045 int new_jx = jx + dx, new_jy = jy + dy;
12047 boolean player_can_move = !player->cannot_move;
12049 if (!player->active || (!dx && !dy))
12050 return MP_NO_ACTION;
12052 player->MovDir = (dx < 0 ? MV_LEFT :
12053 dx > 0 ? MV_RIGHT :
12055 dy > 0 ? MV_DOWN : MV_NONE);
12057 if (!IN_LEV_FIELD(new_jx, new_jy))
12058 return MP_NO_ACTION;
12060 if (!player_can_move)
12062 if (player->MovPos == 0)
12064 player->is_moving = FALSE;
12065 player->is_digging = FALSE;
12066 player->is_collecting = FALSE;
12067 player->is_snapping = FALSE;
12068 player->is_pushing = FALSE;
12072 if (!options.network && game.centered_player_nr == -1 &&
12073 !AllPlayersInSight(player, new_jx, new_jy))
12074 return MP_NO_ACTION;
12076 can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
12077 if (can_move != MP_MOVING)
12080 /* check if DigField() has caused relocation of the player */
12081 if (player->jx != jx || player->jy != jy)
12082 return MP_NO_ACTION; /* <-- !!! CHECK THIS [-> MP_ACTION ?] !!! */
12084 StorePlayer[jx][jy] = 0;
12085 player->last_jx = jx;
12086 player->last_jy = jy;
12087 player->jx = new_jx;
12088 player->jy = new_jy;
12089 StorePlayer[new_jx][new_jy] = player->element_nr;
12091 if (player->move_delay_value_next != -1)
12093 player->move_delay_value = player->move_delay_value_next;
12094 player->move_delay_value_next = -1;
12098 (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
12100 player->step_counter++;
12102 PlayerVisit[jx][jy] = FrameCounter;
12104 player->is_moving = TRUE;
12107 /* should better be called in MovePlayer(), but this breaks some tapes */
12108 ScrollPlayer(player, SCROLL_INIT);
12114 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
12116 int jx = player->jx, jy = player->jy;
12117 int old_jx = jx, old_jy = jy;
12118 int moved = MP_NO_ACTION;
12120 if (!player->active)
12125 if (player->MovPos == 0)
12127 player->is_moving = FALSE;
12128 player->is_digging = FALSE;
12129 player->is_collecting = FALSE;
12130 player->is_snapping = FALSE;
12131 player->is_pushing = FALSE;
12137 if (player->move_delay > 0)
12140 player->move_delay = -1; /* set to "uninitialized" value */
12142 /* store if player is automatically moved to next field */
12143 player->is_auto_moving = (player->programmed_action != MV_NONE);
12145 /* remove the last programmed player action */
12146 player->programmed_action = 0;
12148 if (player->MovPos)
12150 /* should only happen if pre-1.2 tape recordings are played */
12151 /* this is only for backward compatibility */
12153 int original_move_delay_value = player->move_delay_value;
12156 printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%d]\n",
12160 /* scroll remaining steps with finest movement resolution */
12161 player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
12163 while (player->MovPos)
12165 ScrollPlayer(player, SCROLL_GO_ON);
12166 ScrollScreen(NULL, SCROLL_GO_ON);
12168 AdvanceFrameAndPlayerCounters(player->index_nr);
12174 player->move_delay_value = original_move_delay_value;
12177 player->is_active = FALSE;
12179 if (player->last_move_dir & MV_HORIZONTAL)
12181 if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
12182 moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
12186 if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
12187 moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
12190 if (!moved && !player->is_active)
12192 player->is_moving = FALSE;
12193 player->is_digging = FALSE;
12194 player->is_collecting = FALSE;
12195 player->is_snapping = FALSE;
12196 player->is_pushing = FALSE;
12202 if (moved & MP_MOVING && !ScreenMovPos &&
12203 (player->index_nr == game.centered_player_nr ||
12204 game.centered_player_nr == -1))
12206 int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
12207 int offset = game.scroll_delay_value;
12209 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12211 /* actual player has left the screen -- scroll in that direction */
12212 if (jx != old_jx) /* player has moved horizontally */
12213 scroll_x += (jx - old_jx);
12214 else /* player has moved vertically */
12215 scroll_y += (jy - old_jy);
12219 if (jx != old_jx) /* player has moved horizontally */
12221 if ((player->MovDir == MV_LEFT && scroll_x > jx - MIDPOSX + offset) ||
12222 (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
12223 scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
12225 /* don't scroll over playfield boundaries */
12226 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
12227 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
12229 /* don't scroll more than one field at a time */
12230 scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
12232 /* don't scroll against the player's moving direction */
12233 if ((player->MovDir == MV_LEFT && scroll_x > old_scroll_x) ||
12234 (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
12235 scroll_x = old_scroll_x;
12237 else /* player has moved vertically */
12239 if ((player->MovDir == MV_UP && scroll_y > jy - MIDPOSY + offset) ||
12240 (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
12241 scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
12243 /* don't scroll over playfield boundaries */
12244 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
12245 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
12247 /* don't scroll more than one field at a time */
12248 scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
12250 /* don't scroll against the player's moving direction */
12251 if ((player->MovDir == MV_UP && scroll_y > old_scroll_y) ||
12252 (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
12253 scroll_y = old_scroll_y;
12257 if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
12259 if (!options.network && game.centered_player_nr == -1 &&
12260 !AllPlayersInVisibleScreen())
12262 scroll_x = old_scroll_x;
12263 scroll_y = old_scroll_y;
12267 ScrollScreen(player, SCROLL_INIT);
12268 ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
12273 player->StepFrame = 0;
12275 if (moved & MP_MOVING)
12277 if (old_jx != jx && old_jy == jy)
12278 player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
12279 else if (old_jx == jx && old_jy != jy)
12280 player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
12282 TEST_DrawLevelField(jx, jy); /* for "crumbled sand" */
12284 player->last_move_dir = player->MovDir;
12285 player->is_moving = TRUE;
12286 player->is_snapping = FALSE;
12287 player->is_switching = FALSE;
12288 player->is_dropping = FALSE;
12289 player->is_dropping_pressed = FALSE;
12290 player->drop_pressed_delay = 0;
12293 /* should better be called here than above, but this breaks some tapes */
12294 ScrollPlayer(player, SCROLL_INIT);
12299 CheckGravityMovementWhenNotMoving(player);
12301 player->is_moving = FALSE;
12303 /* at this point, the player is allowed to move, but cannot move right now
12304 (e.g. because of something blocking the way) -- ensure that the player
12305 is also allowed to move in the next frame (in old versions before 3.1.1,
12306 the player was forced to wait again for eight frames before next try) */
12308 if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12309 player->move_delay = 0; /* allow direct movement in the next frame */
12312 if (player->move_delay == -1) /* not yet initialized by DigField() */
12313 player->move_delay = player->move_delay_value;
12315 if (game.engine_version < VERSION_IDENT(3,0,7,0))
12317 TestIfPlayerTouchesBadThing(jx, jy);
12318 TestIfPlayerTouchesCustomElement(jx, jy);
12321 if (!player->active)
12322 RemovePlayer(player);
12327 void ScrollPlayer(struct PlayerInfo *player, int mode)
12329 int jx = player->jx, jy = player->jy;
12330 int last_jx = player->last_jx, last_jy = player->last_jy;
12331 int move_stepsize = TILEX / player->move_delay_value;
12333 if (!player->active)
12336 if (player->MovPos == 0 && mode == SCROLL_GO_ON) /* player not moving */
12339 if (mode == SCROLL_INIT)
12341 player->actual_frame_counter = FrameCounter;
12342 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
12344 if ((player->block_last_field || player->block_delay_adjustment > 0) &&
12345 Feld[last_jx][last_jy] == EL_EMPTY)
12347 int last_field_block_delay = 0; /* start with no blocking at all */
12348 int block_delay_adjustment = player->block_delay_adjustment;
12350 /* if player blocks last field, add delay for exactly one move */
12351 if (player->block_last_field)
12353 last_field_block_delay += player->move_delay_value;
12355 /* when blocking enabled, prevent moving up despite gravity */
12356 if (player->gravity && player->MovDir == MV_UP)
12357 block_delay_adjustment = -1;
12360 /* add block delay adjustment (also possible when not blocking) */
12361 last_field_block_delay += block_delay_adjustment;
12363 Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
12364 MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
12367 if (player->MovPos != 0) /* player has not yet reached destination */
12370 else if (!FrameReached(&player->actual_frame_counter, 1))
12373 if (player->MovPos != 0)
12375 player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
12376 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
12378 /* before DrawPlayer() to draw correct player graphic for this case */
12379 if (player->MovPos == 0)
12380 CheckGravityMovement(player);
12383 if (player->MovPos == 0) /* player reached destination field */
12385 if (player->move_delay_reset_counter > 0)
12387 player->move_delay_reset_counter--;
12389 if (player->move_delay_reset_counter == 0)
12391 /* continue with normal speed after quickly moving through gate */
12392 HALVE_PLAYER_SPEED(player);
12394 /* be able to make the next move without delay */
12395 player->move_delay = 0;
12399 player->last_jx = jx;
12400 player->last_jy = jy;
12402 if (Feld[jx][jy] == EL_EXIT_OPEN ||
12403 Feld[jx][jy] == EL_EM_EXIT_OPEN ||
12404 Feld[jx][jy] == EL_EM_EXIT_OPENING ||
12405 Feld[jx][jy] == EL_STEEL_EXIT_OPEN ||
12406 Feld[jx][jy] == EL_EM_STEEL_EXIT_OPEN ||
12407 Feld[jx][jy] == EL_EM_STEEL_EXIT_OPENING ||
12408 Feld[jx][jy] == EL_SP_EXIT_OPEN ||
12409 Feld[jx][jy] == EL_SP_EXIT_OPENING) /* <-- special case */
12411 DrawPlayer(player); /* needed here only to cleanup last field */
12412 RemovePlayer(player);
12414 if (local_player->friends_still_needed == 0 ||
12415 IS_SP_ELEMENT(Feld[jx][jy]))
12416 PlayerWins(player);
12419 /* this breaks one level: "machine", level 000 */
12421 int move_direction = player->MovDir;
12422 int enter_side = MV_DIR_OPPOSITE(move_direction);
12423 int leave_side = move_direction;
12424 int old_jx = last_jx;
12425 int old_jy = last_jy;
12426 int old_element = Feld[old_jx][old_jy];
12427 int new_element = Feld[jx][jy];
12429 if (IS_CUSTOM_ELEMENT(old_element))
12430 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
12432 player->index_bit, leave_side);
12434 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
12435 CE_PLAYER_LEAVES_X,
12436 player->index_bit, leave_side);
12438 if (IS_CUSTOM_ELEMENT(new_element))
12439 CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
12440 player->index_bit, enter_side);
12442 CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
12443 CE_PLAYER_ENTERS_X,
12444 player->index_bit, enter_side);
12446 CheckTriggeredElementChangeBySide(jx, jy, player->initial_element,
12447 CE_MOVE_OF_X, move_direction);
12450 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
12452 TestIfPlayerTouchesBadThing(jx, jy);
12453 TestIfPlayerTouchesCustomElement(jx, jy);
12455 /* needed because pushed element has not yet reached its destination,
12456 so it would trigger a change event at its previous field location */
12457 if (!player->is_pushing)
12458 TestIfElementTouchesCustomElement(jx, jy); /* for empty space */
12460 if (!player->active)
12461 RemovePlayer(player);
12464 if (!local_player->LevelSolved && level.use_step_counter)
12474 if (TimeLeft <= 10 && setup.time_limit)
12475 PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
12477 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
12479 DisplayGameControlValues();
12481 if (!TimeLeft && setup.time_limit)
12482 for (i = 0; i < MAX_PLAYERS; i++)
12483 KillPlayer(&stored_player[i]);
12485 else if (game.no_time_limit && !AllPlayersGone) /* level w/o time limit */
12487 game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
12489 DisplayGameControlValues();
12493 if (tape.single_step && tape.recording && !tape.pausing &&
12494 !player->programmed_action)
12495 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
12497 if (!player->programmed_action)
12498 CheckSaveEngineSnapshot(player);
12502 void ScrollScreen(struct PlayerInfo *player, int mode)
12504 static unsigned int screen_frame_counter = 0;
12506 if (mode == SCROLL_INIT)
12508 /* set scrolling step size according to actual player's moving speed */
12509 ScrollStepSize = TILEX / player->move_delay_value;
12511 screen_frame_counter = FrameCounter;
12512 ScreenMovDir = player->MovDir;
12513 ScreenMovPos = player->MovPos;
12514 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
12517 else if (!FrameReached(&screen_frame_counter, 1))
12522 ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
12523 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
12524 redraw_mask |= REDRAW_FIELD;
12527 ScreenMovDir = MV_NONE;
12530 void TestIfPlayerTouchesCustomElement(int x, int y)
12532 static int xy[4][2] =
12539 static int trigger_sides[4][2] =
12541 /* center side border side */
12542 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
12543 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
12544 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
12545 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
12547 static int touch_dir[4] =
12549 MV_LEFT | MV_RIGHT,
12554 int center_element = Feld[x][y]; /* should always be non-moving! */
12557 for (i = 0; i < NUM_DIRECTIONS; i++)
12559 int xx = x + xy[i][0];
12560 int yy = y + xy[i][1];
12561 int center_side = trigger_sides[i][0];
12562 int border_side = trigger_sides[i][1];
12563 int border_element;
12565 if (!IN_LEV_FIELD(xx, yy))
12568 if (IS_PLAYER(x, y)) /* player found at center element */
12570 struct PlayerInfo *player = PLAYERINFO(x, y);
12572 if (game.engine_version < VERSION_IDENT(3,0,7,0))
12573 border_element = Feld[xx][yy]; /* may be moving! */
12574 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
12575 border_element = Feld[xx][yy];
12576 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
12577 border_element = MovingOrBlocked2Element(xx, yy);
12579 continue; /* center and border element do not touch */
12581 CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
12582 player->index_bit, border_side);
12583 CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
12584 CE_PLAYER_TOUCHES_X,
12585 player->index_bit, border_side);
12588 /* use player element that is initially defined in the level playfield,
12589 not the player element that corresponds to the runtime player number
12590 (example: a level that contains EL_PLAYER_3 as the only player would
12591 incorrectly give EL_PLAYER_1 for "player->element_nr") */
12592 int player_element = PLAYERINFO(x, y)->initial_element;
12594 CheckElementChangeBySide(xx, yy, border_element, player_element,
12595 CE_TOUCHING_X, border_side);
12598 else if (IS_PLAYER(xx, yy)) /* player found at border element */
12600 struct PlayerInfo *player = PLAYERINFO(xx, yy);
12602 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
12604 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
12605 continue; /* center and border element do not touch */
12608 CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
12609 player->index_bit, center_side);
12610 CheckTriggeredElementChangeByPlayer(x, y, center_element,
12611 CE_PLAYER_TOUCHES_X,
12612 player->index_bit, center_side);
12615 /* use player element that is initially defined in the level playfield,
12616 not the player element that corresponds to the runtime player number
12617 (example: a level that contains EL_PLAYER_3 as the only player would
12618 incorrectly give EL_PLAYER_1 for "player->element_nr") */
12619 int player_element = PLAYERINFO(xx, yy)->initial_element;
12621 CheckElementChangeBySide(x, y, center_element, player_element,
12622 CE_TOUCHING_X, center_side);
12630 void TestIfElementTouchesCustomElement(int x, int y)
12632 static int xy[4][2] =
12639 static int trigger_sides[4][2] =
12641 /* center side border side */
12642 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
12643 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
12644 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
12645 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
12647 static int touch_dir[4] =
12649 MV_LEFT | MV_RIGHT,
12654 boolean change_center_element = FALSE;
12655 int center_element = Feld[x][y]; /* should always be non-moving! */
12656 int border_element_old[NUM_DIRECTIONS];
12659 for (i = 0; i < NUM_DIRECTIONS; i++)
12661 int xx = x + xy[i][0];
12662 int yy = y + xy[i][1];
12663 int border_element;
12665 border_element_old[i] = -1;
12667 if (!IN_LEV_FIELD(xx, yy))
12670 if (game.engine_version < VERSION_IDENT(3,0,7,0))
12671 border_element = Feld[xx][yy]; /* may be moving! */
12672 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
12673 border_element = Feld[xx][yy];
12674 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
12675 border_element = MovingOrBlocked2Element(xx, yy);
12677 continue; /* center and border element do not touch */
12679 border_element_old[i] = border_element;
12682 for (i = 0; i < NUM_DIRECTIONS; i++)
12684 int xx = x + xy[i][0];
12685 int yy = y + xy[i][1];
12686 int center_side = trigger_sides[i][0];
12687 int border_element = border_element_old[i];
12689 if (border_element == -1)
12692 /* check for change of border element */
12693 CheckElementChangeBySide(xx, yy, border_element, center_element,
12694 CE_TOUCHING_X, center_side);
12696 /* (center element cannot be player, so we dont have to check this here) */
12699 for (i = 0; i < NUM_DIRECTIONS; i++)
12701 int xx = x + xy[i][0];
12702 int yy = y + xy[i][1];
12703 int border_side = trigger_sides[i][1];
12704 int border_element = border_element_old[i];
12706 if (border_element == -1)
12709 /* check for change of center element (but change it only once) */
12710 if (!change_center_element)
12711 change_center_element =
12712 CheckElementChangeBySide(x, y, center_element, border_element,
12713 CE_TOUCHING_X, border_side);
12715 if (IS_PLAYER(xx, yy))
12717 /* use player element that is initially defined in the level playfield,
12718 not the player element that corresponds to the runtime player number
12719 (example: a level that contains EL_PLAYER_3 as the only player would
12720 incorrectly give EL_PLAYER_1 for "player->element_nr") */
12721 int player_element = PLAYERINFO(xx, yy)->initial_element;
12723 CheckElementChangeBySide(x, y, center_element, player_element,
12724 CE_TOUCHING_X, border_side);
12729 void TestIfElementHitsCustomElement(int x, int y, int direction)
12731 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
12732 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
12733 int hitx = x + dx, hity = y + dy;
12734 int hitting_element = Feld[x][y];
12735 int touched_element;
12737 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
12740 touched_element = (IN_LEV_FIELD(hitx, hity) ?
12741 MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
12743 if (IN_LEV_FIELD(hitx, hity))
12745 int opposite_direction = MV_DIR_OPPOSITE(direction);
12746 int hitting_side = direction;
12747 int touched_side = opposite_direction;
12748 boolean object_hit = (!IS_MOVING(hitx, hity) ||
12749 MovDir[hitx][hity] != direction ||
12750 ABS(MovPos[hitx][hity]) <= TILEY / 2);
12756 CheckElementChangeBySide(x, y, hitting_element, touched_element,
12757 CE_HITTING_X, touched_side);
12759 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
12760 CE_HIT_BY_X, hitting_side);
12762 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
12763 CE_HIT_BY_SOMETHING, opposite_direction);
12765 if (IS_PLAYER(hitx, hity))
12767 /* use player element that is initially defined in the level playfield,
12768 not the player element that corresponds to the runtime player number
12769 (example: a level that contains EL_PLAYER_3 as the only player would
12770 incorrectly give EL_PLAYER_1 for "player->element_nr") */
12771 int player_element = PLAYERINFO(hitx, hity)->initial_element;
12773 CheckElementChangeBySide(x, y, hitting_element, player_element,
12774 CE_HITTING_X, touched_side);
12779 /* "hitting something" is also true when hitting the playfield border */
12780 CheckElementChangeBySide(x, y, hitting_element, touched_element,
12781 CE_HITTING_SOMETHING, direction);
12784 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
12786 int i, kill_x = -1, kill_y = -1;
12788 int bad_element = -1;
12789 static int test_xy[4][2] =
12796 static int test_dir[4] =
12804 for (i = 0; i < NUM_DIRECTIONS; i++)
12806 int test_x, test_y, test_move_dir, test_element;
12808 test_x = good_x + test_xy[i][0];
12809 test_y = good_y + test_xy[i][1];
12811 if (!IN_LEV_FIELD(test_x, test_y))
12815 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
12817 test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
12819 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
12820 2nd case: DONT_TOUCH style bad thing does not move away from good thing
12822 if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
12823 (DONT_TOUCH(test_element) && test_move_dir != test_dir[i]))
12827 bad_element = test_element;
12833 if (kill_x != -1 || kill_y != -1)
12835 if (IS_PLAYER(good_x, good_y))
12837 struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
12839 if (player->shield_deadly_time_left > 0 &&
12840 !IS_INDESTRUCTIBLE(bad_element))
12841 Bang(kill_x, kill_y);
12842 else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
12843 KillPlayer(player);
12846 Bang(good_x, good_y);
12850 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
12852 int i, kill_x = -1, kill_y = -1;
12853 int bad_element = Feld[bad_x][bad_y];
12854 static int test_xy[4][2] =
12861 static int touch_dir[4] =
12863 MV_LEFT | MV_RIGHT,
12868 static int test_dir[4] =
12876 if (bad_element == EL_EXPLOSION) /* skip just exploding bad things */
12879 for (i = 0; i < NUM_DIRECTIONS; i++)
12881 int test_x, test_y, test_move_dir, test_element;
12883 test_x = bad_x + test_xy[i][0];
12884 test_y = bad_y + test_xy[i][1];
12886 if (!IN_LEV_FIELD(test_x, test_y))
12890 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
12892 test_element = Feld[test_x][test_y];
12894 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
12895 2nd case: DONT_TOUCH style bad thing does not move away from good thing
12897 if ((DONT_RUN_INTO(bad_element) && bad_move_dir == test_dir[i]) ||
12898 (DONT_TOUCH(bad_element) && test_move_dir != test_dir[i]))
12900 /* good thing is player or penguin that does not move away */
12901 if (IS_PLAYER(test_x, test_y))
12903 struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
12905 if (bad_element == EL_ROBOT && player->is_moving)
12906 continue; /* robot does not kill player if he is moving */
12908 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
12910 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
12911 continue; /* center and border element do not touch */
12919 else if (test_element == EL_PENGUIN)
12929 if (kill_x != -1 || kill_y != -1)
12931 if (IS_PLAYER(kill_x, kill_y))
12933 struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
12935 if (player->shield_deadly_time_left > 0 &&
12936 !IS_INDESTRUCTIBLE(bad_element))
12937 Bang(bad_x, bad_y);
12938 else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
12939 KillPlayer(player);
12942 Bang(kill_x, kill_y);
12946 void TestIfGoodThingGetsHitByBadThing(int bad_x, int bad_y, int bad_move_dir)
12948 int bad_element = Feld[bad_x][bad_y];
12949 int dx = (bad_move_dir == MV_LEFT ? -1 : bad_move_dir == MV_RIGHT ? +1 : 0);
12950 int dy = (bad_move_dir == MV_UP ? -1 : bad_move_dir == MV_DOWN ? +1 : 0);
12951 int test_x = bad_x + dx, test_y = bad_y + dy;
12952 int test_move_dir, test_element;
12953 int kill_x = -1, kill_y = -1;
12955 if (!IN_LEV_FIELD(test_x, test_y))
12959 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
12961 test_element = Feld[test_x][test_y];
12963 if (test_move_dir != bad_move_dir)
12965 /* good thing can be player or penguin that does not move away */
12966 if (IS_PLAYER(test_x, test_y))
12968 struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
12970 /* (note: in comparison to DONT_RUN_TO and DONT_TOUCH, also handle the
12971 player as being hit when he is moving towards the bad thing, because
12972 the "get hit by" condition would be lost after the player stops) */
12973 if (player->MovPos != 0 && player->MovDir == bad_move_dir)
12974 return; /* player moves away from bad thing */
12979 else if (test_element == EL_PENGUIN)
12986 if (kill_x != -1 || kill_y != -1)
12988 if (IS_PLAYER(kill_x, kill_y))
12990 struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
12992 if (player->shield_deadly_time_left > 0 &&
12993 !IS_INDESTRUCTIBLE(bad_element))
12994 Bang(bad_x, bad_y);
12995 else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
12996 KillPlayer(player);
12999 Bang(kill_x, kill_y);
13003 void TestIfPlayerTouchesBadThing(int x, int y)
13005 TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13008 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
13010 TestIfGoodThingHitsBadThing(x, y, move_dir);
13013 void TestIfBadThingTouchesPlayer(int x, int y)
13015 TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13018 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
13020 TestIfBadThingHitsGoodThing(x, y, move_dir);
13023 void TestIfFriendTouchesBadThing(int x, int y)
13025 TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13028 void TestIfBadThingTouchesFriend(int x, int y)
13030 TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13033 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
13035 int i, kill_x = bad_x, kill_y = bad_y;
13036 static int xy[4][2] =
13044 for (i = 0; i < NUM_DIRECTIONS; i++)
13048 x = bad_x + xy[i][0];
13049 y = bad_y + xy[i][1];
13050 if (!IN_LEV_FIELD(x, y))
13053 element = Feld[x][y];
13054 if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
13055 element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
13063 if (kill_x != bad_x || kill_y != bad_y)
13064 Bang(bad_x, bad_y);
13067 void KillPlayer(struct PlayerInfo *player)
13069 int jx = player->jx, jy = player->jy;
13071 if (!player->active)
13075 printf("::: 0: killed == %d, active == %d, reanimated == %d\n",
13076 player->killed, player->active, player->reanimated);
13079 /* the following code was introduced to prevent an infinite loop when calling
13081 -> CheckTriggeredElementChangeExt()
13082 -> ExecuteCustomElementAction()
13084 -> (infinitely repeating the above sequence of function calls)
13085 which occurs when killing the player while having a CE with the setting
13086 "kill player X when explosion of <player X>"; the solution using a new
13087 field "player->killed" was chosen for backwards compatibility, although
13088 clever use of the fields "player->active" etc. would probably also work */
13090 if (player->killed)
13094 player->killed = TRUE;
13096 /* remove accessible field at the player's position */
13097 Feld[jx][jy] = EL_EMPTY;
13099 /* deactivate shield (else Bang()/Explode() would not work right) */
13100 player->shield_normal_time_left = 0;
13101 player->shield_deadly_time_left = 0;
13104 printf("::: 1: killed == %d, active == %d, reanimated == %d\n",
13105 player->killed, player->active, player->reanimated);
13111 printf("::: 2: killed == %d, active == %d, reanimated == %d\n",
13112 player->killed, player->active, player->reanimated);
13115 if (player->reanimated) /* killed player may have been reanimated */
13116 player->killed = player->reanimated = FALSE;
13118 BuryPlayer(player);
13121 static void KillPlayerUnlessEnemyProtected(int x, int y)
13123 if (!PLAYER_ENEMY_PROTECTED(x, y))
13124 KillPlayer(PLAYERINFO(x, y));
13127 static void KillPlayerUnlessExplosionProtected(int x, int y)
13129 if (!PLAYER_EXPLOSION_PROTECTED(x, y))
13130 KillPlayer(PLAYERINFO(x, y));
13133 void BuryPlayer(struct PlayerInfo *player)
13135 int jx = player->jx, jy = player->jy;
13137 if (!player->active)
13140 PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
13141 PlayLevelSound(jx, jy, SND_GAME_LOSING);
13143 player->GameOver = TRUE;
13144 RemovePlayer(player);
13147 void RemovePlayer(struct PlayerInfo *player)
13149 int jx = player->jx, jy = player->jy;
13150 int i, found = FALSE;
13152 player->present = FALSE;
13153 player->active = FALSE;
13155 if (!ExplodeField[jx][jy])
13156 StorePlayer[jx][jy] = 0;
13158 if (player->is_moving)
13159 TEST_DrawLevelField(player->last_jx, player->last_jy);
13161 for (i = 0; i < MAX_PLAYERS; i++)
13162 if (stored_player[i].active)
13166 AllPlayersGone = TRUE;
13172 static void setFieldForSnapping(int x, int y, int element, int direction)
13174 struct ElementInfo *ei = &element_info[element];
13175 int direction_bit = MV_DIR_TO_BIT(direction);
13176 int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
13177 int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
13178 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
13180 Feld[x][y] = EL_ELEMENT_SNAPPING;
13181 MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
13183 ResetGfxAnimation(x, y);
13185 GfxElement[x][y] = element;
13186 GfxAction[x][y] = action;
13187 GfxDir[x][y] = direction;
13188 GfxFrame[x][y] = -1;
13192 =============================================================================
13193 checkDiagonalPushing()
13194 -----------------------------------------------------------------------------
13195 check if diagonal input device direction results in pushing of object
13196 (by checking if the alternative direction is walkable, diggable, ...)
13197 =============================================================================
13200 static boolean checkDiagonalPushing(struct PlayerInfo *player,
13201 int x, int y, int real_dx, int real_dy)
13203 int jx, jy, dx, dy, xx, yy;
13205 if (real_dx == 0 || real_dy == 0) /* no diagonal direction => push */
13208 /* diagonal direction: check alternative direction */
13213 xx = jx + (dx == 0 ? real_dx : 0);
13214 yy = jy + (dy == 0 ? real_dy : 0);
13216 return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
13220 =============================================================================
13222 -----------------------------------------------------------------------------
13223 x, y: field next to player (non-diagonal) to try to dig to
13224 real_dx, real_dy: direction as read from input device (can be diagonal)
13225 =============================================================================
13228 static int DigField(struct PlayerInfo *player,
13229 int oldx, int oldy, int x, int y,
13230 int real_dx, int real_dy, int mode)
13232 boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
13233 boolean player_was_pushing = player->is_pushing;
13234 boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
13235 boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
13236 int jx = oldx, jy = oldy;
13237 int dx = x - jx, dy = y - jy;
13238 int nextx = x + dx, nexty = y + dy;
13239 int move_direction = (dx == -1 ? MV_LEFT :
13240 dx == +1 ? MV_RIGHT :
13242 dy == +1 ? MV_DOWN : MV_NONE);
13243 int opposite_direction = MV_DIR_OPPOSITE(move_direction);
13244 int dig_side = MV_DIR_OPPOSITE(move_direction);
13245 int old_element = Feld[jx][jy];
13246 int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
13249 if (is_player) /* function can also be called by EL_PENGUIN */
13251 if (player->MovPos == 0)
13253 player->is_digging = FALSE;
13254 player->is_collecting = FALSE;
13257 if (player->MovPos == 0) /* last pushing move finished */
13258 player->is_pushing = FALSE;
13260 if (mode == DF_NO_PUSH) /* player just stopped pushing */
13262 player->is_switching = FALSE;
13263 player->push_delay = -1;
13265 return MP_NO_ACTION;
13269 if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
13270 old_element = Back[jx][jy];
13272 /* in case of element dropped at player position, check background */
13273 else if (Back[jx][jy] != EL_EMPTY &&
13274 game.engine_version >= VERSION_IDENT(2,2,0,0))
13275 old_element = Back[jx][jy];
13277 if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
13278 return MP_NO_ACTION; /* field has no opening in this direction */
13280 if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
13281 return MP_NO_ACTION; /* field has no opening in this direction */
13283 if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
13287 Feld[jx][jy] = player->artwork_element;
13288 InitMovingField(jx, jy, MV_DOWN);
13289 Store[jx][jy] = EL_ACID;
13290 ContinueMoving(jx, jy);
13291 BuryPlayer(player);
13293 return MP_DONT_RUN_INTO;
13296 if (player_can_move && DONT_RUN_INTO(element))
13298 TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
13300 return MP_DONT_RUN_INTO;
13303 if (IS_MOVING(x, y) || IS_PLAYER(x, y))
13304 return MP_NO_ACTION;
13306 collect_count = element_info[element].collect_count_initial;
13308 if (!is_player && !IS_COLLECTIBLE(element)) /* penguin cannot collect it */
13309 return MP_NO_ACTION;
13311 if (game.engine_version < VERSION_IDENT(2,2,0,0))
13312 player_can_move = player_can_move_or_snap;
13314 if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
13315 game.engine_version >= VERSION_IDENT(2,2,0,0))
13317 CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
13318 player->index_bit, dig_side);
13319 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13320 player->index_bit, dig_side);
13322 if (element == EL_DC_LANDMINE)
13325 if (Feld[x][y] != element) /* field changed by snapping */
13328 return MP_NO_ACTION;
13331 if (player->gravity && is_player && !player->is_auto_moving &&
13332 canFallDown(player) && move_direction != MV_DOWN &&
13333 !canMoveToValidFieldWithGravity(jx, jy, move_direction))
13334 return MP_NO_ACTION; /* player cannot walk here due to gravity */
13336 if (player_can_move &&
13337 IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
13339 int sound_element = SND_ELEMENT(element);
13340 int sound_action = ACTION_WALKING;
13342 if (IS_RND_GATE(element))
13344 if (!player->key[RND_GATE_NR(element)])
13345 return MP_NO_ACTION;
13347 else if (IS_RND_GATE_GRAY(element))
13349 if (!player->key[RND_GATE_GRAY_NR(element)])
13350 return MP_NO_ACTION;
13352 else if (IS_RND_GATE_GRAY_ACTIVE(element))
13354 if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
13355 return MP_NO_ACTION;
13357 else if (element == EL_EXIT_OPEN ||
13358 element == EL_EM_EXIT_OPEN ||
13359 element == EL_EM_EXIT_OPENING ||
13360 element == EL_STEEL_EXIT_OPEN ||
13361 element == EL_EM_STEEL_EXIT_OPEN ||
13362 element == EL_EM_STEEL_EXIT_OPENING ||
13363 element == EL_SP_EXIT_OPEN ||
13364 element == EL_SP_EXIT_OPENING)
13366 sound_action = ACTION_PASSING; /* player is passing exit */
13368 else if (element == EL_EMPTY)
13370 sound_action = ACTION_MOVING; /* nothing to walk on */
13373 /* play sound from background or player, whatever is available */
13374 if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
13375 PlayLevelSoundElementAction(x, y, sound_element, sound_action);
13377 PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
13379 else if (player_can_move &&
13380 IS_PASSABLE(element) && canPassField(x, y, move_direction))
13382 if (!ACCESS_FROM(element, opposite_direction))
13383 return MP_NO_ACTION; /* field not accessible from this direction */
13385 if (CAN_MOVE(element)) /* only fixed elements can be passed! */
13386 return MP_NO_ACTION;
13388 if (IS_EM_GATE(element))
13390 if (!player->key[EM_GATE_NR(element)])
13391 return MP_NO_ACTION;
13393 else if (IS_EM_GATE_GRAY(element))
13395 if (!player->key[EM_GATE_GRAY_NR(element)])
13396 return MP_NO_ACTION;
13398 else if (IS_EM_GATE_GRAY_ACTIVE(element))
13400 if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
13401 return MP_NO_ACTION;
13403 else if (IS_EMC_GATE(element))
13405 if (!player->key[EMC_GATE_NR(element)])
13406 return MP_NO_ACTION;
13408 else if (IS_EMC_GATE_GRAY(element))
13410 if (!player->key[EMC_GATE_GRAY_NR(element)])
13411 return MP_NO_ACTION;
13413 else if (IS_EMC_GATE_GRAY_ACTIVE(element))
13415 if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
13416 return MP_NO_ACTION;
13418 else if (element == EL_DC_GATE_WHITE ||
13419 element == EL_DC_GATE_WHITE_GRAY ||
13420 element == EL_DC_GATE_WHITE_GRAY_ACTIVE)
13422 if (player->num_white_keys == 0)
13423 return MP_NO_ACTION;
13425 player->num_white_keys--;
13427 else if (IS_SP_PORT(element))
13429 if (element == EL_SP_GRAVITY_PORT_LEFT ||
13430 element == EL_SP_GRAVITY_PORT_RIGHT ||
13431 element == EL_SP_GRAVITY_PORT_UP ||
13432 element == EL_SP_GRAVITY_PORT_DOWN)
13433 player->gravity = !player->gravity;
13434 else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
13435 element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
13436 element == EL_SP_GRAVITY_ON_PORT_UP ||
13437 element == EL_SP_GRAVITY_ON_PORT_DOWN)
13438 player->gravity = TRUE;
13439 else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
13440 element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
13441 element == EL_SP_GRAVITY_OFF_PORT_UP ||
13442 element == EL_SP_GRAVITY_OFF_PORT_DOWN)
13443 player->gravity = FALSE;
13446 /* automatically move to the next field with double speed */
13447 player->programmed_action = move_direction;
13449 if (player->move_delay_reset_counter == 0)
13451 player->move_delay_reset_counter = 2; /* two double speed steps */
13453 DOUBLE_PLAYER_SPEED(player);
13456 PlayLevelSoundAction(x, y, ACTION_PASSING);
13458 else if (player_can_move_or_snap && IS_DIGGABLE(element))
13462 if (mode != DF_SNAP)
13464 GfxElement[x][y] = GFX_ELEMENT(element);
13465 player->is_digging = TRUE;
13468 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
13470 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
13471 player->index_bit, dig_side);
13473 if (mode == DF_SNAP)
13475 if (level.block_snap_field)
13476 setFieldForSnapping(x, y, element, move_direction);
13478 TestIfElementTouchesCustomElement(x, y); /* for empty space */
13480 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13481 player->index_bit, dig_side);
13484 else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
13488 if (is_player && mode != DF_SNAP)
13490 GfxElement[x][y] = element;
13491 player->is_collecting = TRUE;
13494 if (element == EL_SPEED_PILL)
13496 player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
13498 else if (element == EL_EXTRA_TIME && level.time > 0)
13500 TimeLeft += level.extra_time;
13502 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
13504 DisplayGameControlValues();
13506 else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
13508 player->shield_normal_time_left += level.shield_normal_time;
13509 if (element == EL_SHIELD_DEADLY)
13510 player->shield_deadly_time_left += level.shield_deadly_time;
13512 else if (element == EL_DYNAMITE ||
13513 element == EL_EM_DYNAMITE ||
13514 element == EL_SP_DISK_RED)
13516 if (player->inventory_size < MAX_INVENTORY_SIZE)
13517 player->inventory_element[player->inventory_size++] = element;
13519 DrawGameDoorValues();
13521 else if (element == EL_DYNABOMB_INCREASE_NUMBER)
13523 player->dynabomb_count++;
13524 player->dynabombs_left++;
13526 else if (element == EL_DYNABOMB_INCREASE_SIZE)
13528 player->dynabomb_size++;
13530 else if (element == EL_DYNABOMB_INCREASE_POWER)
13532 player->dynabomb_xl = TRUE;
13534 else if (IS_KEY(element))
13536 player->key[KEY_NR(element)] = TRUE;
13538 DrawGameDoorValues();
13540 else if (element == EL_DC_KEY_WHITE)
13542 player->num_white_keys++;
13544 /* display white keys? */
13545 /* DrawGameDoorValues(); */
13547 else if (IS_ENVELOPE(element))
13549 player->show_envelope = element;
13551 else if (element == EL_EMC_LENSES)
13553 game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
13555 RedrawAllInvisibleElementsForLenses();
13557 else if (element == EL_EMC_MAGNIFIER)
13559 game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
13561 RedrawAllInvisibleElementsForMagnifier();
13563 else if (IS_DROPPABLE(element) ||
13564 IS_THROWABLE(element)) /* can be collected and dropped */
13568 if (collect_count == 0)
13569 player->inventory_infinite_element = element;
13571 for (i = 0; i < collect_count; i++)
13572 if (player->inventory_size < MAX_INVENTORY_SIZE)
13573 player->inventory_element[player->inventory_size++] = element;
13575 DrawGameDoorValues();
13577 else if (collect_count > 0)
13579 local_player->gems_still_needed -= collect_count;
13580 if (local_player->gems_still_needed < 0)
13581 local_player->gems_still_needed = 0;
13583 game_panel_controls[GAME_PANEL_GEMS].value = local_player->gems_still_needed;
13585 DisplayGameControlValues();
13588 RaiseScoreElement(element);
13589 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
13592 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
13593 player->index_bit, dig_side);
13595 if (mode == DF_SNAP)
13597 if (level.block_snap_field)
13598 setFieldForSnapping(x, y, element, move_direction);
13600 TestIfElementTouchesCustomElement(x, y); /* for empty space */
13602 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13603 player->index_bit, dig_side);
13606 else if (player_can_move_or_snap && IS_PUSHABLE(element))
13608 if (mode == DF_SNAP && element != EL_BD_ROCK)
13609 return MP_NO_ACTION;
13611 if (CAN_FALL(element) && dy)
13612 return MP_NO_ACTION;
13614 if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
13615 !(element == EL_SPRING && level.use_spring_bug))
13616 return MP_NO_ACTION;
13618 if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
13619 ((move_direction & MV_VERTICAL &&
13620 ((element_info[element].move_pattern & MV_LEFT &&
13621 IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
13622 (element_info[element].move_pattern & MV_RIGHT &&
13623 IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
13624 (move_direction & MV_HORIZONTAL &&
13625 ((element_info[element].move_pattern & MV_UP &&
13626 IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
13627 (element_info[element].move_pattern & MV_DOWN &&
13628 IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
13629 return MP_NO_ACTION;
13631 /* do not push elements already moving away faster than player */
13632 if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
13633 ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
13634 return MP_NO_ACTION;
13636 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
13638 if (player->push_delay_value == -1 || !player_was_pushing)
13639 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13641 else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
13643 if (player->push_delay_value == -1)
13644 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13646 else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
13648 if (!player->is_pushing)
13649 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13652 player->is_pushing = TRUE;
13653 player->is_active = TRUE;
13655 if (!(IN_LEV_FIELD(nextx, nexty) &&
13656 (IS_FREE(nextx, nexty) ||
13657 (IS_SB_ELEMENT(element) &&
13658 Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY) ||
13659 (IS_CUSTOM_ELEMENT(element) &&
13660 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty)))))
13661 return MP_NO_ACTION;
13663 if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
13664 return MP_NO_ACTION;
13666 if (player->push_delay == -1) /* new pushing; restart delay */
13667 player->push_delay = 0;
13669 if (player->push_delay < player->push_delay_value &&
13670 !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
13671 element != EL_SPRING && element != EL_BALLOON)
13673 /* make sure that there is no move delay before next try to push */
13674 if (game.engine_version >= VERSION_IDENT(3,0,7,1))
13675 player->move_delay = 0;
13677 return MP_NO_ACTION;
13680 if (IS_CUSTOM_ELEMENT(element) &&
13681 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty))
13683 if (!DigFieldByCE(nextx, nexty, element))
13684 return MP_NO_ACTION;
13687 if (IS_SB_ELEMENT(element))
13689 if (element == EL_SOKOBAN_FIELD_FULL)
13691 Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
13692 local_player->sokobanfields_still_needed++;
13695 if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
13697 Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
13698 local_player->sokobanfields_still_needed--;
13701 Feld[x][y] = EL_SOKOBAN_OBJECT;
13703 if (Back[x][y] == Back[nextx][nexty])
13704 PlayLevelSoundAction(x, y, ACTION_PUSHING);
13705 else if (Back[x][y] != 0)
13706 PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
13709 PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
13712 if (local_player->sokobanfields_still_needed == 0 &&
13713 (game.emulation == EMU_SOKOBAN || level.auto_exit_sokoban))
13715 PlayerWins(player);
13717 PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
13721 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
13723 InitMovingField(x, y, move_direction);
13724 GfxAction[x][y] = ACTION_PUSHING;
13726 if (mode == DF_SNAP)
13727 ContinueMoving(x, y);
13729 MovPos[x][y] = (dx != 0 ? dx : dy);
13731 Pushed[x][y] = TRUE;
13732 Pushed[nextx][nexty] = TRUE;
13734 if (game.engine_version < VERSION_IDENT(2,2,0,7))
13735 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13737 player->push_delay_value = -1; /* get new value later */
13739 /* check for element change _after_ element has been pushed */
13740 if (game.use_change_when_pushing_bug)
13742 CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
13743 player->index_bit, dig_side);
13744 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
13745 player->index_bit, dig_side);
13748 else if (IS_SWITCHABLE(element))
13750 if (PLAYER_SWITCHING(player, x, y))
13752 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
13753 player->index_bit, dig_side);
13758 player->is_switching = TRUE;
13759 player->switch_x = x;
13760 player->switch_y = y;
13762 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
13764 if (element == EL_ROBOT_WHEEL)
13766 Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
13770 game.robot_wheel_active = TRUE;
13772 TEST_DrawLevelField(x, y);
13774 else if (element == EL_SP_TERMINAL)
13778 SCAN_PLAYFIELD(xx, yy)
13780 if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
13782 else if (Feld[xx][yy] == EL_SP_TERMINAL)
13783 Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
13786 else if (IS_BELT_SWITCH(element))
13788 ToggleBeltSwitch(x, y);
13790 else if (element == EL_SWITCHGATE_SWITCH_UP ||
13791 element == EL_SWITCHGATE_SWITCH_DOWN ||
13792 element == EL_DC_SWITCHGATE_SWITCH_UP ||
13793 element == EL_DC_SWITCHGATE_SWITCH_DOWN)
13795 ToggleSwitchgateSwitch(x, y);
13797 else if (element == EL_LIGHT_SWITCH ||
13798 element == EL_LIGHT_SWITCH_ACTIVE)
13800 ToggleLightSwitch(x, y);
13802 else if (element == EL_TIMEGATE_SWITCH ||
13803 element == EL_DC_TIMEGATE_SWITCH)
13805 ActivateTimegateSwitch(x, y);
13807 else if (element == EL_BALLOON_SWITCH_LEFT ||
13808 element == EL_BALLOON_SWITCH_RIGHT ||
13809 element == EL_BALLOON_SWITCH_UP ||
13810 element == EL_BALLOON_SWITCH_DOWN ||
13811 element == EL_BALLOON_SWITCH_NONE ||
13812 element == EL_BALLOON_SWITCH_ANY)
13814 game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT ? MV_LEFT :
13815 element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
13816 element == EL_BALLOON_SWITCH_UP ? MV_UP :
13817 element == EL_BALLOON_SWITCH_DOWN ? MV_DOWN :
13818 element == EL_BALLOON_SWITCH_NONE ? MV_NONE :
13821 else if (element == EL_LAMP)
13823 Feld[x][y] = EL_LAMP_ACTIVE;
13824 local_player->lights_still_needed--;
13826 ResetGfxAnimation(x, y);
13827 TEST_DrawLevelField(x, y);
13829 else if (element == EL_TIME_ORB_FULL)
13831 Feld[x][y] = EL_TIME_ORB_EMPTY;
13833 if (level.time > 0 || level.use_time_orb_bug)
13835 TimeLeft += level.time_orb_time;
13836 game.no_time_limit = FALSE;
13838 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
13840 DisplayGameControlValues();
13843 ResetGfxAnimation(x, y);
13844 TEST_DrawLevelField(x, y);
13846 else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
13847 element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
13851 game.ball_state = !game.ball_state;
13853 SCAN_PLAYFIELD(xx, yy)
13855 int e = Feld[xx][yy];
13857 if (game.ball_state)
13859 if (e == EL_EMC_MAGIC_BALL)
13860 CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
13861 else if (e == EL_EMC_MAGIC_BALL_SWITCH)
13862 CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
13866 if (e == EL_EMC_MAGIC_BALL_ACTIVE)
13867 CreateField(xx, yy, EL_EMC_MAGIC_BALL);
13868 else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
13869 CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
13874 CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
13875 player->index_bit, dig_side);
13877 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
13878 player->index_bit, dig_side);
13880 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
13881 player->index_bit, dig_side);
13887 if (!PLAYER_SWITCHING(player, x, y))
13889 player->is_switching = TRUE;
13890 player->switch_x = x;
13891 player->switch_y = y;
13893 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
13894 player->index_bit, dig_side);
13895 CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
13896 player->index_bit, dig_side);
13898 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
13899 player->index_bit, dig_side);
13900 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
13901 player->index_bit, dig_side);
13904 CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
13905 player->index_bit, dig_side);
13906 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
13907 player->index_bit, dig_side);
13909 return MP_NO_ACTION;
13912 player->push_delay = -1;
13914 if (is_player) /* function can also be called by EL_PENGUIN */
13916 if (Feld[x][y] != element) /* really digged/collected something */
13918 player->is_collecting = !player->is_digging;
13919 player->is_active = TRUE;
13926 static boolean DigFieldByCE(int x, int y, int digging_element)
13928 int element = Feld[x][y];
13930 if (!IS_FREE(x, y))
13932 int action = (IS_DIGGABLE(element) ? ACTION_DIGGING :
13933 IS_COLLECTIBLE(element) ? ACTION_COLLECTING :
13936 /* no element can dig solid indestructible elements */
13937 if (IS_INDESTRUCTIBLE(element) &&
13938 !IS_DIGGABLE(element) &&
13939 !IS_COLLECTIBLE(element))
13942 if (AmoebaNr[x][y] &&
13943 (element == EL_AMOEBA_FULL ||
13944 element == EL_BD_AMOEBA ||
13945 element == EL_AMOEBA_GROWING))
13947 AmoebaCnt[AmoebaNr[x][y]]--;
13948 AmoebaCnt2[AmoebaNr[x][y]]--;
13951 if (IS_MOVING(x, y))
13952 RemoveMovingField(x, y);
13956 TEST_DrawLevelField(x, y);
13959 /* if digged element was about to explode, prevent the explosion */
13960 ExplodeField[x][y] = EX_TYPE_NONE;
13962 PlayLevelSoundAction(x, y, action);
13965 Store[x][y] = EL_EMPTY;
13967 /* this makes it possible to leave the removed element again */
13968 if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element)))
13969 Store[x][y] = element;
13974 static boolean SnapField(struct PlayerInfo *player, int dx, int dy)
13976 int jx = player->jx, jy = player->jy;
13977 int x = jx + dx, y = jy + dy;
13978 int snap_direction = (dx == -1 ? MV_LEFT :
13979 dx == +1 ? MV_RIGHT :
13981 dy == +1 ? MV_DOWN : MV_NONE);
13982 boolean can_continue_snapping = (level.continuous_snapping &&
13983 WasJustFalling[x][y] < CHECK_DELAY_FALLING);
13985 if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
13988 if (!player->active || !IN_LEV_FIELD(x, y))
13996 if (player->MovPos == 0)
13997 player->is_pushing = FALSE;
13999 player->is_snapping = FALSE;
14001 if (player->MovPos == 0)
14003 player->is_moving = FALSE;
14004 player->is_digging = FALSE;
14005 player->is_collecting = FALSE;
14011 /* prevent snapping with already pressed snap key when not allowed */
14012 if (player->is_snapping && !can_continue_snapping)
14015 player->MovDir = snap_direction;
14017 if (player->MovPos == 0)
14019 player->is_moving = FALSE;
14020 player->is_digging = FALSE;
14021 player->is_collecting = FALSE;
14024 player->is_dropping = FALSE;
14025 player->is_dropping_pressed = FALSE;
14026 player->drop_pressed_delay = 0;
14028 if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
14031 player->is_snapping = TRUE;
14032 player->is_active = TRUE;
14034 if (player->MovPos == 0)
14036 player->is_moving = FALSE;
14037 player->is_digging = FALSE;
14038 player->is_collecting = FALSE;
14041 if (player->MovPos != 0) /* prevent graphic bugs in versions < 2.2.0 */
14042 TEST_DrawLevelField(player->last_jx, player->last_jy);
14044 TEST_DrawLevelField(x, y);
14049 static boolean DropElement(struct PlayerInfo *player)
14051 int old_element, new_element;
14052 int dropx = player->jx, dropy = player->jy;
14053 int drop_direction = player->MovDir;
14054 int drop_side = drop_direction;
14055 int drop_element = get_next_dropped_element(player);
14057 player->is_dropping_pressed = TRUE;
14059 /* do not drop an element on top of another element; when holding drop key
14060 pressed without moving, dropped element must move away before the next
14061 element can be dropped (this is especially important if the next element
14062 is dynamite, which can be placed on background for historical reasons) */
14063 if (PLAYER_DROPPING(player, dropx, dropy) && Feld[dropx][dropy] != EL_EMPTY)
14066 if (IS_THROWABLE(drop_element))
14068 dropx += GET_DX_FROM_DIR(drop_direction);
14069 dropy += GET_DY_FROM_DIR(drop_direction);
14071 if (!IN_LEV_FIELD(dropx, dropy))
14075 old_element = Feld[dropx][dropy]; /* old element at dropping position */
14076 new_element = drop_element; /* default: no change when dropping */
14078 /* check if player is active, not moving and ready to drop */
14079 if (!player->active || player->MovPos || player->drop_delay > 0)
14082 /* check if player has anything that can be dropped */
14083 if (new_element == EL_UNDEFINED)
14086 /* check if drop key was pressed long enough for EM style dynamite */
14087 if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
14090 /* check if anything can be dropped at the current position */
14091 if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
14094 /* collected custom elements can only be dropped on empty fields */
14095 if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
14098 if (old_element != EL_EMPTY)
14099 Back[dropx][dropy] = old_element; /* store old element on this field */
14101 ResetGfxAnimation(dropx, dropy);
14102 ResetRandomAnimationValue(dropx, dropy);
14104 if (player->inventory_size > 0 ||
14105 player->inventory_infinite_element != EL_UNDEFINED)
14107 if (player->inventory_size > 0)
14109 player->inventory_size--;
14111 DrawGameDoorValues();
14113 if (new_element == EL_DYNAMITE)
14114 new_element = EL_DYNAMITE_ACTIVE;
14115 else if (new_element == EL_EM_DYNAMITE)
14116 new_element = EL_EM_DYNAMITE_ACTIVE;
14117 else if (new_element == EL_SP_DISK_RED)
14118 new_element = EL_SP_DISK_RED_ACTIVE;
14121 Feld[dropx][dropy] = new_element;
14123 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
14124 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
14125 el2img(Feld[dropx][dropy]), 0);
14127 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
14129 /* needed if previous element just changed to "empty" in the last frame */
14130 ChangeCount[dropx][dropy] = 0; /* allow at least one more change */
14132 CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
14133 player->index_bit, drop_side);
14134 CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
14136 player->index_bit, drop_side);
14138 TestIfElementTouchesCustomElement(dropx, dropy);
14140 else /* player is dropping a dyna bomb */
14142 player->dynabombs_left--;
14144 Feld[dropx][dropy] = new_element;
14146 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
14147 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
14148 el2img(Feld[dropx][dropy]), 0);
14150 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
14153 if (Feld[dropx][dropy] == new_element) /* uninitialized unless CE change */
14154 InitField_WithBug1(dropx, dropy, FALSE);
14156 new_element = Feld[dropx][dropy]; /* element might have changed */
14158 if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
14159 element_info[new_element].move_pattern == MV_WHEN_DROPPED)
14161 if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
14162 MovDir[dropx][dropy] = drop_direction;
14164 ChangeCount[dropx][dropy] = 0; /* allow at least one more change */
14166 /* do not cause impact style collision by dropping elements that can fall */
14167 CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
14170 player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
14171 player->is_dropping = TRUE;
14173 player->drop_pressed_delay = 0;
14174 player->is_dropping_pressed = FALSE;
14176 player->drop_x = dropx;
14177 player->drop_y = dropy;
14182 /* ------------------------------------------------------------------------- */
14183 /* game sound playing functions */
14184 /* ------------------------------------------------------------------------- */
14186 static int *loop_sound_frame = NULL;
14187 static int *loop_sound_volume = NULL;
14189 void InitPlayLevelSound()
14191 int num_sounds = getSoundListSize();
14193 checked_free(loop_sound_frame);
14194 checked_free(loop_sound_volume);
14196 loop_sound_frame = checked_calloc(num_sounds * sizeof(int));
14197 loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
14200 static void PlayLevelSound(int x, int y, int nr)
14202 int sx = SCREENX(x), sy = SCREENY(y);
14203 int volume, stereo_position;
14204 int max_distance = 8;
14205 int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
14207 if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
14208 (!setup.sound_loops && IS_LOOP_SOUND(nr)))
14211 if (!IN_LEV_FIELD(x, y) ||
14212 sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
14213 sy < -max_distance || sy >= SCR_FIELDY + max_distance)
14216 volume = SOUND_MAX_VOLUME;
14218 if (!IN_SCR_FIELD(sx, sy))
14220 int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
14221 int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
14223 volume -= volume * (dx > dy ? dx : dy) / max_distance;
14226 stereo_position = (SOUND_MAX_LEFT +
14227 (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
14228 (SCR_FIELDX + 2 * max_distance));
14230 if (IS_LOOP_SOUND(nr))
14232 /* This assures that quieter loop sounds do not overwrite louder ones,
14233 while restarting sound volume comparison with each new game frame. */
14235 if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
14238 loop_sound_volume[nr] = volume;
14239 loop_sound_frame[nr] = FrameCounter;
14242 PlaySoundExt(nr, volume, stereo_position, type);
14245 static void PlayLevelSoundNearest(int x, int y, int sound_action)
14247 PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
14248 x > LEVELX(BX2) ? LEVELX(BX2) : x,
14249 y < LEVELY(BY1) ? LEVELY(BY1) :
14250 y > LEVELY(BY2) ? LEVELY(BY2) : y,
14254 static void PlayLevelSoundAction(int x, int y, int action)
14256 PlayLevelSoundElementAction(x, y, Feld[x][y], action);
14259 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
14261 int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
14263 if (sound_effect != SND_UNDEFINED)
14264 PlayLevelSound(x, y, sound_effect);
14267 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
14270 int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
14272 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14273 PlayLevelSound(x, y, sound_effect);
14276 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
14278 int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
14280 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14281 PlayLevelSound(x, y, sound_effect);
14284 static void StopLevelSoundActionIfLoop(int x, int y, int action)
14286 int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
14288 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14289 StopSound(sound_effect);
14292 static void PlayLevelMusic()
14294 if (levelset.music[level_nr] != MUS_UNDEFINED)
14295 PlayMusic(levelset.music[level_nr]); /* from config file */
14297 PlayMusic(MAP_NOCONF_MUSIC(level_nr)); /* from music dir */
14300 void PlayLevelSound_EM(int xx, int yy, int element_em, int sample)
14302 int element = (element_em > -1 ? map_element_EM_to_RND(element_em) : 0);
14303 int offset = (BorderElement == EL_STEELWALL ? 1 : 0);
14304 int x = xx - 1 - offset;
14305 int y = yy - 1 - offset;
14310 PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
14314 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14318 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14322 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14326 PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
14330 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14334 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14337 case SAMPLE_android_clone:
14338 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14341 case SAMPLE_android_move:
14342 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14345 case SAMPLE_spring:
14346 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14350 PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
14354 PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
14357 case SAMPLE_eater_eat:
14358 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14362 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14365 case SAMPLE_collect:
14366 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
14369 case SAMPLE_diamond:
14370 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14373 case SAMPLE_squash:
14374 /* !!! CHECK THIS !!! */
14376 PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
14378 PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
14382 case SAMPLE_wonderfall:
14383 PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
14387 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14391 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14395 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14399 PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
14403 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14407 PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
14410 case SAMPLE_wonder:
14411 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14415 PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
14418 case SAMPLE_exit_open:
14419 PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
14422 case SAMPLE_exit_leave:
14423 PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
14426 case SAMPLE_dynamite:
14427 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14431 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14435 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
14439 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14443 PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
14447 PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
14451 PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
14455 PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
14460 void PlayLevelSound_SP(int xx, int yy, int element_sp, int action_sp)
14462 int element = map_element_SP_to_RND(element_sp);
14463 int action = map_action_SP_to_RND(action_sp);
14464 int offset = (setup.sp_show_border_elements ? 0 : 1);
14465 int x = xx - offset;
14466 int y = yy - offset;
14468 PlayLevelSoundElementAction(x, y, element, action);
14471 void RaiseScore(int value)
14473 local_player->score += value;
14475 game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
14477 DisplayGameControlValues();
14480 void RaiseScoreElement(int element)
14485 case EL_BD_DIAMOND:
14486 case EL_EMERALD_YELLOW:
14487 case EL_EMERALD_RED:
14488 case EL_EMERALD_PURPLE:
14489 case EL_SP_INFOTRON:
14490 RaiseScore(level.score[SC_EMERALD]);
14493 RaiseScore(level.score[SC_DIAMOND]);
14496 RaiseScore(level.score[SC_CRYSTAL]);
14499 RaiseScore(level.score[SC_PEARL]);
14502 case EL_BD_BUTTERFLY:
14503 case EL_SP_ELECTRON:
14504 RaiseScore(level.score[SC_BUG]);
14507 case EL_BD_FIREFLY:
14508 case EL_SP_SNIKSNAK:
14509 RaiseScore(level.score[SC_SPACESHIP]);
14512 case EL_DARK_YAMYAM:
14513 RaiseScore(level.score[SC_YAMYAM]);
14516 RaiseScore(level.score[SC_ROBOT]);
14519 RaiseScore(level.score[SC_PACMAN]);
14522 RaiseScore(level.score[SC_NUT]);
14525 case EL_EM_DYNAMITE:
14526 case EL_SP_DISK_RED:
14527 case EL_DYNABOMB_INCREASE_NUMBER:
14528 case EL_DYNABOMB_INCREASE_SIZE:
14529 case EL_DYNABOMB_INCREASE_POWER:
14530 RaiseScore(level.score[SC_DYNAMITE]);
14532 case EL_SHIELD_NORMAL:
14533 case EL_SHIELD_DEADLY:
14534 RaiseScore(level.score[SC_SHIELD]);
14536 case EL_EXTRA_TIME:
14537 RaiseScore(level.extra_time_score);
14551 case EL_DC_KEY_WHITE:
14552 RaiseScore(level.score[SC_KEY]);
14555 RaiseScore(element_info[element].collect_score);
14560 void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message)
14562 if (skip_request || Request(message, REQ_ASK | REQ_STAY_CLOSED))
14564 /* closing door required in case of envelope style request dialogs */
14566 CloseDoor(DOOR_CLOSE_1);
14568 #if defined(NETWORK_AVALIABLE)
14569 if (options.network)
14570 SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
14576 FadeSkipNextFadeIn();
14578 game_status = GAME_MODE_MAIN;
14584 game_status = GAME_MODE_MAIN;
14590 else /* continue playing the game */
14592 if (tape.playing && tape.deactivate_display)
14593 TapeDeactivateDisplayOff(TRUE);
14595 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
14597 if (tape.playing && tape.deactivate_display)
14598 TapeDeactivateDisplayOn();
14602 void RequestQuitGame(boolean ask_if_really_quit)
14604 boolean quick_quit = (!ask_if_really_quit || level_editor_test_game);
14605 boolean skip_request = AllPlayersGone || quick_quit;
14607 RequestQuitGameExt(skip_request, quick_quit,
14608 "Do you really want to quit the game?");
14612 /* ------------------------------------------------------------------------- */
14613 /* random generator functions */
14614 /* ------------------------------------------------------------------------- */
14616 unsigned int InitEngineRandom_RND(int seed)
14618 game.num_random_calls = 0;
14620 return InitEngineRandom(seed);
14623 unsigned int RND(int max)
14627 game.num_random_calls++;
14629 return GetEngineRandom(max);
14636 /* ------------------------------------------------------------------------- */
14637 /* game engine snapshot handling functions */
14638 /* ------------------------------------------------------------------------- */
14640 struct EngineSnapshotInfo
14642 /* runtime values for custom element collect score */
14643 int collect_score[NUM_CUSTOM_ELEMENTS];
14645 /* runtime values for group element choice position */
14646 int choice_pos[NUM_GROUP_ELEMENTS];
14648 /* runtime values for belt position animations */
14649 int belt_graphic[4][NUM_BELT_PARTS];
14650 int belt_anim_mode[4][NUM_BELT_PARTS];
14653 static struct EngineSnapshotInfo engine_snapshot_rnd;
14654 static char *snapshot_level_identifier = NULL;
14655 static int snapshot_level_nr = -1;
14657 static void SaveEngineSnapshotValues_RND()
14659 static int belt_base_active_element[4] =
14661 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
14662 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
14663 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
14664 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
14668 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
14670 int element = EL_CUSTOM_START + i;
14672 engine_snapshot_rnd.collect_score[i] = element_info[element].collect_score;
14675 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
14677 int element = EL_GROUP_START + i;
14679 engine_snapshot_rnd.choice_pos[i] = element_info[element].group->choice_pos;
14682 for (i = 0; i < 4; i++)
14684 for (j = 0; j < NUM_BELT_PARTS; j++)
14686 int element = belt_base_active_element[i] + j;
14687 int graphic = el2img(element);
14688 int anim_mode = graphic_info[graphic].anim_mode;
14690 engine_snapshot_rnd.belt_graphic[i][j] = graphic;
14691 engine_snapshot_rnd.belt_anim_mode[i][j] = anim_mode;
14696 static void LoadEngineSnapshotValues_RND()
14698 unsigned int num_random_calls = game.num_random_calls;
14701 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
14703 int element = EL_CUSTOM_START + i;
14705 element_info[element].collect_score = engine_snapshot_rnd.collect_score[i];
14708 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
14710 int element = EL_GROUP_START + i;
14712 element_info[element].group->choice_pos = engine_snapshot_rnd.choice_pos[i];
14715 for (i = 0; i < 4; i++)
14717 for (j = 0; j < NUM_BELT_PARTS; j++)
14719 int graphic = engine_snapshot_rnd.belt_graphic[i][j];
14720 int anim_mode = engine_snapshot_rnd.belt_anim_mode[i][j];
14722 graphic_info[graphic].anim_mode = anim_mode;
14726 if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
14728 InitRND(tape.random_seed);
14729 for (i = 0; i < num_random_calls; i++)
14733 if (game.num_random_calls != num_random_calls)
14735 Error(ERR_INFO, "number of random calls out of sync");
14736 Error(ERR_INFO, "number of random calls should be %d", num_random_calls);
14737 Error(ERR_INFO, "number of random calls is %d", game.num_random_calls);
14738 Error(ERR_EXIT, "this should not happen -- please debug");
14742 void FreeEngineSnapshotSingle()
14744 FreeSnapshotSingle();
14746 setString(&snapshot_level_identifier, NULL);
14747 snapshot_level_nr = -1;
14750 void FreeEngineSnapshotList()
14752 FreeSnapshotList();
14755 ListNode *SaveEngineSnapshotBuffers()
14757 ListNode *buffers = NULL;
14759 /* copy some special values to a structure better suited for the snapshot */
14761 if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
14762 SaveEngineSnapshotValues_RND();
14763 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
14764 SaveEngineSnapshotValues_EM();
14765 if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
14766 SaveEngineSnapshotValues_SP(&buffers);
14768 /* save values stored in special snapshot structure */
14770 if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
14771 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
14772 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
14773 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
14774 if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
14775 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_sp));
14777 /* save further RND engine values */
14779 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(stored_player));
14780 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(game));
14781 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(tape));
14783 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ZX));
14784 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ZY));
14785 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExitX));
14786 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExitY));
14788 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(FrameCounter));
14789 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeFrames));
14790 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimePlayed));
14791 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeLeft));
14792 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TapeTime));
14794 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovDir));
14795 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovPos));
14796 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenGfxPos));
14798 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScrollStepSize));
14800 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AllPlayersGone));
14802 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt));
14803 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2));
14805 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Feld));
14806 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovPos));
14807 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDir));
14808 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDelay));
14809 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeDelay));
14810 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangePage));
14811 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CustomValue));
14812 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store));
14813 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store2));
14814 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(StorePlayer));
14815 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Back));
14816 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaNr));
14817 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustMoving));
14818 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustFalling));
14819 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckCollision));
14820 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckImpact));
14821 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Stop));
14822 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Pushed));
14824 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeCount));
14825 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeEvent));
14827 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodePhase));
14828 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeDelay));
14829 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeField));
14831 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(RunnerVisit));
14832 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(PlayerVisit));
14834 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxFrame));
14835 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxRandom));
14836 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxElement));
14837 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxAction));
14838 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxDir));
14840 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_x));
14841 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_y));
14844 ListNode *node = engine_snapshot_list_rnd;
14847 while (node != NULL)
14849 num_bytes += ((struct EngineSnapshotNodeInfo *)node->content)->size;
14854 printf("::: size of engine snapshot: %d bytes\n", num_bytes);
14860 void SaveEngineSnapshotSingle()
14862 ListNode *buffers = SaveEngineSnapshotBuffers();
14864 /* finally save all snapshot buffers to single snapshot */
14865 SaveSnapshotSingle(buffers);
14867 /* save level identification information */
14868 setString(&snapshot_level_identifier, leveldir_current->identifier);
14869 snapshot_level_nr = level_nr;
14872 static boolean SaveEngineSnapshotToListExt(boolean initial_snapshot)
14874 boolean save_snapshot =
14875 (initial_snapshot ||
14876 (game.snapshot.mode == SNAPSHOT_MODE_EVERY_STEP) ||
14877 (game.snapshot.mode == SNAPSHOT_MODE_EVERY_MOVE &&
14878 game.snapshot.changed_action));
14880 game.snapshot.changed_action = FALSE;
14882 if (game.snapshot.mode == SNAPSHOT_MODE_OFF ||
14883 tape.quick_resume ||
14887 ListNode *buffers = SaveEngineSnapshotBuffers();
14889 /* finally save all snapshot buffers to snapshot list */
14890 SaveSnapshotToList(buffers);
14895 boolean SaveEngineSnapshotToList()
14897 return SaveEngineSnapshotToListExt(FALSE);
14900 void SaveEngineSnapshotToListInitial()
14902 FreeEngineSnapshotList();
14904 SaveEngineSnapshotToListExt(TRUE);
14907 void LoadEngineSnapshotValues()
14909 /* restore special values from snapshot structure */
14911 if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
14912 LoadEngineSnapshotValues_RND();
14913 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
14914 LoadEngineSnapshotValues_EM();
14915 if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
14916 LoadEngineSnapshotValues_SP();
14919 void LoadEngineSnapshotSingle()
14921 LoadSnapshotSingle();
14923 LoadEngineSnapshotValues();
14926 void LoadEngineSnapshot_Undo(int steps)
14928 LoadSnapshotFromList_Older(steps);
14930 LoadEngineSnapshotValues();
14933 void LoadEngineSnapshot_Redo(int steps)
14935 LoadSnapshotFromList_Newer(steps);
14937 LoadEngineSnapshotValues();
14940 boolean CheckEngineSnapshotSingle()
14942 return (strEqual(snapshot_level_identifier, leveldir_current->identifier) &&
14943 snapshot_level_nr == level_nr);
14946 boolean CheckEngineSnapshotList()
14948 return CheckSnapshotList();
14952 /* ---------- new game button stuff ---------------------------------------- */
14960 } gamebutton_info[NUM_GAME_BUTTONS] =
14963 IMG_GAME_BUTTON_GFX_STOP, &game.button.stop,
14964 GAME_CTRL_ID_STOP, "stop game"
14967 IMG_GAME_BUTTON_GFX_PAUSE, &game.button.pause,
14968 GAME_CTRL_ID_PAUSE, "pause game"
14971 IMG_GAME_BUTTON_GFX_PLAY, &game.button.play,
14972 GAME_CTRL_ID_PLAY, "play game"
14975 IMG_GAME_BUTTON_GFX_UNDO, &game.button.undo,
14976 GAME_CTRL_ID_UNDO, "undo step"
14979 IMG_GAME_BUTTON_GFX_REDO, &game.button.redo,
14980 GAME_CTRL_ID_REDO, "redo step"
14983 IMG_GAME_BUTTON_GFX_SAVE, &game.button.save,
14984 GAME_CTRL_ID_SAVE, "save game"
14987 IMG_GAME_BUTTON_GFX_PAUSE2, &game.button.pause2,
14988 GAME_CTRL_ID_PAUSE2, "pause game"
14991 IMG_GAME_BUTTON_GFX_LOAD, &game.button.load,
14992 GAME_CTRL_ID_LOAD, "load game"
14995 IMG_GAME_BUTTON_GFX_SOUND_MUSIC, &game.button.sound_music,
14996 SOUND_CTRL_ID_MUSIC, "background music on/off"
14999 IMG_GAME_BUTTON_GFX_SOUND_LOOPS, &game.button.sound_loops,
15000 SOUND_CTRL_ID_LOOPS, "sound loops on/off"
15003 IMG_GAME_BUTTON_GFX_SOUND_SIMPLE, &game.button.sound_simple,
15004 SOUND_CTRL_ID_SIMPLE, "normal sounds on/off"
15008 void CreateGameButtons()
15012 for (i = 0; i < NUM_GAME_BUTTONS; i++)
15014 struct GraphicInfo *gfx = &graphic_info[gamebutton_info[i].graphic];
15015 struct XY *pos = gamebutton_info[i].pos;
15016 struct GadgetInfo *gi;
15019 unsigned int event_mask;
15020 int base_x = (tape.show_game_buttons ? VX : DX);
15021 int base_y = (tape.show_game_buttons ? VY : DY);
15022 int gd_x = gfx->src_x;
15023 int gd_y = gfx->src_y;
15024 int gd_xp = gfx->src_x + gfx->pressed_xoffset;
15025 int gd_yp = gfx->src_y + gfx->pressed_yoffset;
15026 int gd_xa = gfx->src_x + gfx->active_xoffset;
15027 int gd_ya = gfx->src_y + gfx->active_yoffset;
15028 int gd_xap = gfx->src_x + gfx->active_xoffset + gfx->pressed_xoffset;
15029 int gd_yap = gfx->src_y + gfx->active_yoffset + gfx->pressed_yoffset;
15032 if (gfx->bitmap == NULL)
15034 game_gadget[id] = NULL;
15039 if (id == GAME_CTRL_ID_STOP ||
15040 id == GAME_CTRL_ID_PLAY ||
15041 id == GAME_CTRL_ID_SAVE ||
15042 id == GAME_CTRL_ID_LOAD)
15044 button_type = GD_TYPE_NORMAL_BUTTON;
15046 event_mask = GD_EVENT_RELEASED;
15048 else if (id == GAME_CTRL_ID_UNDO ||
15049 id == GAME_CTRL_ID_REDO)
15051 button_type = GD_TYPE_NORMAL_BUTTON;
15053 event_mask = GD_EVENT_PRESSED | GD_EVENT_REPEATED;
15057 button_type = GD_TYPE_CHECK_BUTTON;
15059 ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
15060 (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
15061 (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
15062 event_mask = GD_EVENT_PRESSED;
15065 gi = CreateGadget(GDI_CUSTOM_ID, id,
15066 GDI_INFO_TEXT, gamebutton_info[i].infotext,
15067 GDI_X, base_x + GDI_ACTIVE_POS(pos->x),
15068 GDI_Y, base_y + GDI_ACTIVE_POS(pos->y),
15069 GDI_WIDTH, gfx->width,
15070 GDI_HEIGHT, gfx->height,
15071 GDI_TYPE, button_type,
15072 GDI_STATE, GD_BUTTON_UNPRESSED,
15073 GDI_CHECKED, checked,
15074 GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
15075 GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
15076 GDI_ALT_DESIGN_UNPRESSED, gfx->bitmap, gd_xa, gd_ya,
15077 GDI_ALT_DESIGN_PRESSED, gfx->bitmap, gd_xap, gd_yap,
15078 GDI_DIRECT_DRAW, FALSE,
15079 GDI_EVENT_MASK, event_mask,
15080 GDI_CALLBACK_ACTION, HandleGameButtons,
15084 Error(ERR_EXIT, "cannot create gadget");
15086 game_gadget[id] = gi;
15090 void FreeGameButtons()
15094 for (i = 0; i < NUM_GAME_BUTTONS; i++)
15095 FreeGadget(game_gadget[i]);
15098 static void MapGameButtonsAtSamePosition(int id)
15102 for (i = 0; i < NUM_GAME_BUTTONS; i++)
15104 gamebutton_info[i].pos->x == gamebutton_info[id].pos->x &&
15105 gamebutton_info[i].pos->y == gamebutton_info[id].pos->y)
15106 MapGadget(game_gadget[i]);
15109 static void UnmapGameButtonsAtSamePosition(int id)
15113 for (i = 0; i < NUM_GAME_BUTTONS; i++)
15115 gamebutton_info[i].pos->x == gamebutton_info[id].pos->x &&
15116 gamebutton_info[i].pos->y == gamebutton_info[id].pos->y)
15117 UnmapGadget(game_gadget[i]);
15120 void MapUndoRedoButtons()
15122 UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
15123 UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
15125 MapGadget(game_gadget[GAME_CTRL_ID_UNDO]);
15126 MapGadget(game_gadget[GAME_CTRL_ID_REDO]);
15129 void UnmapUndoRedoButtons()
15131 UnmapGadget(game_gadget[GAME_CTRL_ID_UNDO]);
15132 UnmapGadget(game_gadget[GAME_CTRL_ID_REDO]);
15134 MapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
15135 MapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
15138 void MapGameButtons()
15142 for (i = 0; i < NUM_GAME_BUTTONS; i++)
15143 if (i != GAME_CTRL_ID_UNDO &&
15144 i != GAME_CTRL_ID_REDO)
15145 MapGadget(game_gadget[i]);
15147 if (setup.show_snapshot_buttons)
15149 UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_SAVE);
15150 UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE2);
15151 UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_LOAD);
15155 UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_STOP);
15156 UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE);
15157 UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PLAY);
15160 RedrawGameButtons();
15163 void UnmapGameButtons()
15167 for (i = 0; i < NUM_GAME_BUTTONS; i++)
15168 UnmapGadget(game_gadget[i]);
15171 void RedrawGameButtons()
15175 for (i = 0; i < NUM_GAME_BUTTONS; i++)
15176 RedrawGadget(game_gadget[i]);
15178 // RedrawGadget() may have set REDRAW_ALL if buttons are defined off-area
15179 redraw_mask &= ~REDRAW_ALL;
15182 void GameUndoRedoExt()
15184 ClearPlayerAction();
15186 tape.pausing = TRUE;
15189 UpdateAndDisplayGameControlValues();
15191 DrawCompleteVideoDisplay();
15192 DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
15193 DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
15194 DrawVideoDisplay((tape.single_step ? VIDEO_STATE_1STEP_ON :
15195 VIDEO_STATE_1STEP_OFF), 0);
15200 void GameUndo(int steps)
15202 if (!CheckEngineSnapshotList())
15205 LoadEngineSnapshot_Undo(steps);
15210 void GameRedo(int steps)
15212 if (!CheckEngineSnapshotList())
15215 LoadEngineSnapshot_Redo(steps);
15220 static void HandleGameButtonsExt(int id, int button)
15222 int steps = BUTTON_STEPSIZE(button);
15223 boolean handle_game_buttons =
15224 (game_status == GAME_MODE_PLAYING ||
15225 (game_status == GAME_MODE_MAIN && tape.show_game_buttons));
15227 if (!handle_game_buttons)
15232 case GAME_CTRL_ID_STOP:
15233 if (game_status == GAME_MODE_MAIN)
15239 RequestQuitGame(TRUE);
15243 case GAME_CTRL_ID_PAUSE:
15244 case GAME_CTRL_ID_PAUSE2:
15245 if (options.network && game_status == GAME_MODE_PLAYING)
15247 #if defined(NETWORK_AVALIABLE)
15249 SendToServer_ContinuePlaying();
15251 SendToServer_PausePlaying();
15255 TapeTogglePause(TAPE_TOGGLE_MANUAL);
15258 case GAME_CTRL_ID_PLAY:
15259 if (game_status == GAME_MODE_MAIN)
15261 StartGameActions(options.network, setup.autorecord, level.random_seed);
15263 else if (tape.pausing)
15265 #if defined(NETWORK_AVALIABLE)
15266 if (options.network)
15267 SendToServer_ContinuePlaying();
15270 TapeTogglePause(TAPE_TOGGLE_MANUAL);
15274 case GAME_CTRL_ID_UNDO:
15278 case GAME_CTRL_ID_REDO:
15282 case GAME_CTRL_ID_SAVE:
15286 case GAME_CTRL_ID_LOAD:
15290 case SOUND_CTRL_ID_MUSIC:
15291 if (setup.sound_music)
15293 setup.sound_music = FALSE;
15297 else if (audio.music_available)
15299 setup.sound = setup.sound_music = TRUE;
15301 SetAudioMode(setup.sound);
15307 case SOUND_CTRL_ID_LOOPS:
15308 if (setup.sound_loops)
15309 setup.sound_loops = FALSE;
15310 else if (audio.loops_available)
15312 setup.sound = setup.sound_loops = TRUE;
15314 SetAudioMode(setup.sound);
15318 case SOUND_CTRL_ID_SIMPLE:
15319 if (setup.sound_simple)
15320 setup.sound_simple = FALSE;
15321 else if (audio.sound_available)
15323 setup.sound = setup.sound_simple = TRUE;
15325 SetAudioMode(setup.sound);
15334 static void HandleGameButtons(struct GadgetInfo *gi)
15336 HandleGameButtonsExt(gi->custom_id, gi->event.button);
15339 void HandleSoundButtonKeys(Key key)
15342 if (key == setup.shortcut.sound_simple)
15343 ClickOnGadget(game_gadget[SOUND_CTRL_ID_SIMPLE], MB_LEFTBUTTON);
15344 else if (key == setup.shortcut.sound_loops)
15345 ClickOnGadget(game_gadget[SOUND_CTRL_ID_LOOPS], MB_LEFTBUTTON);
15346 else if (key == setup.shortcut.sound_music)
15347 ClickOnGadget(game_gadget[SOUND_CTRL_ID_MUSIC], MB_LEFTBUTTON);