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"
24 #define DEBUG_INIT_PLAYER 1
25 #define DEBUG_PLAYER_ACTIONS 0
27 /* EXPERIMENTAL STUFF */
28 #define USE_NEW_AMOEBA_CODE FALSE
30 /* EXPERIMENTAL STUFF */
31 #define USE_QUICKSAND_BD_ROCK_BUGFIX 0
32 #define USE_QUICKSAND_IMPACT_BUGFIX 0
33 #define USE_DELAYED_GFX_REDRAW 0
34 #define USE_NEW_PLAYER_ASSIGNMENTS 1
36 #if USE_DELAYED_GFX_REDRAW
37 #define TEST_DrawLevelField(x, y) \
38 GfxRedraw[x][y] |= GFX_REDRAW_TILE
39 #define TEST_DrawLevelFieldCrumbled(x, y) \
40 GfxRedraw[x][y] |= GFX_REDRAW_TILE_CRUMBLED
41 #define TEST_DrawLevelFieldCrumbledNeighbours(x, y) \
42 GfxRedraw[x][y] |= GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS
43 #define TEST_DrawTwinkleOnField(x, y) \
44 GfxRedraw[x][y] |= GFX_REDRAW_TILE_TWINKLED
46 #define TEST_DrawLevelField(x, y) \
48 #define TEST_DrawLevelFieldCrumbled(x, y) \
49 DrawLevelFieldCrumbled(x, y)
50 #define TEST_DrawLevelFieldCrumbledNeighbours(x, y) \
51 DrawLevelFieldCrumbledNeighbours(x, y)
52 #define TEST_DrawTwinkleOnField(x, y) \
53 DrawTwinkleOnField(x, y)
62 /* for MovePlayer() */
63 #define MP_NO_ACTION 0
66 #define MP_DONT_RUN_INTO (MP_MOVING | MP_ACTION)
68 /* for ScrollPlayer() */
70 #define SCROLL_GO_ON 1
72 /* for Bang()/Explode() */
73 #define EX_PHASE_START 0
74 #define EX_TYPE_NONE 0
75 #define EX_TYPE_NORMAL (1 << 0)
76 #define EX_TYPE_CENTER (1 << 1)
77 #define EX_TYPE_BORDER (1 << 2)
78 #define EX_TYPE_CROSS (1 << 3)
79 #define EX_TYPE_DYNA (1 << 4)
80 #define EX_TYPE_SINGLE_TILE (EX_TYPE_CENTER | EX_TYPE_BORDER)
82 #define PANEL_OFF() (local_player->LevelSolved_PanelOff)
83 #define PANEL_DEACTIVATED(p) ((p)->x < 0 || (p)->y < 0 || PANEL_OFF())
84 #define PANEL_XPOS(p) (DX + ALIGNED_TEXT_XPOS(p))
85 #define PANEL_YPOS(p) (DY + ALIGNED_TEXT_YPOS(p))
87 /* game panel display and control definitions */
88 #define GAME_PANEL_LEVEL_NUMBER 0
89 #define GAME_PANEL_GEMS 1
90 #define GAME_PANEL_INVENTORY_COUNT 2
91 #define GAME_PANEL_INVENTORY_FIRST_1 3
92 #define GAME_PANEL_INVENTORY_FIRST_2 4
93 #define GAME_PANEL_INVENTORY_FIRST_3 5
94 #define GAME_PANEL_INVENTORY_FIRST_4 6
95 #define GAME_PANEL_INVENTORY_FIRST_5 7
96 #define GAME_PANEL_INVENTORY_FIRST_6 8
97 #define GAME_PANEL_INVENTORY_FIRST_7 9
98 #define GAME_PANEL_INVENTORY_FIRST_8 10
99 #define GAME_PANEL_INVENTORY_LAST_1 11
100 #define GAME_PANEL_INVENTORY_LAST_2 12
101 #define GAME_PANEL_INVENTORY_LAST_3 13
102 #define GAME_PANEL_INVENTORY_LAST_4 14
103 #define GAME_PANEL_INVENTORY_LAST_5 15
104 #define GAME_PANEL_INVENTORY_LAST_6 16
105 #define GAME_PANEL_INVENTORY_LAST_7 17
106 #define GAME_PANEL_INVENTORY_LAST_8 18
107 #define GAME_PANEL_KEY_1 19
108 #define GAME_PANEL_KEY_2 20
109 #define GAME_PANEL_KEY_3 21
110 #define GAME_PANEL_KEY_4 22
111 #define GAME_PANEL_KEY_5 23
112 #define GAME_PANEL_KEY_6 24
113 #define GAME_PANEL_KEY_7 25
114 #define GAME_PANEL_KEY_8 26
115 #define GAME_PANEL_KEY_WHITE 27
116 #define GAME_PANEL_KEY_WHITE_COUNT 28
117 #define GAME_PANEL_SCORE 29
118 #define GAME_PANEL_HIGHSCORE 30
119 #define GAME_PANEL_TIME 31
120 #define GAME_PANEL_TIME_HH 32
121 #define GAME_PANEL_TIME_MM 33
122 #define GAME_PANEL_TIME_SS 34
123 #define GAME_PANEL_FRAME 35
124 #define GAME_PANEL_SHIELD_NORMAL 36
125 #define GAME_PANEL_SHIELD_NORMAL_TIME 37
126 #define GAME_PANEL_SHIELD_DEADLY 38
127 #define GAME_PANEL_SHIELD_DEADLY_TIME 39
128 #define GAME_PANEL_EXIT 40
129 #define GAME_PANEL_EMC_MAGIC_BALL 41
130 #define GAME_PANEL_EMC_MAGIC_BALL_SWITCH 42
131 #define GAME_PANEL_LIGHT_SWITCH 43
132 #define GAME_PANEL_LIGHT_SWITCH_TIME 44
133 #define GAME_PANEL_TIMEGATE_SWITCH 45
134 #define GAME_PANEL_TIMEGATE_SWITCH_TIME 46
135 #define GAME_PANEL_SWITCHGATE_SWITCH 47
136 #define GAME_PANEL_EMC_LENSES 48
137 #define GAME_PANEL_EMC_LENSES_TIME 49
138 #define GAME_PANEL_EMC_MAGNIFIER 50
139 #define GAME_PANEL_EMC_MAGNIFIER_TIME 51
140 #define GAME_PANEL_BALLOON_SWITCH 52
141 #define GAME_PANEL_DYNABOMB_NUMBER 53
142 #define GAME_PANEL_DYNABOMB_SIZE 54
143 #define GAME_PANEL_DYNABOMB_POWER 55
144 #define GAME_PANEL_PENGUINS 56
145 #define GAME_PANEL_SOKOBAN_OBJECTS 57
146 #define GAME_PANEL_SOKOBAN_FIELDS 58
147 #define GAME_PANEL_ROBOT_WHEEL 59
148 #define GAME_PANEL_CONVEYOR_BELT_1 60
149 #define GAME_PANEL_CONVEYOR_BELT_2 61
150 #define GAME_PANEL_CONVEYOR_BELT_3 62
151 #define GAME_PANEL_CONVEYOR_BELT_4 63
152 #define GAME_PANEL_CONVEYOR_BELT_1_SWITCH 64
153 #define GAME_PANEL_CONVEYOR_BELT_2_SWITCH 65
154 #define GAME_PANEL_CONVEYOR_BELT_3_SWITCH 66
155 #define GAME_PANEL_CONVEYOR_BELT_4_SWITCH 67
156 #define GAME_PANEL_MAGIC_WALL 68
157 #define GAME_PANEL_MAGIC_WALL_TIME 69
158 #define GAME_PANEL_GRAVITY_STATE 70
159 #define GAME_PANEL_GRAPHIC_1 71
160 #define GAME_PANEL_GRAPHIC_2 72
161 #define GAME_PANEL_GRAPHIC_3 73
162 #define GAME_PANEL_GRAPHIC_4 74
163 #define GAME_PANEL_GRAPHIC_5 75
164 #define GAME_PANEL_GRAPHIC_6 76
165 #define GAME_PANEL_GRAPHIC_7 77
166 #define GAME_PANEL_GRAPHIC_8 78
167 #define GAME_PANEL_ELEMENT_1 79
168 #define GAME_PANEL_ELEMENT_2 80
169 #define GAME_PANEL_ELEMENT_3 81
170 #define GAME_PANEL_ELEMENT_4 82
171 #define GAME_PANEL_ELEMENT_5 83
172 #define GAME_PANEL_ELEMENT_6 84
173 #define GAME_PANEL_ELEMENT_7 85
174 #define GAME_PANEL_ELEMENT_8 86
175 #define GAME_PANEL_ELEMENT_COUNT_1 87
176 #define GAME_PANEL_ELEMENT_COUNT_2 88
177 #define GAME_PANEL_ELEMENT_COUNT_3 89
178 #define GAME_PANEL_ELEMENT_COUNT_4 90
179 #define GAME_PANEL_ELEMENT_COUNT_5 91
180 #define GAME_PANEL_ELEMENT_COUNT_6 92
181 #define GAME_PANEL_ELEMENT_COUNT_7 93
182 #define GAME_PANEL_ELEMENT_COUNT_8 94
183 #define GAME_PANEL_CE_SCORE_1 95
184 #define GAME_PANEL_CE_SCORE_2 96
185 #define GAME_PANEL_CE_SCORE_3 97
186 #define GAME_PANEL_CE_SCORE_4 98
187 #define GAME_PANEL_CE_SCORE_5 99
188 #define GAME_PANEL_CE_SCORE_6 100
189 #define GAME_PANEL_CE_SCORE_7 101
190 #define GAME_PANEL_CE_SCORE_8 102
191 #define GAME_PANEL_CE_SCORE_1_ELEMENT 103
192 #define GAME_PANEL_CE_SCORE_2_ELEMENT 104
193 #define GAME_PANEL_CE_SCORE_3_ELEMENT 105
194 #define GAME_PANEL_CE_SCORE_4_ELEMENT 106
195 #define GAME_PANEL_CE_SCORE_5_ELEMENT 107
196 #define GAME_PANEL_CE_SCORE_6_ELEMENT 108
197 #define GAME_PANEL_CE_SCORE_7_ELEMENT 109
198 #define GAME_PANEL_CE_SCORE_8_ELEMENT 110
199 #define GAME_PANEL_PLAYER_NAME 111
200 #define GAME_PANEL_LEVEL_NAME 112
201 #define GAME_PANEL_LEVEL_AUTHOR 113
203 #define NUM_GAME_PANEL_CONTROLS 114
205 struct GamePanelOrderInfo
211 static struct GamePanelOrderInfo game_panel_order[NUM_GAME_PANEL_CONTROLS];
213 struct GamePanelControlInfo
217 struct TextPosInfo *pos;
220 int value, last_value;
221 int frame, last_frame;
226 static struct GamePanelControlInfo game_panel_controls[] =
229 GAME_PANEL_LEVEL_NUMBER,
230 &game.panel.level_number,
239 GAME_PANEL_INVENTORY_COUNT,
240 &game.panel.inventory_count,
244 GAME_PANEL_INVENTORY_FIRST_1,
245 &game.panel.inventory_first[0],
249 GAME_PANEL_INVENTORY_FIRST_2,
250 &game.panel.inventory_first[1],
254 GAME_PANEL_INVENTORY_FIRST_3,
255 &game.panel.inventory_first[2],
259 GAME_PANEL_INVENTORY_FIRST_4,
260 &game.panel.inventory_first[3],
264 GAME_PANEL_INVENTORY_FIRST_5,
265 &game.panel.inventory_first[4],
269 GAME_PANEL_INVENTORY_FIRST_6,
270 &game.panel.inventory_first[5],
274 GAME_PANEL_INVENTORY_FIRST_7,
275 &game.panel.inventory_first[6],
279 GAME_PANEL_INVENTORY_FIRST_8,
280 &game.panel.inventory_first[7],
284 GAME_PANEL_INVENTORY_LAST_1,
285 &game.panel.inventory_last[0],
289 GAME_PANEL_INVENTORY_LAST_2,
290 &game.panel.inventory_last[1],
294 GAME_PANEL_INVENTORY_LAST_3,
295 &game.panel.inventory_last[2],
299 GAME_PANEL_INVENTORY_LAST_4,
300 &game.panel.inventory_last[3],
304 GAME_PANEL_INVENTORY_LAST_5,
305 &game.panel.inventory_last[4],
309 GAME_PANEL_INVENTORY_LAST_6,
310 &game.panel.inventory_last[5],
314 GAME_PANEL_INVENTORY_LAST_7,
315 &game.panel.inventory_last[6],
319 GAME_PANEL_INVENTORY_LAST_8,
320 &game.panel.inventory_last[7],
364 GAME_PANEL_KEY_WHITE,
365 &game.panel.key_white,
369 GAME_PANEL_KEY_WHITE_COUNT,
370 &game.panel.key_white_count,
379 GAME_PANEL_HIGHSCORE,
380 &game.panel.highscore,
409 GAME_PANEL_SHIELD_NORMAL,
410 &game.panel.shield_normal,
414 GAME_PANEL_SHIELD_NORMAL_TIME,
415 &game.panel.shield_normal_time,
419 GAME_PANEL_SHIELD_DEADLY,
420 &game.panel.shield_deadly,
424 GAME_PANEL_SHIELD_DEADLY_TIME,
425 &game.panel.shield_deadly_time,
434 GAME_PANEL_EMC_MAGIC_BALL,
435 &game.panel.emc_magic_ball,
439 GAME_PANEL_EMC_MAGIC_BALL_SWITCH,
440 &game.panel.emc_magic_ball_switch,
444 GAME_PANEL_LIGHT_SWITCH,
445 &game.panel.light_switch,
449 GAME_PANEL_LIGHT_SWITCH_TIME,
450 &game.panel.light_switch_time,
454 GAME_PANEL_TIMEGATE_SWITCH,
455 &game.panel.timegate_switch,
459 GAME_PANEL_TIMEGATE_SWITCH_TIME,
460 &game.panel.timegate_switch_time,
464 GAME_PANEL_SWITCHGATE_SWITCH,
465 &game.panel.switchgate_switch,
469 GAME_PANEL_EMC_LENSES,
470 &game.panel.emc_lenses,
474 GAME_PANEL_EMC_LENSES_TIME,
475 &game.panel.emc_lenses_time,
479 GAME_PANEL_EMC_MAGNIFIER,
480 &game.panel.emc_magnifier,
484 GAME_PANEL_EMC_MAGNIFIER_TIME,
485 &game.panel.emc_magnifier_time,
489 GAME_PANEL_BALLOON_SWITCH,
490 &game.panel.balloon_switch,
494 GAME_PANEL_DYNABOMB_NUMBER,
495 &game.panel.dynabomb_number,
499 GAME_PANEL_DYNABOMB_SIZE,
500 &game.panel.dynabomb_size,
504 GAME_PANEL_DYNABOMB_POWER,
505 &game.panel.dynabomb_power,
510 &game.panel.penguins,
514 GAME_PANEL_SOKOBAN_OBJECTS,
515 &game.panel.sokoban_objects,
519 GAME_PANEL_SOKOBAN_FIELDS,
520 &game.panel.sokoban_fields,
524 GAME_PANEL_ROBOT_WHEEL,
525 &game.panel.robot_wheel,
529 GAME_PANEL_CONVEYOR_BELT_1,
530 &game.panel.conveyor_belt[0],
534 GAME_PANEL_CONVEYOR_BELT_2,
535 &game.panel.conveyor_belt[1],
539 GAME_PANEL_CONVEYOR_BELT_3,
540 &game.panel.conveyor_belt[2],
544 GAME_PANEL_CONVEYOR_BELT_4,
545 &game.panel.conveyor_belt[3],
549 GAME_PANEL_CONVEYOR_BELT_1_SWITCH,
550 &game.panel.conveyor_belt_switch[0],
554 GAME_PANEL_CONVEYOR_BELT_2_SWITCH,
555 &game.panel.conveyor_belt_switch[1],
559 GAME_PANEL_CONVEYOR_BELT_3_SWITCH,
560 &game.panel.conveyor_belt_switch[2],
564 GAME_PANEL_CONVEYOR_BELT_4_SWITCH,
565 &game.panel.conveyor_belt_switch[3],
569 GAME_PANEL_MAGIC_WALL,
570 &game.panel.magic_wall,
574 GAME_PANEL_MAGIC_WALL_TIME,
575 &game.panel.magic_wall_time,
579 GAME_PANEL_GRAVITY_STATE,
580 &game.panel.gravity_state,
584 GAME_PANEL_GRAPHIC_1,
585 &game.panel.graphic[0],
589 GAME_PANEL_GRAPHIC_2,
590 &game.panel.graphic[1],
594 GAME_PANEL_GRAPHIC_3,
595 &game.panel.graphic[2],
599 GAME_PANEL_GRAPHIC_4,
600 &game.panel.graphic[3],
604 GAME_PANEL_GRAPHIC_5,
605 &game.panel.graphic[4],
609 GAME_PANEL_GRAPHIC_6,
610 &game.panel.graphic[5],
614 GAME_PANEL_GRAPHIC_7,
615 &game.panel.graphic[6],
619 GAME_PANEL_GRAPHIC_8,
620 &game.panel.graphic[7],
624 GAME_PANEL_ELEMENT_1,
625 &game.panel.element[0],
629 GAME_PANEL_ELEMENT_2,
630 &game.panel.element[1],
634 GAME_PANEL_ELEMENT_3,
635 &game.panel.element[2],
639 GAME_PANEL_ELEMENT_4,
640 &game.panel.element[3],
644 GAME_PANEL_ELEMENT_5,
645 &game.panel.element[4],
649 GAME_PANEL_ELEMENT_6,
650 &game.panel.element[5],
654 GAME_PANEL_ELEMENT_7,
655 &game.panel.element[6],
659 GAME_PANEL_ELEMENT_8,
660 &game.panel.element[7],
664 GAME_PANEL_ELEMENT_COUNT_1,
665 &game.panel.element_count[0],
669 GAME_PANEL_ELEMENT_COUNT_2,
670 &game.panel.element_count[1],
674 GAME_PANEL_ELEMENT_COUNT_3,
675 &game.panel.element_count[2],
679 GAME_PANEL_ELEMENT_COUNT_4,
680 &game.panel.element_count[3],
684 GAME_PANEL_ELEMENT_COUNT_5,
685 &game.panel.element_count[4],
689 GAME_PANEL_ELEMENT_COUNT_6,
690 &game.panel.element_count[5],
694 GAME_PANEL_ELEMENT_COUNT_7,
695 &game.panel.element_count[6],
699 GAME_PANEL_ELEMENT_COUNT_8,
700 &game.panel.element_count[7],
704 GAME_PANEL_CE_SCORE_1,
705 &game.panel.ce_score[0],
709 GAME_PANEL_CE_SCORE_2,
710 &game.panel.ce_score[1],
714 GAME_PANEL_CE_SCORE_3,
715 &game.panel.ce_score[2],
719 GAME_PANEL_CE_SCORE_4,
720 &game.panel.ce_score[3],
724 GAME_PANEL_CE_SCORE_5,
725 &game.panel.ce_score[4],
729 GAME_PANEL_CE_SCORE_6,
730 &game.panel.ce_score[5],
734 GAME_PANEL_CE_SCORE_7,
735 &game.panel.ce_score[6],
739 GAME_PANEL_CE_SCORE_8,
740 &game.panel.ce_score[7],
744 GAME_PANEL_CE_SCORE_1_ELEMENT,
745 &game.panel.ce_score_element[0],
749 GAME_PANEL_CE_SCORE_2_ELEMENT,
750 &game.panel.ce_score_element[1],
754 GAME_PANEL_CE_SCORE_3_ELEMENT,
755 &game.panel.ce_score_element[2],
759 GAME_PANEL_CE_SCORE_4_ELEMENT,
760 &game.panel.ce_score_element[3],
764 GAME_PANEL_CE_SCORE_5_ELEMENT,
765 &game.panel.ce_score_element[4],
769 GAME_PANEL_CE_SCORE_6_ELEMENT,
770 &game.panel.ce_score_element[5],
774 GAME_PANEL_CE_SCORE_7_ELEMENT,
775 &game.panel.ce_score_element[6],
779 GAME_PANEL_CE_SCORE_8_ELEMENT,
780 &game.panel.ce_score_element[7],
784 GAME_PANEL_PLAYER_NAME,
785 &game.panel.player_name,
789 GAME_PANEL_LEVEL_NAME,
790 &game.panel.level_name,
794 GAME_PANEL_LEVEL_AUTHOR,
795 &game.panel.level_author,
806 /* values for delayed check of falling and moving elements and for collision */
807 #define CHECK_DELAY_MOVING 3
808 #define CHECK_DELAY_FALLING CHECK_DELAY_MOVING
809 #define CHECK_DELAY_COLLISION 2
810 #define CHECK_DELAY_IMPACT CHECK_DELAY_COLLISION
812 /* values for initial player move delay (initial delay counter value) */
813 #define INITIAL_MOVE_DELAY_OFF -1
814 #define INITIAL_MOVE_DELAY_ON 0
816 /* values for player movement speed (which is in fact a delay value) */
817 #define MOVE_DELAY_MIN_SPEED 32
818 #define MOVE_DELAY_NORMAL_SPEED 8
819 #define MOVE_DELAY_HIGH_SPEED 4
820 #define MOVE_DELAY_MAX_SPEED 1
822 #define DOUBLE_MOVE_DELAY(x) (x = (x < MOVE_DELAY_MIN_SPEED ? x * 2 : x))
823 #define HALVE_MOVE_DELAY(x) (x = (x > MOVE_DELAY_MAX_SPEED ? x / 2 : x))
825 #define DOUBLE_PLAYER_SPEED(p) (HALVE_MOVE_DELAY( (p)->move_delay_value))
826 #define HALVE_PLAYER_SPEED(p) (DOUBLE_MOVE_DELAY((p)->move_delay_value))
828 /* values for other actions */
829 #define MOVE_STEPSIZE_NORMAL (TILEX / MOVE_DELAY_NORMAL_SPEED)
830 #define MOVE_STEPSIZE_MIN (1)
831 #define MOVE_STEPSIZE_MAX (TILEX)
833 #define GET_DX_FROM_DIR(d) ((d) == MV_LEFT ? -1 : (d) == MV_RIGHT ? 1 : 0)
834 #define GET_DY_FROM_DIR(d) ((d) == MV_UP ? -1 : (d) == MV_DOWN ? 1 : 0)
836 #define INIT_GFX_RANDOM() (GetSimpleRandom(1000000))
838 #define GET_NEW_PUSH_DELAY(e) ( (element_info[e].push_delay_fixed) + \
839 RND(element_info[e].push_delay_random))
840 #define GET_NEW_DROP_DELAY(e) ( (element_info[e].drop_delay_fixed) + \
841 RND(element_info[e].drop_delay_random))
842 #define GET_NEW_MOVE_DELAY(e) ( (element_info[e].move_delay_fixed) + \
843 RND(element_info[e].move_delay_random))
844 #define GET_MAX_MOVE_DELAY(e) ( (element_info[e].move_delay_fixed) + \
845 (element_info[e].move_delay_random))
846 #define GET_NEW_CE_VALUE(e) ( (element_info[e].ce_value_fixed_initial) +\
847 RND(element_info[e].ce_value_random_initial))
848 #define GET_CE_SCORE(e) ( (element_info[e].collect_score))
849 #define GET_CHANGE_DELAY(c) ( ((c)->delay_fixed * (c)->delay_frames) + \
850 RND((c)->delay_random * (c)->delay_frames))
851 #define GET_CE_DELAY_VALUE(c) ( ((c)->delay_fixed) + \
852 RND((c)->delay_random))
855 #define GET_VALID_RUNTIME_ELEMENT(e) \
856 ((e) >= NUM_RUNTIME_ELEMENTS ? EL_UNKNOWN : (e))
858 #define RESOLVED_REFERENCE_ELEMENT(be, e) \
859 ((be) + (e) - EL_SELF < EL_CUSTOM_START ? EL_CUSTOM_START : \
860 (be) + (e) - EL_SELF > EL_CUSTOM_END ? EL_CUSTOM_END : \
861 (be) + (e) - EL_SELF)
863 #define GET_PLAYER_FROM_BITS(p) \
864 (EL_PLAYER_1 + ((p) != PLAYER_BITS_ANY ? log_2(p) : 0))
866 #define GET_TARGET_ELEMENT(be, e, ch, cv, cs) \
867 ((e) == EL_TRIGGER_PLAYER ? (ch)->actual_trigger_player : \
868 (e) == EL_TRIGGER_ELEMENT ? (ch)->actual_trigger_element : \
869 (e) == EL_TRIGGER_CE_VALUE ? (ch)->actual_trigger_ce_value : \
870 (e) == EL_TRIGGER_CE_SCORE ? (ch)->actual_trigger_ce_score : \
871 (e) == EL_CURRENT_CE_VALUE ? (cv) : \
872 (e) == EL_CURRENT_CE_SCORE ? (cs) : \
873 (e) >= EL_PREV_CE_8 && (e) <= EL_NEXT_CE_8 ? \
874 RESOLVED_REFERENCE_ELEMENT(be, e) : \
877 #define CAN_GROW_INTO(e) \
878 ((e) == EL_SAND || (IS_DIGGABLE(e) && level.grow_into_diggable))
880 #define ELEMENT_CAN_ENTER_FIELD_BASE_X(x, y, condition) \
881 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
884 #define ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, condition) \
885 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
886 (CAN_MOVE_INTO_ACID(e) && \
887 Feld[x][y] == EL_ACID) || \
890 #define ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, condition) \
891 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) || \
892 (CAN_MOVE_INTO_ACID(e) && \
893 Feld[x][y] == EL_ACID) || \
896 #define ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, condition) \
897 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
899 (CAN_MOVE_INTO_ACID(e) && \
900 Feld[x][y] == EL_ACID) || \
901 (DONT_COLLIDE_WITH(e) && \
903 !PLAYER_ENEMY_PROTECTED(x, y))))
905 #define ELEMENT_CAN_ENTER_FIELD(e, x, y) \
906 ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, 0)
908 #define SATELLITE_CAN_ENTER_FIELD(x, y) \
909 ELEMENT_CAN_ENTER_FIELD_BASE_2(EL_SATELLITE, x, y, 0)
911 #define ANDROID_CAN_ENTER_FIELD(e, x, y) \
912 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, Feld[x][y] == EL_EMC_PLANT)
914 #define ANDROID_CAN_CLONE_FIELD(x, y) \
915 (IN_LEV_FIELD(x, y) && (CAN_BE_CLONED_BY_ANDROID(Feld[x][y]) || \
916 CAN_BE_CLONED_BY_ANDROID(EL_TRIGGER_ELEMENT)))
918 #define ENEMY_CAN_ENTER_FIELD(e, x, y) \
919 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
921 #define YAMYAM_CAN_ENTER_FIELD(e, x, y) \
922 ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, Feld[x][y] == EL_DIAMOND)
924 #define DARK_YAMYAM_CAN_ENTER_FIELD(e, x, y) \
925 ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x,y, IS_FOOD_DARK_YAMYAM(Feld[x][y]))
927 #define PACMAN_CAN_ENTER_FIELD(e, x, y) \
928 ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, IS_AMOEBOID(Feld[x][y]))
930 #define PIG_CAN_ENTER_FIELD(e, x, y) \
931 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, IS_FOOD_PIG(Feld[x][y]))
933 #define PENGUIN_CAN_ENTER_FIELD(e, x, y) \
934 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (Feld[x][y] == EL_EXIT_OPEN || \
935 Feld[x][y] == EL_EM_EXIT_OPEN || \
936 Feld[x][y] == EL_STEEL_EXIT_OPEN || \
937 Feld[x][y] == EL_EM_STEEL_EXIT_OPEN || \
938 IS_FOOD_PENGUIN(Feld[x][y])))
939 #define DRAGON_CAN_ENTER_FIELD(e, x, y) \
940 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
942 #define MOLE_CAN_ENTER_FIELD(e, x, y, condition) \
943 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (condition))
945 #define SPRING_CAN_ENTER_FIELD(e, x, y) \
946 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
948 #define SPRING_CAN_BUMP_FROM_FIELD(x, y) \
949 (IN_LEV_FIELD(x, y) && (Feld[x][y] == EL_EMC_SPRING_BUMPER || \
950 Feld[x][y] == EL_EMC_SPRING_BUMPER_ACTIVE))
952 #define MOVE_ENTER_EL(e) (element_info[e].move_enter_element)
954 #define CE_ENTER_FIELD_COND(e, x, y) \
955 (!IS_PLAYER(x, y) && \
956 IS_EQUAL_OR_IN_GROUP(Feld[x][y], MOVE_ENTER_EL(e)))
958 #define CUSTOM_ELEMENT_CAN_ENTER_FIELD(e, x, y) \
959 ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, CE_ENTER_FIELD_COND(e, x, y))
961 #define IN_LEV_FIELD_AND_IS_FREE(x, y) (IN_LEV_FIELD(x, y) && IS_FREE(x, y))
962 #define IN_LEV_FIELD_AND_NOT_FREE(x, y) (IN_LEV_FIELD(x, y) && !IS_FREE(x, y))
964 #define ACCESS_FROM(e, d) (element_info[e].access_direction &(d))
965 #define IS_WALKABLE_FROM(e, d) (IS_WALKABLE(e) && ACCESS_FROM(e, d))
966 #define IS_PASSABLE_FROM(e, d) (IS_PASSABLE(e) && ACCESS_FROM(e, d))
967 #define IS_ACCESSIBLE_FROM(e, d) (IS_ACCESSIBLE(e) && ACCESS_FROM(e, d))
969 /* game button identifiers */
970 #define GAME_CTRL_ID_STOP 0
971 #define GAME_CTRL_ID_PAUSE 1
972 #define GAME_CTRL_ID_PLAY 2
973 #define SOUND_CTRL_ID_MUSIC 3
974 #define SOUND_CTRL_ID_LOOPS 4
975 #define SOUND_CTRL_ID_SIMPLE 5
976 #define GAME_CTRL_ID_SAVE 6
977 #define GAME_CTRL_ID_LOAD 7
979 #define NUM_GAME_BUTTONS 8
982 /* forward declaration for internal use */
984 static void CreateField(int, int, int);
986 static void ResetGfxAnimation(int, int);
988 static void SetPlayerWaiting(struct PlayerInfo *, boolean);
989 static void AdvanceFrameAndPlayerCounters(int);
991 static boolean MovePlayerOneStep(struct PlayerInfo *, int, int, int, int);
992 static boolean MovePlayer(struct PlayerInfo *, int, int);
993 static void ScrollPlayer(struct PlayerInfo *, int);
994 static void ScrollScreen(struct PlayerInfo *, int);
996 static int DigField(struct PlayerInfo *, int, int, int, int, int, int, int);
997 static boolean DigFieldByCE(int, int, int);
998 static boolean SnapField(struct PlayerInfo *, int, int);
999 static boolean DropElement(struct PlayerInfo *);
1001 static void InitBeltMovement(void);
1002 static void CloseAllOpenTimegates(void);
1003 static void CheckGravityMovement(struct PlayerInfo *);
1004 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *);
1005 static void KillPlayerUnlessEnemyProtected(int, int);
1006 static void KillPlayerUnlessExplosionProtected(int, int);
1008 static void TestIfPlayerTouchesCustomElement(int, int);
1009 static void TestIfElementTouchesCustomElement(int, int);
1010 static void TestIfElementHitsCustomElement(int, int, int);
1012 static void HandleElementChange(int, int, int);
1013 static void ExecuteCustomElementAction(int, int, int, int);
1014 static boolean ChangeElement(int, int, int, int);
1016 static boolean CheckTriggeredElementChangeExt(int, int, int, int, int,int,int);
1017 #define CheckTriggeredElementChange(x, y, e, ev) \
1018 CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY,CH_SIDE_ANY, -1)
1019 #define CheckTriggeredElementChangeByPlayer(x, y, e, ev, p, s) \
1020 CheckTriggeredElementChangeExt(x, y, e, ev, p, s, -1)
1021 #define CheckTriggeredElementChangeBySide(x, y, e, ev, s) \
1022 CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, s, -1)
1023 #define CheckTriggeredElementChangeByPage(x, y, e, ev, p) \
1024 CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY, CH_SIDE_ANY, p)
1026 static boolean CheckElementChangeExt(int, int, int, int, int, int, int);
1027 #define CheckElementChange(x, y, e, te, ev) \
1028 CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, CH_SIDE_ANY)
1029 #define CheckElementChangeByPlayer(x, y, e, ev, p, s) \
1030 CheckElementChangeExt(x, y, e, EL_EMPTY, ev, p, s)
1031 #define CheckElementChangeBySide(x, y, e, te, ev, s) \
1032 CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, s)
1034 static void PlayLevelSound(int, int, int);
1035 static void PlayLevelSoundNearest(int, int, int);
1036 static void PlayLevelSoundAction(int, int, int);
1037 static void PlayLevelSoundElementAction(int, int, int, int);
1038 static void PlayLevelSoundElementActionIfLoop(int, int, int, int);
1039 static void PlayLevelSoundActionIfLoop(int, int, int);
1040 static void StopLevelSoundActionIfLoop(int, int, int);
1041 static void PlayLevelMusic();
1043 static void HandleGameButtons(struct GadgetInfo *);
1045 int AmoebeNachbarNr(int, int);
1046 void AmoebeUmwandeln(int, int);
1047 void ContinueMoving(int, int);
1048 void Bang(int, int);
1049 void InitMovDir(int, int);
1050 void InitAmoebaNr(int, int);
1051 int NewHiScore(void);
1053 void TestIfGoodThingHitsBadThing(int, int, int);
1054 void TestIfBadThingHitsGoodThing(int, int, int);
1055 void TestIfPlayerTouchesBadThing(int, int);
1056 void TestIfPlayerRunsIntoBadThing(int, int, int);
1057 void TestIfBadThingTouchesPlayer(int, int);
1058 void TestIfBadThingRunsIntoPlayer(int, int, int);
1059 void TestIfFriendTouchesBadThing(int, int);
1060 void TestIfBadThingTouchesFriend(int, int);
1061 void TestIfBadThingTouchesOtherBadThing(int, int);
1062 void TestIfGoodThingGetsHitByBadThing(int, int, int);
1064 void KillPlayer(struct PlayerInfo *);
1065 void BuryPlayer(struct PlayerInfo *);
1066 void RemovePlayer(struct PlayerInfo *);
1068 static int getInvisibleActiveFromInvisibleElement(int);
1069 static int getInvisibleFromInvisibleActiveElement(int);
1071 static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
1073 /* for detection of endless loops, caused by custom element programming */
1074 /* (using maximal playfield width x 10 is just a rough approximation) */
1075 #define MAX_ELEMENT_CHANGE_RECURSION_DEPTH (MAX_PLAYFIELD_WIDTH * 10)
1077 #define RECURSION_LOOP_DETECTION_START(e, rc) \
1079 if (recursion_loop_detected) \
1082 if (recursion_loop_depth > MAX_ELEMENT_CHANGE_RECURSION_DEPTH) \
1084 recursion_loop_detected = TRUE; \
1085 recursion_loop_element = (e); \
1088 recursion_loop_depth++; \
1091 #define RECURSION_LOOP_DETECTION_END() \
1093 recursion_loop_depth--; \
1096 static int recursion_loop_depth;
1097 static boolean recursion_loop_detected;
1098 static boolean recursion_loop_element;
1100 static int map_player_action[MAX_PLAYERS];
1103 /* ------------------------------------------------------------------------- */
1104 /* definition of elements that automatically change to other elements after */
1105 /* a specified time, eventually calling a function when changing */
1106 /* ------------------------------------------------------------------------- */
1108 /* forward declaration for changer functions */
1109 static void InitBuggyBase(int, int);
1110 static void WarnBuggyBase(int, int);
1112 static void InitTrap(int, int);
1113 static void ActivateTrap(int, int);
1114 static void ChangeActiveTrap(int, int);
1116 static void InitRobotWheel(int, int);
1117 static void RunRobotWheel(int, int);
1118 static void StopRobotWheel(int, int);
1120 static void InitTimegateWheel(int, int);
1121 static void RunTimegateWheel(int, int);
1123 static void InitMagicBallDelay(int, int);
1124 static void ActivateMagicBall(int, int);
1126 struct ChangingElementInfo
1131 void (*pre_change_function)(int x, int y);
1132 void (*change_function)(int x, int y);
1133 void (*post_change_function)(int x, int y);
1136 static struct ChangingElementInfo change_delay_list[] =
1171 EL_STEEL_EXIT_OPENING,
1179 EL_STEEL_EXIT_CLOSING,
1180 EL_STEEL_EXIT_CLOSED,
1203 EL_EM_STEEL_EXIT_OPENING,
1204 EL_EM_STEEL_EXIT_OPEN,
1211 EL_EM_STEEL_EXIT_CLOSING,
1235 EL_SWITCHGATE_OPENING,
1243 EL_SWITCHGATE_CLOSING,
1244 EL_SWITCHGATE_CLOSED,
1251 EL_TIMEGATE_OPENING,
1259 EL_TIMEGATE_CLOSING,
1268 EL_ACID_SPLASH_LEFT,
1276 EL_ACID_SPLASH_RIGHT,
1285 EL_SP_BUGGY_BASE_ACTIVATING,
1292 EL_SP_BUGGY_BASE_ACTIVATING,
1293 EL_SP_BUGGY_BASE_ACTIVE,
1300 EL_SP_BUGGY_BASE_ACTIVE,
1324 EL_ROBOT_WHEEL_ACTIVE,
1332 EL_TIMEGATE_SWITCH_ACTIVE,
1340 EL_DC_TIMEGATE_SWITCH_ACTIVE,
1341 EL_DC_TIMEGATE_SWITCH,
1348 EL_EMC_MAGIC_BALL_ACTIVE,
1349 EL_EMC_MAGIC_BALL_ACTIVE,
1356 EL_EMC_SPRING_BUMPER_ACTIVE,
1357 EL_EMC_SPRING_BUMPER,
1364 EL_DIAGONAL_SHRINKING,
1372 EL_DIAGONAL_GROWING,
1393 int push_delay_fixed, push_delay_random;
1397 { EL_SPRING, 0, 0 },
1398 { EL_BALLOON, 0, 0 },
1400 { EL_SOKOBAN_OBJECT, 2, 0 },
1401 { EL_SOKOBAN_FIELD_FULL, 2, 0 },
1402 { EL_SATELLITE, 2, 0 },
1403 { EL_SP_DISK_YELLOW, 2, 0 },
1405 { EL_UNDEFINED, 0, 0 },
1413 move_stepsize_list[] =
1415 { EL_AMOEBA_DROP, 2 },
1416 { EL_AMOEBA_DROPPING, 2 },
1417 { EL_QUICKSAND_FILLING, 1 },
1418 { EL_QUICKSAND_EMPTYING, 1 },
1419 { EL_QUICKSAND_FAST_FILLING, 2 },
1420 { EL_QUICKSAND_FAST_EMPTYING, 2 },
1421 { EL_MAGIC_WALL_FILLING, 2 },
1422 { EL_MAGIC_WALL_EMPTYING, 2 },
1423 { EL_BD_MAGIC_WALL_FILLING, 2 },
1424 { EL_BD_MAGIC_WALL_EMPTYING, 2 },
1425 { EL_DC_MAGIC_WALL_FILLING, 2 },
1426 { EL_DC_MAGIC_WALL_EMPTYING, 2 },
1428 { EL_UNDEFINED, 0 },
1436 collect_count_list[] =
1439 { EL_BD_DIAMOND, 1 },
1440 { EL_EMERALD_YELLOW, 1 },
1441 { EL_EMERALD_RED, 1 },
1442 { EL_EMERALD_PURPLE, 1 },
1444 { EL_SP_INFOTRON, 1 },
1448 { EL_UNDEFINED, 0 },
1456 access_direction_list[] =
1458 { EL_TUBE_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1459 { EL_TUBE_VERTICAL, MV_UP | MV_DOWN },
1460 { EL_TUBE_HORIZONTAL, MV_LEFT | MV_RIGHT },
1461 { EL_TUBE_VERTICAL_LEFT, MV_LEFT | MV_UP | MV_DOWN },
1462 { EL_TUBE_VERTICAL_RIGHT, MV_RIGHT | MV_UP | MV_DOWN },
1463 { EL_TUBE_HORIZONTAL_UP, MV_LEFT | MV_RIGHT | MV_UP },
1464 { EL_TUBE_HORIZONTAL_DOWN, MV_LEFT | MV_RIGHT | MV_DOWN },
1465 { EL_TUBE_LEFT_UP, MV_LEFT | MV_UP },
1466 { EL_TUBE_LEFT_DOWN, MV_LEFT | MV_DOWN },
1467 { EL_TUBE_RIGHT_UP, MV_RIGHT | MV_UP },
1468 { EL_TUBE_RIGHT_DOWN, MV_RIGHT | MV_DOWN },
1470 { EL_SP_PORT_LEFT, MV_RIGHT },
1471 { EL_SP_PORT_RIGHT, MV_LEFT },
1472 { EL_SP_PORT_UP, MV_DOWN },
1473 { EL_SP_PORT_DOWN, MV_UP },
1474 { EL_SP_PORT_HORIZONTAL, MV_LEFT | MV_RIGHT },
1475 { EL_SP_PORT_VERTICAL, MV_UP | MV_DOWN },
1476 { EL_SP_PORT_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1477 { EL_SP_GRAVITY_PORT_LEFT, MV_RIGHT },
1478 { EL_SP_GRAVITY_PORT_RIGHT, MV_LEFT },
1479 { EL_SP_GRAVITY_PORT_UP, MV_DOWN },
1480 { EL_SP_GRAVITY_PORT_DOWN, MV_UP },
1481 { EL_SP_GRAVITY_ON_PORT_LEFT, MV_RIGHT },
1482 { EL_SP_GRAVITY_ON_PORT_RIGHT, MV_LEFT },
1483 { EL_SP_GRAVITY_ON_PORT_UP, MV_DOWN },
1484 { EL_SP_GRAVITY_ON_PORT_DOWN, MV_UP },
1485 { EL_SP_GRAVITY_OFF_PORT_LEFT, MV_RIGHT },
1486 { EL_SP_GRAVITY_OFF_PORT_RIGHT, MV_LEFT },
1487 { EL_SP_GRAVITY_OFF_PORT_UP, MV_DOWN },
1488 { EL_SP_GRAVITY_OFF_PORT_DOWN, MV_UP },
1490 { EL_UNDEFINED, MV_NONE }
1493 static boolean trigger_events[MAX_NUM_ELEMENTS][NUM_CHANGE_EVENTS];
1495 #define IS_AUTO_CHANGING(e) (element_info[e].has_change_event[CE_DELAY])
1496 #define IS_JUST_CHANGING(x, y) (ChangeDelay[x][y] != 0)
1497 #define IS_CHANGING(x, y) (IS_AUTO_CHANGING(Feld[x][y]) || \
1498 IS_JUST_CHANGING(x, y))
1500 #define CE_PAGE(e, ce) (element_info[e].event_page[ce])
1502 /* static variables for playfield scan mode (scanning forward or backward) */
1503 static int playfield_scan_start_x = 0;
1504 static int playfield_scan_start_y = 0;
1505 static int playfield_scan_delta_x = 1;
1506 static int playfield_scan_delta_y = 1;
1508 #define SCAN_PLAYFIELD(x, y) for ((y) = playfield_scan_start_y; \
1509 (y) >= 0 && (y) <= lev_fieldy - 1; \
1510 (y) += playfield_scan_delta_y) \
1511 for ((x) = playfield_scan_start_x; \
1512 (x) >= 0 && (x) <= lev_fieldx - 1; \
1513 (x) += playfield_scan_delta_x)
1516 void DEBUG_SetMaximumDynamite()
1520 for (i = 0; i < MAX_INVENTORY_SIZE; i++)
1521 if (local_player->inventory_size < MAX_INVENTORY_SIZE)
1522 local_player->inventory_element[local_player->inventory_size++] =
1527 static void InitPlayfieldScanModeVars()
1529 if (game.use_reverse_scan_direction)
1531 playfield_scan_start_x = lev_fieldx - 1;
1532 playfield_scan_start_y = lev_fieldy - 1;
1534 playfield_scan_delta_x = -1;
1535 playfield_scan_delta_y = -1;
1539 playfield_scan_start_x = 0;
1540 playfield_scan_start_y = 0;
1542 playfield_scan_delta_x = 1;
1543 playfield_scan_delta_y = 1;
1547 static void InitPlayfieldScanMode(int mode)
1549 game.use_reverse_scan_direction =
1550 (mode == CA_ARG_SCAN_MODE_REVERSE ? TRUE : FALSE);
1552 InitPlayfieldScanModeVars();
1555 static int get_move_delay_from_stepsize(int move_stepsize)
1558 MIN(MAX(MOVE_STEPSIZE_MIN, move_stepsize), MOVE_STEPSIZE_MAX);
1560 /* make sure that stepsize value is always a power of 2 */
1561 move_stepsize = (1 << log_2(move_stepsize));
1563 return TILEX / move_stepsize;
1566 static void SetPlayerMoveSpeed(struct PlayerInfo *player, int move_stepsize,
1569 int player_nr = player->index_nr;
1570 int move_delay = get_move_delay_from_stepsize(move_stepsize);
1571 boolean cannot_move = (move_stepsize == STEPSIZE_NOT_MOVING ? TRUE : FALSE);
1573 /* do no immediately change move delay -- the player might just be moving */
1574 player->move_delay_value_next = move_delay;
1576 /* information if player can move must be set separately */
1577 player->cannot_move = cannot_move;
1581 player->move_delay = game.initial_move_delay[player_nr];
1582 player->move_delay_value = game.initial_move_delay_value[player_nr];
1584 player->move_delay_value_next = -1;
1586 player->move_delay_reset_counter = 0;
1590 void GetPlayerConfig()
1592 GameFrameDelay = setup.game_frame_delay;
1594 if (!audio.sound_available)
1595 setup.sound_simple = FALSE;
1597 if (!audio.loops_available)
1598 setup.sound_loops = FALSE;
1600 if (!audio.music_available)
1601 setup.sound_music = FALSE;
1603 if (!video.fullscreen_available)
1604 setup.fullscreen = FALSE;
1606 setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
1608 SetAudioMode(setup.sound);
1612 int GetElementFromGroupElement(int element)
1614 if (IS_GROUP_ELEMENT(element))
1616 struct ElementGroupInfo *group = element_info[element].group;
1617 int last_anim_random_frame = gfx.anim_random_frame;
1620 if (group->choice_mode == ANIM_RANDOM)
1621 gfx.anim_random_frame = RND(group->num_elements_resolved);
1623 element_pos = getAnimationFrame(group->num_elements_resolved, 1,
1624 group->choice_mode, 0,
1627 if (group->choice_mode == ANIM_RANDOM)
1628 gfx.anim_random_frame = last_anim_random_frame;
1630 group->choice_pos++;
1632 element = group->element_resolved[element_pos];
1638 static void InitPlayerField(int x, int y, int element, boolean init_game)
1640 if (element == EL_SP_MURPHY)
1644 if (stored_player[0].present)
1646 Feld[x][y] = EL_SP_MURPHY_CLONE;
1652 stored_player[0].initial_element = element;
1653 stored_player[0].use_murphy = TRUE;
1655 if (!level.use_artwork_element[0])
1656 stored_player[0].artwork_element = EL_SP_MURPHY;
1659 Feld[x][y] = EL_PLAYER_1;
1665 struct PlayerInfo *player = &stored_player[Feld[x][y] - EL_PLAYER_1];
1666 int jx = player->jx, jy = player->jy;
1668 player->present = TRUE;
1670 player->block_last_field = (element == EL_SP_MURPHY ?
1671 level.sp_block_last_field :
1672 level.block_last_field);
1674 /* ---------- initialize player's last field block delay --------------- */
1676 /* always start with reliable default value (no adjustment needed) */
1677 player->block_delay_adjustment = 0;
1679 /* special case 1: in Supaplex, Murphy blocks last field one more frame */
1680 if (player->block_last_field && element == EL_SP_MURPHY)
1681 player->block_delay_adjustment = 1;
1683 /* special case 2: in game engines before 3.1.1, blocking was different */
1684 if (game.use_block_last_field_bug)
1685 player->block_delay_adjustment = (player->block_last_field ? -1 : 1);
1687 if (!options.network || player->connected)
1689 player->active = TRUE;
1691 /* remove potentially duplicate players */
1692 if (StorePlayer[jx][jy] == Feld[x][y])
1693 StorePlayer[jx][jy] = 0;
1695 StorePlayer[x][y] = Feld[x][y];
1697 #if DEBUG_INIT_PLAYER
1700 printf("- player element %d activated", player->element_nr);
1701 printf(" (local player is %d and currently %s)\n",
1702 local_player->element_nr,
1703 local_player->active ? "active" : "not active");
1708 Feld[x][y] = EL_EMPTY;
1710 player->jx = player->last_jx = x;
1711 player->jy = player->last_jy = y;
1716 int player_nr = GET_PLAYER_NR(element);
1717 struct PlayerInfo *player = &stored_player[player_nr];
1719 if (player->active && player->killed)
1720 player->reanimated = TRUE; /* if player was just killed, reanimate him */
1724 static void InitField(int x, int y, boolean init_game)
1726 int element = Feld[x][y];
1735 InitPlayerField(x, y, element, init_game);
1738 case EL_SOKOBAN_FIELD_PLAYER:
1739 element = Feld[x][y] = EL_PLAYER_1;
1740 InitField(x, y, init_game);
1742 element = Feld[x][y] = EL_SOKOBAN_FIELD_EMPTY;
1743 InitField(x, y, init_game);
1746 case EL_SOKOBAN_FIELD_EMPTY:
1747 local_player->sokobanfields_still_needed++;
1751 if (x < lev_fieldx-1 && Feld[x+1][y] == EL_ACID)
1752 Feld[x][y] = EL_ACID_POOL_TOPLEFT;
1753 else if (x > 0 && Feld[x-1][y] == EL_ACID)
1754 Feld[x][y] = EL_ACID_POOL_TOPRIGHT;
1755 else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPLEFT)
1756 Feld[x][y] = EL_ACID_POOL_BOTTOMLEFT;
1757 else if (y > 0 && Feld[x][y-1] == EL_ACID)
1758 Feld[x][y] = EL_ACID_POOL_BOTTOM;
1759 else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPRIGHT)
1760 Feld[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
1769 case EL_SPACESHIP_RIGHT:
1770 case EL_SPACESHIP_UP:
1771 case EL_SPACESHIP_LEFT:
1772 case EL_SPACESHIP_DOWN:
1773 case EL_BD_BUTTERFLY:
1774 case EL_BD_BUTTERFLY_RIGHT:
1775 case EL_BD_BUTTERFLY_UP:
1776 case EL_BD_BUTTERFLY_LEFT:
1777 case EL_BD_BUTTERFLY_DOWN:
1779 case EL_BD_FIREFLY_RIGHT:
1780 case EL_BD_FIREFLY_UP:
1781 case EL_BD_FIREFLY_LEFT:
1782 case EL_BD_FIREFLY_DOWN:
1783 case EL_PACMAN_RIGHT:
1785 case EL_PACMAN_LEFT:
1786 case EL_PACMAN_DOWN:
1788 case EL_YAMYAM_LEFT:
1789 case EL_YAMYAM_RIGHT:
1791 case EL_YAMYAM_DOWN:
1792 case EL_DARK_YAMYAM:
1795 case EL_SP_SNIKSNAK:
1796 case EL_SP_ELECTRON:
1805 case EL_AMOEBA_FULL:
1810 case EL_AMOEBA_DROP:
1811 if (y == lev_fieldy - 1)
1813 Feld[x][y] = EL_AMOEBA_GROWING;
1814 Store[x][y] = EL_AMOEBA_WET;
1818 case EL_DYNAMITE_ACTIVE:
1819 case EL_SP_DISK_RED_ACTIVE:
1820 case EL_DYNABOMB_PLAYER_1_ACTIVE:
1821 case EL_DYNABOMB_PLAYER_2_ACTIVE:
1822 case EL_DYNABOMB_PLAYER_3_ACTIVE:
1823 case EL_DYNABOMB_PLAYER_4_ACTIVE:
1824 MovDelay[x][y] = 96;
1827 case EL_EM_DYNAMITE_ACTIVE:
1828 MovDelay[x][y] = 32;
1832 local_player->lights_still_needed++;
1836 local_player->friends_still_needed++;
1841 GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
1844 case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
1845 case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
1846 case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
1847 case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
1848 case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
1849 case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
1850 case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
1851 case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
1852 case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
1853 case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
1854 case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
1855 case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
1858 int belt_nr = getBeltNrFromBeltSwitchElement(Feld[x][y]);
1859 int belt_dir = getBeltDirFromBeltSwitchElement(Feld[x][y]);
1860 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Feld[x][y]);
1862 if (game.belt_dir_nr[belt_nr] == 3) /* initial value */
1864 game.belt_dir[belt_nr] = belt_dir;
1865 game.belt_dir_nr[belt_nr] = belt_dir_nr;
1867 else /* more than one switch -- set it like the first switch */
1869 Feld[x][y] = Feld[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
1874 case EL_LIGHT_SWITCH_ACTIVE:
1876 game.light_time_left = level.time_light * FRAMES_PER_SECOND;
1879 case EL_INVISIBLE_STEELWALL:
1880 case EL_INVISIBLE_WALL:
1881 case EL_INVISIBLE_SAND:
1882 if (game.light_time_left > 0 ||
1883 game.lenses_time_left > 0)
1884 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
1887 case EL_EMC_MAGIC_BALL:
1888 if (game.ball_state)
1889 Feld[x][y] = EL_EMC_MAGIC_BALL_ACTIVE;
1892 case EL_EMC_MAGIC_BALL_SWITCH:
1893 if (game.ball_state)
1894 Feld[x][y] = EL_EMC_MAGIC_BALL_SWITCH_ACTIVE;
1897 case EL_TRIGGER_PLAYER:
1898 case EL_TRIGGER_ELEMENT:
1899 case EL_TRIGGER_CE_VALUE:
1900 case EL_TRIGGER_CE_SCORE:
1902 case EL_ANY_ELEMENT:
1903 case EL_CURRENT_CE_VALUE:
1904 case EL_CURRENT_CE_SCORE:
1921 /* reference elements should not be used on the playfield */
1922 Feld[x][y] = EL_EMPTY;
1926 if (IS_CUSTOM_ELEMENT(element))
1928 if (CAN_MOVE(element))
1931 if (!element_info[element].use_last_ce_value || init_game)
1932 CustomValue[x][y] = GET_NEW_CE_VALUE(Feld[x][y]);
1934 else if (IS_GROUP_ELEMENT(element))
1936 Feld[x][y] = GetElementFromGroupElement(element);
1938 InitField(x, y, init_game);
1945 CheckTriggeredElementChange(x, y, element, CE_CREATION_OF_X);
1948 static inline void InitField_WithBug1(int x, int y, boolean init_game)
1950 InitField(x, y, init_game);
1952 /* not needed to call InitMovDir() -- already done by InitField()! */
1953 if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1954 CAN_MOVE(Feld[x][y]))
1958 static inline void InitField_WithBug2(int x, int y, boolean init_game)
1960 int old_element = Feld[x][y];
1962 InitField(x, y, init_game);
1964 /* not needed to call InitMovDir() -- already done by InitField()! */
1965 if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1966 CAN_MOVE(old_element) &&
1967 (old_element < EL_MOLE_LEFT || old_element > EL_MOLE_DOWN))
1970 /* this case is in fact a combination of not less than three bugs:
1971 first, it calls InitMovDir() for elements that can move, although this is
1972 already done by InitField(); then, it checks the element that was at this
1973 field _before_ the call to InitField() (which can change it); lastly, it
1974 was not called for "mole with direction" elements, which were treated as
1975 "cannot move" due to (fixed) wrong element initialization in "src/init.c"
1979 static int get_key_element_from_nr(int key_nr)
1981 int key_base_element = (key_nr >= STD_NUM_KEYS ? EL_EMC_KEY_5 - STD_NUM_KEYS :
1982 level.game_engine_type == GAME_ENGINE_TYPE_EM ?
1983 EL_EM_KEY_1 : EL_KEY_1);
1985 return key_base_element + key_nr;
1988 static int get_next_dropped_element(struct PlayerInfo *player)
1990 return (player->inventory_size > 0 ?
1991 player->inventory_element[player->inventory_size - 1] :
1992 player->inventory_infinite_element != EL_UNDEFINED ?
1993 player->inventory_infinite_element :
1994 player->dynabombs_left > 0 ?
1995 EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
1999 static int get_inventory_element_from_pos(struct PlayerInfo *player, int pos)
2001 /* pos >= 0: get element from bottom of the stack;
2002 pos < 0: get element from top of the stack */
2006 int min_inventory_size = -pos;
2007 int inventory_pos = player->inventory_size - min_inventory_size;
2008 int min_dynabombs_left = min_inventory_size - player->inventory_size;
2010 return (player->inventory_size >= min_inventory_size ?
2011 player->inventory_element[inventory_pos] :
2012 player->inventory_infinite_element != EL_UNDEFINED ?
2013 player->inventory_infinite_element :
2014 player->dynabombs_left >= min_dynabombs_left ?
2015 EL_DYNABOMB_PLAYER_1 + player->index_nr :
2020 int min_dynabombs_left = pos + 1;
2021 int min_inventory_size = pos + 1 - player->dynabombs_left;
2022 int inventory_pos = pos - player->dynabombs_left;
2024 return (player->inventory_infinite_element != EL_UNDEFINED ?
2025 player->inventory_infinite_element :
2026 player->dynabombs_left >= min_dynabombs_left ?
2027 EL_DYNABOMB_PLAYER_1 + player->index_nr :
2028 player->inventory_size >= min_inventory_size ?
2029 player->inventory_element[inventory_pos] :
2034 static int compareGamePanelOrderInfo(const void *object1, const void *object2)
2036 const struct GamePanelOrderInfo *gpo1 = (struct GamePanelOrderInfo *)object1;
2037 const struct GamePanelOrderInfo *gpo2 = (struct GamePanelOrderInfo *)object2;
2040 if (gpo1->sort_priority != gpo2->sort_priority)
2041 compare_result = gpo1->sort_priority - gpo2->sort_priority;
2043 compare_result = gpo1->nr - gpo2->nr;
2045 return compare_result;
2048 void InitGameControlValues()
2052 for (i = 0; game_panel_controls[i].nr != -1; i++)
2054 struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2055 struct GamePanelOrderInfo *gpo = &game_panel_order[i];
2056 struct TextPosInfo *pos = gpc->pos;
2058 int type = gpc->type;
2062 Error(ERR_INFO, "'game_panel_controls' structure corrupted at %d", i);
2063 Error(ERR_EXIT, "this should not happen -- please debug");
2066 /* force update of game controls after initialization */
2067 gpc->value = gpc->last_value = -1;
2068 gpc->frame = gpc->last_frame = -1;
2069 gpc->gfx_frame = -1;
2071 /* determine panel value width for later calculation of alignment */
2072 if (type == TYPE_INTEGER || type == TYPE_STRING)
2074 pos->width = pos->size * getFontWidth(pos->font);
2075 pos->height = getFontHeight(pos->font);
2077 else if (type == TYPE_ELEMENT)
2079 pos->width = pos->size;
2080 pos->height = pos->size;
2083 /* fill structure for game panel draw order */
2085 gpo->sort_priority = pos->sort_priority;
2088 /* sort game panel controls according to sort_priority and control number */
2089 qsort(game_panel_order, NUM_GAME_PANEL_CONTROLS,
2090 sizeof(struct GamePanelOrderInfo), compareGamePanelOrderInfo);
2093 void UpdatePlayfieldElementCount()
2095 boolean use_element_count = FALSE;
2098 /* first check if it is needed at all to calculate playfield element count */
2099 for (i = GAME_PANEL_ELEMENT_COUNT_1; i <= GAME_PANEL_ELEMENT_COUNT_8; i++)
2100 if (!PANEL_DEACTIVATED(game_panel_controls[i].pos))
2101 use_element_count = TRUE;
2103 if (!use_element_count)
2106 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2107 element_info[i].element_count = 0;
2109 SCAN_PLAYFIELD(x, y)
2111 element_info[Feld[x][y]].element_count++;
2114 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
2115 for (j = 0; j < MAX_NUM_ELEMENTS; j++)
2116 if (IS_IN_GROUP(j, i))
2117 element_info[EL_GROUP_START + i].element_count +=
2118 element_info[j].element_count;
2121 void UpdateGameControlValues()
2124 int time = (local_player->LevelSolved ?
2125 local_player->LevelSolved_CountingTime :
2126 level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2127 level.native_em_level->lev->time :
2128 level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2129 level.native_sp_level->game_sp->time_played :
2130 game.no_time_limit ? TimePlayed : TimeLeft);
2131 int score = (local_player->LevelSolved ?
2132 local_player->LevelSolved_CountingScore :
2133 level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2134 level.native_em_level->lev->score :
2135 level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2136 level.native_sp_level->game_sp->score :
2137 local_player->score);
2138 int gems = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2139 level.native_em_level->lev->required :
2140 level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2141 level.native_sp_level->game_sp->infotrons_still_needed :
2142 local_player->gems_still_needed);
2143 int exit_closed = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2144 level.native_em_level->lev->required > 0 :
2145 level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2146 level.native_sp_level->game_sp->infotrons_still_needed > 0 :
2147 local_player->gems_still_needed > 0 ||
2148 local_player->sokobanfields_still_needed > 0 ||
2149 local_player->lights_still_needed > 0);
2151 UpdatePlayfieldElementCount();
2153 /* update game panel control values */
2155 game_panel_controls[GAME_PANEL_LEVEL_NUMBER].value = level_nr;
2156 game_panel_controls[GAME_PANEL_GEMS].value = gems;
2158 game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value = 0;
2159 for (i = 0; i < MAX_NUM_KEYS; i++)
2160 game_panel_controls[GAME_PANEL_KEY_1 + i].value = EL_EMPTY;
2161 game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_EMPTY;
2162 game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value = 0;
2164 if (game.centered_player_nr == -1)
2166 for (i = 0; i < MAX_PLAYERS; i++)
2168 /* only one player in Supaplex game engine */
2169 if (level.game_engine_type == GAME_ENGINE_TYPE_SP && i > 0)
2172 for (k = 0; k < MAX_NUM_KEYS; k++)
2174 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2176 if (level.native_em_level->ply[i]->keys & (1 << k))
2177 game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2178 get_key_element_from_nr(k);
2180 else if (stored_player[i].key[k])
2181 game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2182 get_key_element_from_nr(k);
2185 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2186 game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2187 level.native_em_level->ply[i]->dynamite;
2188 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
2189 game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2190 level.native_sp_level->game_sp->red_disk_count;
2192 game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2193 stored_player[i].inventory_size;
2195 if (stored_player[i].num_white_keys > 0)
2196 game_panel_controls[GAME_PANEL_KEY_WHITE].value =
2199 game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2200 stored_player[i].num_white_keys;
2205 int player_nr = game.centered_player_nr;
2207 for (k = 0; k < MAX_NUM_KEYS; k++)
2209 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2211 if (level.native_em_level->ply[player_nr]->keys & (1 << k))
2212 game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2213 get_key_element_from_nr(k);
2215 else if (stored_player[player_nr].key[k])
2216 game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2217 get_key_element_from_nr(k);
2220 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2221 game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2222 level.native_em_level->ply[player_nr]->dynamite;
2223 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
2224 game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2225 level.native_sp_level->game_sp->red_disk_count;
2227 game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2228 stored_player[player_nr].inventory_size;
2230 if (stored_player[player_nr].num_white_keys > 0)
2231 game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_DC_KEY_WHITE;
2233 game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2234 stored_player[player_nr].num_white_keys;
2237 for (i = 0; i < NUM_PANEL_INVENTORY; i++)
2239 game_panel_controls[GAME_PANEL_INVENTORY_FIRST_1 + i].value =
2240 get_inventory_element_from_pos(local_player, i);
2241 game_panel_controls[GAME_PANEL_INVENTORY_LAST_1 + i].value =
2242 get_inventory_element_from_pos(local_player, -i - 1);
2245 game_panel_controls[GAME_PANEL_SCORE].value = score;
2246 game_panel_controls[GAME_PANEL_HIGHSCORE].value = highscore[0].Score;
2248 game_panel_controls[GAME_PANEL_TIME].value = time;
2250 game_panel_controls[GAME_PANEL_TIME_HH].value = time / 3600;
2251 game_panel_controls[GAME_PANEL_TIME_MM].value = (time / 60) % 60;
2252 game_panel_controls[GAME_PANEL_TIME_SS].value = time % 60;
2254 game_panel_controls[GAME_PANEL_FRAME].value = FrameCounter;
2256 game_panel_controls[GAME_PANEL_SHIELD_NORMAL].value =
2257 (local_player->shield_normal_time_left > 0 ? EL_SHIELD_NORMAL_ACTIVE :
2259 game_panel_controls[GAME_PANEL_SHIELD_NORMAL_TIME].value =
2260 local_player->shield_normal_time_left;
2261 game_panel_controls[GAME_PANEL_SHIELD_DEADLY].value =
2262 (local_player->shield_deadly_time_left > 0 ? EL_SHIELD_DEADLY_ACTIVE :
2264 game_panel_controls[GAME_PANEL_SHIELD_DEADLY_TIME].value =
2265 local_player->shield_deadly_time_left;
2267 game_panel_controls[GAME_PANEL_EXIT].value =
2268 (exit_closed ? EL_EXIT_CLOSED : EL_EXIT_OPEN);
2270 game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL].value =
2271 (game.ball_state ? EL_EMC_MAGIC_BALL_ACTIVE : EL_EMC_MAGIC_BALL);
2272 game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL_SWITCH].value =
2273 (game.ball_state ? EL_EMC_MAGIC_BALL_SWITCH_ACTIVE :
2274 EL_EMC_MAGIC_BALL_SWITCH);
2276 game_panel_controls[GAME_PANEL_LIGHT_SWITCH].value =
2277 (game.light_time_left > 0 ? EL_LIGHT_SWITCH_ACTIVE : EL_LIGHT_SWITCH);
2278 game_panel_controls[GAME_PANEL_LIGHT_SWITCH_TIME].value =
2279 game.light_time_left;
2281 game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH].value =
2282 (game.timegate_time_left > 0 ? EL_TIMEGATE_OPEN : EL_TIMEGATE_CLOSED);
2283 game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH_TIME].value =
2284 game.timegate_time_left;
2286 game_panel_controls[GAME_PANEL_SWITCHGATE_SWITCH].value =
2287 EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
2289 game_panel_controls[GAME_PANEL_EMC_LENSES].value =
2290 (game.lenses_time_left > 0 ? EL_EMC_LENSES : EL_EMPTY);
2291 game_panel_controls[GAME_PANEL_EMC_LENSES_TIME].value =
2292 game.lenses_time_left;
2294 game_panel_controls[GAME_PANEL_EMC_MAGNIFIER].value =
2295 (game.magnify_time_left > 0 ? EL_EMC_MAGNIFIER : EL_EMPTY);
2296 game_panel_controls[GAME_PANEL_EMC_MAGNIFIER_TIME].value =
2297 game.magnify_time_left;
2299 game_panel_controls[GAME_PANEL_BALLOON_SWITCH].value =
2300 (game.wind_direction == MV_LEFT ? EL_BALLOON_SWITCH_LEFT :
2301 game.wind_direction == MV_RIGHT ? EL_BALLOON_SWITCH_RIGHT :
2302 game.wind_direction == MV_UP ? EL_BALLOON_SWITCH_UP :
2303 game.wind_direction == MV_DOWN ? EL_BALLOON_SWITCH_DOWN :
2304 EL_BALLOON_SWITCH_NONE);
2306 game_panel_controls[GAME_PANEL_DYNABOMB_NUMBER].value =
2307 local_player->dynabomb_count;
2308 game_panel_controls[GAME_PANEL_DYNABOMB_SIZE].value =
2309 local_player->dynabomb_size;
2310 game_panel_controls[GAME_PANEL_DYNABOMB_POWER].value =
2311 (local_player->dynabomb_xl ? EL_DYNABOMB_INCREASE_POWER : EL_EMPTY);
2313 game_panel_controls[GAME_PANEL_PENGUINS].value =
2314 local_player->friends_still_needed;
2316 game_panel_controls[GAME_PANEL_SOKOBAN_OBJECTS].value =
2317 local_player->sokobanfields_still_needed;
2318 game_panel_controls[GAME_PANEL_SOKOBAN_FIELDS].value =
2319 local_player->sokobanfields_still_needed;
2321 game_panel_controls[GAME_PANEL_ROBOT_WHEEL].value =
2322 (game.robot_wheel_active ? EL_ROBOT_WHEEL_ACTIVE : EL_ROBOT_WHEEL);
2324 for (i = 0; i < NUM_BELTS; i++)
2326 game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1 + i].value =
2327 (game.belt_dir[i] != MV_NONE ? EL_CONVEYOR_BELT_1_MIDDLE_ACTIVE :
2328 EL_CONVEYOR_BELT_1_MIDDLE) + i;
2329 game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1_SWITCH + i].value =
2330 getBeltSwitchElementFromBeltNrAndBeltDir(i, game.belt_dir[i]);
2333 game_panel_controls[GAME_PANEL_MAGIC_WALL].value =
2334 (game.magic_wall_active ? EL_MAGIC_WALL_ACTIVE : EL_MAGIC_WALL);
2335 game_panel_controls[GAME_PANEL_MAGIC_WALL_TIME].value =
2336 game.magic_wall_time_left;
2338 game_panel_controls[GAME_PANEL_GRAVITY_STATE].value =
2339 local_player->gravity;
2341 for (i = 0; i < NUM_PANEL_GRAPHICS; i++)
2342 game_panel_controls[GAME_PANEL_GRAPHIC_1 + i].value = EL_GRAPHIC_1 + i;
2344 for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2345 game_panel_controls[GAME_PANEL_ELEMENT_1 + i].value =
2346 (IS_DRAWABLE_ELEMENT(game.panel.element[i].id) ?
2347 game.panel.element[i].id : EL_UNDEFINED);
2349 for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2350 game_panel_controls[GAME_PANEL_ELEMENT_COUNT_1 + i].value =
2351 (IS_VALID_ELEMENT(game.panel.element_count[i].id) ?
2352 element_info[game.panel.element_count[i].id].element_count : 0);
2354 for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2355 game_panel_controls[GAME_PANEL_CE_SCORE_1 + i].value =
2356 (IS_CUSTOM_ELEMENT(game.panel.ce_score[i].id) ?
2357 element_info[game.panel.ce_score[i].id].collect_score : 0);
2359 for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2360 game_panel_controls[GAME_PANEL_CE_SCORE_1_ELEMENT + i].value =
2361 (IS_CUSTOM_ELEMENT(game.panel.ce_score_element[i].id) ?
2362 element_info[game.panel.ce_score_element[i].id].collect_score :
2365 game_panel_controls[GAME_PANEL_PLAYER_NAME].value = 0;
2366 game_panel_controls[GAME_PANEL_LEVEL_NAME].value = 0;
2367 game_panel_controls[GAME_PANEL_LEVEL_AUTHOR].value = 0;
2369 /* update game panel control frames */
2371 for (i = 0; game_panel_controls[i].nr != -1; i++)
2373 struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2375 if (gpc->type == TYPE_ELEMENT)
2377 if (gpc->value != EL_UNDEFINED && gpc->value != EL_EMPTY)
2379 int last_anim_random_frame = gfx.anim_random_frame;
2380 int element = gpc->value;
2381 int graphic = el2panelimg(element);
2383 if (gpc->value != gpc->last_value)
2386 gpc->gfx_random = INIT_GFX_RANDOM();
2392 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2393 IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2394 gpc->gfx_random = INIT_GFX_RANDOM();
2397 if (ANIM_MODE(graphic) == ANIM_RANDOM)
2398 gfx.anim_random_frame = gpc->gfx_random;
2400 if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
2401 gpc->gfx_frame = element_info[element].collect_score;
2403 gpc->frame = getGraphicAnimationFrame(el2panelimg(gpc->value),
2406 if (ANIM_MODE(graphic) == ANIM_RANDOM)
2407 gfx.anim_random_frame = last_anim_random_frame;
2413 void DisplayGameControlValues()
2415 boolean redraw_panel = FALSE;
2418 for (i = 0; game_panel_controls[i].nr != -1; i++)
2420 struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2422 if (PANEL_DEACTIVATED(gpc->pos))
2425 if (gpc->value == gpc->last_value &&
2426 gpc->frame == gpc->last_frame)
2429 redraw_panel = TRUE;
2435 /* copy default game door content to main double buffer */
2437 /* !!! CHECK AGAIN !!! */
2438 SetPanelBackground();
2439 // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
2440 DrawBackground(DX, DY, DXSIZE, DYSIZE);
2442 /* redraw game control buttons */
2443 RedrawGameButtons();
2445 game_status = GAME_MODE_PSEUDO_PANEL;
2447 for (i = 0; i < NUM_GAME_PANEL_CONTROLS; i++)
2449 int nr = game_panel_order[i].nr;
2450 struct GamePanelControlInfo *gpc = &game_panel_controls[nr];
2451 struct TextPosInfo *pos = gpc->pos;
2452 int type = gpc->type;
2453 int value = gpc->value;
2454 int frame = gpc->frame;
2455 int size = pos->size;
2456 int font = pos->font;
2457 boolean draw_masked = pos->draw_masked;
2458 int mask_mode = (draw_masked ? BLIT_MASKED : BLIT_OPAQUE);
2460 if (PANEL_DEACTIVATED(pos))
2463 gpc->last_value = value;
2464 gpc->last_frame = frame;
2466 if (type == TYPE_INTEGER)
2468 if (nr == GAME_PANEL_LEVEL_NUMBER ||
2469 nr == GAME_PANEL_TIME)
2471 boolean use_dynamic_size = (size == -1 ? TRUE : FALSE);
2473 if (use_dynamic_size) /* use dynamic number of digits */
2475 int value_change = (nr == GAME_PANEL_LEVEL_NUMBER ? 100 : 1000);
2476 int size1 = (nr == GAME_PANEL_LEVEL_NUMBER ? 2 : 3);
2477 int size2 = size1 + 1;
2478 int font1 = pos->font;
2479 int font2 = pos->font_alt;
2481 size = (value < value_change ? size1 : size2);
2482 font = (value < value_change ? font1 : font2);
2486 /* correct text size if "digits" is zero or less */
2488 size = strlen(int2str(value, size));
2490 /* dynamically correct text alignment */
2491 pos->width = size * getFontWidth(font);
2493 DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2494 int2str(value, size), font, mask_mode);
2496 else if (type == TYPE_ELEMENT)
2498 int element, graphic;
2502 int dst_x = PANEL_XPOS(pos);
2503 int dst_y = PANEL_YPOS(pos);
2505 if (value != EL_UNDEFINED && value != EL_EMPTY)
2508 graphic = el2panelimg(value);
2510 // printf("::: %d, '%s' [%d]\n", element, EL_NAME(element), size);
2512 if (element >= EL_GRAPHIC_1 && element <= EL_GRAPHIC_8 && size == 0)
2515 getSizedGraphicSource(graphic, frame, size, &src_bitmap,
2518 width = graphic_info[graphic].width * size / TILESIZE;
2519 height = graphic_info[graphic].height * size / TILESIZE;
2522 BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2525 BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2529 else if (type == TYPE_STRING)
2531 boolean active = (value != 0);
2532 char *state_normal = "off";
2533 char *state_active = "on";
2534 char *state = (active ? state_active : state_normal);
2535 char *s = (nr == GAME_PANEL_GRAVITY_STATE ? state :
2536 nr == GAME_PANEL_PLAYER_NAME ? setup.player_name :
2537 nr == GAME_PANEL_LEVEL_NAME ? level.name :
2538 nr == GAME_PANEL_LEVEL_AUTHOR ? level.author : NULL);
2540 if (nr == GAME_PANEL_GRAVITY_STATE)
2542 int font1 = pos->font; /* (used for normal state) */
2543 int font2 = pos->font_alt; /* (used for active state) */
2545 font = (active ? font2 : font1);
2554 /* don't truncate output if "chars" is zero or less */
2557 /* dynamically correct text alignment */
2558 pos->width = size * getFontWidth(font);
2561 s_cut = getStringCopyN(s, size);
2563 DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2564 s_cut, font, mask_mode);
2570 redraw_mask |= REDRAW_DOOR_1;
2573 game_status = GAME_MODE_PLAYING;
2576 void UpdateAndDisplayGameControlValues()
2578 if (tape.warp_forward)
2581 UpdateGameControlValues();
2582 DisplayGameControlValues();
2585 void UpdateGameDoorValues()
2587 UpdateGameControlValues();
2590 void DrawGameDoorValues()
2592 DisplayGameControlValues();
2597 =============================================================================
2599 -----------------------------------------------------------------------------
2600 initialize game engine due to level / tape version number
2601 =============================================================================
2604 static void InitGameEngine()
2606 int i, j, k, l, x, y;
2608 /* set game engine from tape file when re-playing, else from level file */
2609 game.engine_version = (tape.playing ? tape.engine_version :
2610 level.game_version);
2612 /* set single or multi-player game mode (needed for re-playing tapes) */
2613 game.team_mode = setup.team_mode;
2617 int num_players = 0;
2619 for (i = 0; i < MAX_PLAYERS; i++)
2620 if (tape.player_participates[i])
2623 /* multi-player tapes contain input data for more than one player */
2624 game.team_mode = (num_players > 1);
2627 /* ---------------------------------------------------------------------- */
2628 /* set flags for bugs and changes according to active game engine version */
2629 /* ---------------------------------------------------------------------- */
2632 Summary of bugfix/change:
2633 Fixed handling for custom elements that change when pushed by the player.
2635 Fixed/changed in version:
2639 Before 3.1.0, custom elements that "change when pushing" changed directly
2640 after the player started pushing them (until then handled in "DigField()").
2641 Since 3.1.0, these custom elements are not changed until the "pushing"
2642 move of the element is finished (now handled in "ContinueMoving()").
2644 Affected levels/tapes:
2645 The first condition is generally needed for all levels/tapes before version
2646 3.1.0, which might use the old behaviour before it was changed; known tapes
2647 that are affected are some tapes from the level set "Walpurgis Gardens" by
2649 The second condition is an exception from the above case and is needed for
2650 the special case of tapes recorded with game (not engine!) version 3.1.0 or
2651 above (including some development versions of 3.1.0), but before it was
2652 known that this change would break tapes like the above and was fixed in
2653 3.1.1, so that the changed behaviour was active although the engine version
2654 while recording maybe was before 3.1.0. There is at least one tape that is
2655 affected by this exception, which is the tape for the one-level set "Bug
2656 Machine" by Juergen Bonhagen.
2659 game.use_change_when_pushing_bug =
2660 (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2662 tape.game_version >= VERSION_IDENT(3,1,0,0) &&
2663 tape.game_version < VERSION_IDENT(3,1,1,0)));
2666 Summary of bugfix/change:
2667 Fixed handling for blocking the field the player leaves when moving.
2669 Fixed/changed in version:
2673 Before 3.1.1, when "block last field when moving" was enabled, the field
2674 the player is leaving when moving was blocked for the time of the move,
2675 and was directly unblocked afterwards. This resulted in the last field
2676 being blocked for exactly one less than the number of frames of one player
2677 move. Additionally, even when blocking was disabled, the last field was
2678 blocked for exactly one frame.
2679 Since 3.1.1, due to changes in player movement handling, the last field
2680 is not blocked at all when blocking is disabled. When blocking is enabled,
2681 the last field is blocked for exactly the number of frames of one player
2682 move. Additionally, if the player is Murphy, the hero of Supaplex, the
2683 last field is blocked for exactly one more than the number of frames of
2686 Affected levels/tapes:
2687 (!!! yet to be determined -- probably many !!!)
2690 game.use_block_last_field_bug =
2691 (game.engine_version < VERSION_IDENT(3,1,1,0));
2693 /* ---------------------------------------------------------------------- */
2695 /* set maximal allowed number of custom element changes per game frame */
2696 game.max_num_changes_per_frame = 1;
2698 /* default scan direction: scan playfield from top/left to bottom/right */
2699 InitPlayfieldScanMode(CA_ARG_SCAN_MODE_NORMAL);
2701 /* dynamically adjust element properties according to game engine version */
2702 InitElementPropertiesEngine(game.engine_version);
2705 printf("level %d: level version == %06d\n", level_nr, level.game_version);
2706 printf(" tape version == %06d [%s] [file: %06d]\n",
2707 tape.engine_version, (tape.playing ? "PLAYING" : "RECORDING"),
2709 printf(" => game.engine_version == %06d\n", game.engine_version);
2712 /* ---------- initialize player's initial move delay --------------------- */
2714 /* dynamically adjust player properties according to level information */
2715 for (i = 0; i < MAX_PLAYERS; i++)
2716 game.initial_move_delay_value[i] =
2717 get_move_delay_from_stepsize(level.initial_player_stepsize[i]);
2719 /* dynamically adjust player properties according to game engine version */
2720 for (i = 0; i < MAX_PLAYERS; i++)
2721 game.initial_move_delay[i] =
2722 (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
2723 game.initial_move_delay_value[i] : 0);
2725 /* ---------- initialize player's initial push delay --------------------- */
2727 /* dynamically adjust player properties according to game engine version */
2728 game.initial_push_delay_value =
2729 (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
2731 /* ---------- initialize changing elements ------------------------------- */
2733 /* initialize changing elements information */
2734 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2736 struct ElementInfo *ei = &element_info[i];
2738 /* this pointer might have been changed in the level editor */
2739 ei->change = &ei->change_page[0];
2741 if (!IS_CUSTOM_ELEMENT(i))
2743 ei->change->target_element = EL_EMPTY_SPACE;
2744 ei->change->delay_fixed = 0;
2745 ei->change->delay_random = 0;
2746 ei->change->delay_frames = 1;
2749 for (j = 0; j < NUM_CHANGE_EVENTS; j++)
2751 ei->has_change_event[j] = FALSE;
2753 ei->event_page_nr[j] = 0;
2754 ei->event_page[j] = &ei->change_page[0];
2758 /* add changing elements from pre-defined list */
2759 for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
2761 struct ChangingElementInfo *ch_delay = &change_delay_list[i];
2762 struct ElementInfo *ei = &element_info[ch_delay->element];
2764 ei->change->target_element = ch_delay->target_element;
2765 ei->change->delay_fixed = ch_delay->change_delay;
2767 ei->change->pre_change_function = ch_delay->pre_change_function;
2768 ei->change->change_function = ch_delay->change_function;
2769 ei->change->post_change_function = ch_delay->post_change_function;
2771 ei->change->can_change = TRUE;
2772 ei->change->can_change_or_has_action = TRUE;
2774 ei->has_change_event[CE_DELAY] = TRUE;
2776 SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
2777 SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE_OR_HAS_ACTION, TRUE);
2780 /* ---------- initialize internal run-time variables --------------------- */
2782 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2784 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
2786 for (j = 0; j < ei->num_change_pages; j++)
2788 ei->change_page[j].can_change_or_has_action =
2789 (ei->change_page[j].can_change |
2790 ei->change_page[j].has_action);
2794 /* add change events from custom element configuration */
2795 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2797 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
2799 for (j = 0; j < ei->num_change_pages; j++)
2801 if (!ei->change_page[j].can_change_or_has_action)
2804 for (k = 0; k < NUM_CHANGE_EVENTS; k++)
2806 /* only add event page for the first page found with this event */
2807 if (ei->change_page[j].has_event[k] && !(ei->has_change_event[k]))
2809 ei->has_change_event[k] = TRUE;
2811 ei->event_page_nr[k] = j;
2812 ei->event_page[k] = &ei->change_page[j];
2818 /* ---------- initialize reference elements in change conditions --------- */
2820 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2822 int element = EL_CUSTOM_START + i;
2823 struct ElementInfo *ei = &element_info[element];
2825 for (j = 0; j < ei->num_change_pages; j++)
2827 int trigger_element = ei->change_page[j].initial_trigger_element;
2829 if (trigger_element >= EL_PREV_CE_8 &&
2830 trigger_element <= EL_NEXT_CE_8)
2831 trigger_element = RESOLVED_REFERENCE_ELEMENT(element, trigger_element);
2833 ei->change_page[j].trigger_element = trigger_element;
2837 /* ---------- initialize run-time trigger player and element ------------- */
2839 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2841 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
2843 for (j = 0; j < ei->num_change_pages; j++)
2845 ei->change_page[j].actual_trigger_element = EL_EMPTY;
2846 ei->change_page[j].actual_trigger_player = EL_EMPTY;
2847 ei->change_page[j].actual_trigger_player_bits = CH_PLAYER_NONE;
2848 ei->change_page[j].actual_trigger_side = CH_SIDE_NONE;
2849 ei->change_page[j].actual_trigger_ce_value = 0;
2850 ei->change_page[j].actual_trigger_ce_score = 0;
2854 /* ---------- initialize trigger events ---------------------------------- */
2856 /* initialize trigger events information */
2857 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2858 for (j = 0; j < NUM_CHANGE_EVENTS; j++)
2859 trigger_events[i][j] = FALSE;
2861 /* add trigger events from element change event properties */
2862 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2864 struct ElementInfo *ei = &element_info[i];
2866 for (j = 0; j < ei->num_change_pages; j++)
2868 if (!ei->change_page[j].can_change_or_has_action)
2871 if (ei->change_page[j].has_event[CE_BY_OTHER_ACTION])
2873 int trigger_element = ei->change_page[j].trigger_element;
2875 for (k = 0; k < NUM_CHANGE_EVENTS; k++)
2877 if (ei->change_page[j].has_event[k])
2879 if (IS_GROUP_ELEMENT(trigger_element))
2881 struct ElementGroupInfo *group =
2882 element_info[trigger_element].group;
2884 for (l = 0; l < group->num_elements_resolved; l++)
2885 trigger_events[group->element_resolved[l]][k] = TRUE;
2887 else if (trigger_element == EL_ANY_ELEMENT)
2888 for (l = 0; l < MAX_NUM_ELEMENTS; l++)
2889 trigger_events[l][k] = TRUE;
2891 trigger_events[trigger_element][k] = TRUE;
2898 /* ---------- initialize push delay -------------------------------------- */
2900 /* initialize push delay values to default */
2901 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2903 if (!IS_CUSTOM_ELEMENT(i))
2905 /* set default push delay values (corrected since version 3.0.7-1) */
2906 if (game.engine_version < VERSION_IDENT(3,0,7,1))
2908 element_info[i].push_delay_fixed = 2;
2909 element_info[i].push_delay_random = 8;
2913 element_info[i].push_delay_fixed = 8;
2914 element_info[i].push_delay_random = 8;
2919 /* set push delay value for certain elements from pre-defined list */
2920 for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
2922 int e = push_delay_list[i].element;
2924 element_info[e].push_delay_fixed = push_delay_list[i].push_delay_fixed;
2925 element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
2928 /* set push delay value for Supaplex elements for newer engine versions */
2929 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
2931 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2933 if (IS_SP_ELEMENT(i))
2935 /* set SP push delay to just enough to push under a falling zonk */
2936 int delay = (game.engine_version >= VERSION_IDENT(3,1,1,0) ? 8 : 6);
2938 element_info[i].push_delay_fixed = delay;
2939 element_info[i].push_delay_random = 0;
2944 /* ---------- initialize move stepsize ----------------------------------- */
2946 /* initialize move stepsize values to default */
2947 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2948 if (!IS_CUSTOM_ELEMENT(i))
2949 element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
2951 /* set move stepsize value for certain elements from pre-defined list */
2952 for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
2954 int e = move_stepsize_list[i].element;
2956 element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
2959 /* ---------- initialize collect score ----------------------------------- */
2961 /* initialize collect score values for custom elements from initial value */
2962 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2963 if (IS_CUSTOM_ELEMENT(i))
2964 element_info[i].collect_score = element_info[i].collect_score_initial;
2966 /* ---------- initialize collect count ----------------------------------- */
2968 /* initialize collect count values for non-custom elements */
2969 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2970 if (!IS_CUSTOM_ELEMENT(i))
2971 element_info[i].collect_count_initial = 0;
2973 /* add collect count values for all elements from pre-defined list */
2974 for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
2975 element_info[collect_count_list[i].element].collect_count_initial =
2976 collect_count_list[i].count;
2978 /* ---------- initialize access direction -------------------------------- */
2980 /* initialize access direction values to default (access from every side) */
2981 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2982 if (!IS_CUSTOM_ELEMENT(i))
2983 element_info[i].access_direction = MV_ALL_DIRECTIONS;
2985 /* set access direction value for certain elements from pre-defined list */
2986 for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
2987 element_info[access_direction_list[i].element].access_direction =
2988 access_direction_list[i].direction;
2990 /* ---------- initialize explosion content ------------------------------- */
2991 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2993 if (IS_CUSTOM_ELEMENT(i))
2996 for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
2998 /* (content for EL_YAMYAM set at run-time with game.yamyam_content_nr) */
3000 element_info[i].content.e[x][y] =
3001 (i == EL_PLAYER_1 ? EL_EMERALD_YELLOW :
3002 i == EL_PLAYER_2 ? EL_EMERALD_RED :
3003 i == EL_PLAYER_3 ? EL_EMERALD :
3004 i == EL_PLAYER_4 ? EL_EMERALD_PURPLE :
3005 i == EL_MOLE ? EL_EMERALD_RED :
3006 i == EL_PENGUIN ? EL_EMERALD_PURPLE :
3007 i == EL_BUG ? (x == 1 && y == 1 ? EL_DIAMOND : EL_EMERALD) :
3008 i == EL_BD_BUTTERFLY ? EL_BD_DIAMOND :
3009 i == EL_SP_ELECTRON ? EL_SP_INFOTRON :
3010 i == EL_AMOEBA_TO_DIAMOND ? level.amoeba_content :
3011 i == EL_WALL_EMERALD ? EL_EMERALD :
3012 i == EL_WALL_DIAMOND ? EL_DIAMOND :
3013 i == EL_WALL_BD_DIAMOND ? EL_BD_DIAMOND :
3014 i == EL_WALL_EMERALD_YELLOW ? EL_EMERALD_YELLOW :
3015 i == EL_WALL_EMERALD_RED ? EL_EMERALD_RED :
3016 i == EL_WALL_EMERALD_PURPLE ? EL_EMERALD_PURPLE :
3017 i == EL_WALL_PEARL ? EL_PEARL :
3018 i == EL_WALL_CRYSTAL ? EL_CRYSTAL :
3023 /* ---------- initialize recursion detection ------------------------------ */
3024 recursion_loop_depth = 0;
3025 recursion_loop_detected = FALSE;
3026 recursion_loop_element = EL_UNDEFINED;
3028 /* ---------- initialize graphics engine ---------------------------------- */
3029 game.scroll_delay_value =
3030 (game.forced_scroll_delay_value != -1 ? game.forced_scroll_delay_value :
3031 setup.scroll_delay ? setup.scroll_delay_value : 0);
3032 game.scroll_delay_value =
3033 MIN(MAX(MIN_SCROLL_DELAY, game.scroll_delay_value), MAX_SCROLL_DELAY);
3036 int get_num_special_action(int element, int action_first, int action_last)
3038 int num_special_action = 0;
3041 for (i = action_first; i <= action_last; i++)
3043 boolean found = FALSE;
3045 for (j = 0; j < NUM_DIRECTIONS; j++)
3046 if (el_act_dir2img(element, i, j) !=
3047 el_act_dir2img(element, ACTION_DEFAULT, j))
3051 num_special_action++;
3056 return num_special_action;
3061 =============================================================================
3063 -----------------------------------------------------------------------------
3064 initialize and start new game
3065 =============================================================================
3070 int full_lev_fieldx = lev_fieldx + (BorderElement != EL_EMPTY ? 2 : 0);
3071 int full_lev_fieldy = lev_fieldy + (BorderElement != EL_EMPTY ? 2 : 0);
3073 boolean emulate_bd = TRUE; /* unless non-BOULDERDASH elements found */
3074 boolean emulate_sb = TRUE; /* unless non-SOKOBAN elements found */
3075 boolean emulate_sp = TRUE; /* unless non-SUPAPLEX elements found */
3076 int initial_move_dir = MV_DOWN;
3079 game_status = GAME_MODE_PLAYING;
3083 if (!game.restart_level)
3084 CloseDoor(DOOR_CLOSE_1);
3086 if (level_editor_test_game)
3087 FadeSkipNextFadeIn();
3089 FadeSetEnterScreen();
3091 FadeOut(REDRAW_FIELD);
3093 /* needed if different viewport properties defined for playing */
3094 ChangeViewportPropertiesIfNeeded();
3096 DrawCompleteVideoDisplay();
3099 InitGameControlValues();
3101 /* don't play tapes over network */
3102 network_playing = (options.network && !tape.playing);
3104 for (i = 0; i < MAX_PLAYERS; i++)
3106 struct PlayerInfo *player = &stored_player[i];
3108 player->index_nr = i;
3109 player->index_bit = (1 << i);
3110 player->element_nr = EL_PLAYER_1 + i;
3112 player->present = FALSE;
3113 player->active = FALSE;
3114 player->mapped = FALSE;
3116 player->killed = FALSE;
3117 player->reanimated = FALSE;
3120 player->effective_action = 0;
3121 player->programmed_action = 0;
3124 player->score_final = 0;
3126 player->gems_still_needed = level.gems_needed;
3127 player->sokobanfields_still_needed = 0;
3128 player->lights_still_needed = 0;
3129 player->friends_still_needed = 0;
3131 for (j = 0; j < MAX_NUM_KEYS; j++)
3132 player->key[j] = FALSE;
3134 player->num_white_keys = 0;
3136 player->dynabomb_count = 0;
3137 player->dynabomb_size = 1;
3138 player->dynabombs_left = 0;
3139 player->dynabomb_xl = FALSE;
3141 player->MovDir = initial_move_dir;
3144 player->GfxDir = initial_move_dir;
3145 player->GfxAction = ACTION_DEFAULT;
3147 player->StepFrame = 0;
3149 player->initial_element = player->element_nr;
3150 player->artwork_element =
3151 (level.use_artwork_element[i] ? level.artwork_element[i] :
3152 player->element_nr);
3153 player->use_murphy = FALSE;
3155 player->block_last_field = FALSE; /* initialized in InitPlayerField() */
3156 player->block_delay_adjustment = 0; /* initialized in InitPlayerField() */
3158 player->gravity = level.initial_player_gravity[i];
3160 player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
3162 player->actual_frame_counter = 0;
3164 player->step_counter = 0;
3166 player->last_move_dir = initial_move_dir;
3168 player->is_active = FALSE;
3170 player->is_waiting = FALSE;
3171 player->is_moving = FALSE;
3172 player->is_auto_moving = FALSE;
3173 player->is_digging = FALSE;
3174 player->is_snapping = FALSE;
3175 player->is_collecting = FALSE;
3176 player->is_pushing = FALSE;
3177 player->is_switching = FALSE;
3178 player->is_dropping = FALSE;
3179 player->is_dropping_pressed = FALSE;
3181 player->is_bored = FALSE;
3182 player->is_sleeping = FALSE;
3184 player->frame_counter_bored = -1;
3185 player->frame_counter_sleeping = -1;
3187 player->anim_delay_counter = 0;
3188 player->post_delay_counter = 0;
3190 player->dir_waiting = initial_move_dir;
3191 player->action_waiting = ACTION_DEFAULT;
3192 player->last_action_waiting = ACTION_DEFAULT;
3193 player->special_action_bored = ACTION_DEFAULT;
3194 player->special_action_sleeping = ACTION_DEFAULT;
3196 player->switch_x = -1;
3197 player->switch_y = -1;
3199 player->drop_x = -1;
3200 player->drop_y = -1;
3202 player->show_envelope = 0;
3204 SetPlayerMoveSpeed(player, level.initial_player_stepsize[i], TRUE);
3206 player->push_delay = -1; /* initialized when pushing starts */
3207 player->push_delay_value = game.initial_push_delay_value;
3209 player->drop_delay = 0;
3210 player->drop_pressed_delay = 0;
3212 player->last_jx = -1;
3213 player->last_jy = -1;
3217 player->shield_normal_time_left = 0;
3218 player->shield_deadly_time_left = 0;
3220 player->inventory_infinite_element = EL_UNDEFINED;
3221 player->inventory_size = 0;
3223 if (level.use_initial_inventory[i])
3225 for (j = 0; j < level.initial_inventory_size[i]; j++)
3227 int element = level.initial_inventory_content[i][j];
3228 int collect_count = element_info[element].collect_count_initial;
3231 if (!IS_CUSTOM_ELEMENT(element))
3234 if (collect_count == 0)
3235 player->inventory_infinite_element = element;
3237 for (k = 0; k < collect_count; k++)
3238 if (player->inventory_size < MAX_INVENTORY_SIZE)
3239 player->inventory_element[player->inventory_size++] = element;
3243 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
3244 SnapField(player, 0, 0);
3246 player->LevelSolved = FALSE;
3247 player->GameOver = FALSE;
3249 player->LevelSolved_GameWon = FALSE;
3250 player->LevelSolved_GameEnd = FALSE;
3251 player->LevelSolved_PanelOff = FALSE;
3252 player->LevelSolved_SaveTape = FALSE;
3253 player->LevelSolved_SaveScore = FALSE;
3254 player->LevelSolved_CountingTime = 0;
3255 player->LevelSolved_CountingScore = 0;
3257 map_player_action[i] = i;
3260 network_player_action_received = FALSE;
3262 #if defined(NETWORK_AVALIABLE)
3263 /* initial null action */
3264 if (network_playing)
3265 SendToServer_MovePlayer(MV_NONE);
3274 TimeLeft = level.time;
3277 ScreenMovDir = MV_NONE;
3281 ScrollStepSize = 0; /* will be correctly initialized by ScrollScreen() */
3283 AllPlayersGone = FALSE;
3285 game.no_time_limit = (level.time == 0);
3287 game.yamyam_content_nr = 0;
3288 game.robot_wheel_active = FALSE;
3289 game.magic_wall_active = FALSE;
3290 game.magic_wall_time_left = 0;
3291 game.light_time_left = 0;
3292 game.timegate_time_left = 0;
3293 game.switchgate_pos = 0;
3294 game.wind_direction = level.wind_direction_initial;
3296 game.lenses_time_left = 0;
3297 game.magnify_time_left = 0;
3299 game.ball_state = level.ball_state_initial;
3300 game.ball_content_nr = 0;
3302 game.envelope_active = FALSE;
3304 /* set focus to local player for network games, else to all players */
3305 game.centered_player_nr = (network_playing ? local_player->index_nr : -1);
3306 game.centered_player_nr_next = game.centered_player_nr;
3307 game.set_centered_player = FALSE;
3309 if (network_playing && tape.recording)
3311 /* store client dependent player focus when recording network games */
3312 tape.centered_player_nr_next = game.centered_player_nr_next;
3313 tape.set_centered_player = TRUE;
3316 for (i = 0; i < NUM_BELTS; i++)
3318 game.belt_dir[i] = MV_NONE;
3319 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
3322 for (i = 0; i < MAX_NUM_AMOEBA; i++)
3323 AmoebaCnt[i] = AmoebaCnt2[i] = 0;
3325 #if DEBUG_INIT_PLAYER
3328 printf("Player status at level initialization:\n");
3332 SCAN_PLAYFIELD(x, y)
3334 Feld[x][y] = level.field[x][y];
3335 MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
3336 ChangeDelay[x][y] = 0;
3337 ChangePage[x][y] = -1;
3338 CustomValue[x][y] = 0; /* initialized in InitField() */
3339 Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
3341 WasJustMoving[x][y] = 0;
3342 WasJustFalling[x][y] = 0;
3343 CheckCollision[x][y] = 0;
3344 CheckImpact[x][y] = 0;
3346 Pushed[x][y] = FALSE;
3348 ChangeCount[x][y] = 0;
3349 ChangeEvent[x][y] = -1;
3351 ExplodePhase[x][y] = 0;
3352 ExplodeDelay[x][y] = 0;
3353 ExplodeField[x][y] = EX_TYPE_NONE;
3355 RunnerVisit[x][y] = 0;
3356 PlayerVisit[x][y] = 0;
3359 GfxRandom[x][y] = INIT_GFX_RANDOM();
3360 GfxElement[x][y] = EL_UNDEFINED;
3361 GfxAction[x][y] = ACTION_DEFAULT;
3362 GfxDir[x][y] = MV_NONE;
3363 GfxRedraw[x][y] = GFX_REDRAW_NONE;
3366 SCAN_PLAYFIELD(x, y)
3368 if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
3370 if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
3372 if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
3375 InitField(x, y, TRUE);
3377 ResetGfxAnimation(x, y);
3382 for (i = 0; i < MAX_PLAYERS; i++)
3384 struct PlayerInfo *player = &stored_player[i];
3386 /* set number of special actions for bored and sleeping animation */
3387 player->num_special_action_bored =
3388 get_num_special_action(player->artwork_element,
3389 ACTION_BORING_1, ACTION_BORING_LAST);
3390 player->num_special_action_sleeping =
3391 get_num_special_action(player->artwork_element,
3392 ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
3395 game.emulation = (emulate_bd ? EMU_BOULDERDASH :
3396 emulate_sb ? EMU_SOKOBAN :
3397 emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
3399 /* initialize type of slippery elements */
3400 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3402 if (!IS_CUSTOM_ELEMENT(i))
3404 /* default: elements slip down either to the left or right randomly */
3405 element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
3407 /* SP style elements prefer to slip down on the left side */
3408 if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
3409 element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3411 /* BD style elements prefer to slip down on the left side */
3412 if (game.emulation == EMU_BOULDERDASH)
3413 element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3417 /* initialize explosion and ignition delay */
3418 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3420 if (!IS_CUSTOM_ELEMENT(i))
3423 int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
3424 game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
3425 game.emulation == EMU_SUPAPLEX ? 3 : 2);
3426 int last_phase = (num_phase + 1) * delay;
3427 int half_phase = (num_phase / 2) * delay;
3429 element_info[i].explosion_delay = last_phase - 1;
3430 element_info[i].ignition_delay = half_phase;
3432 if (i == EL_BLACK_ORB)
3433 element_info[i].ignition_delay = 1;
3437 /* correct non-moving belts to start moving left */
3438 for (i = 0; i < NUM_BELTS; i++)
3439 if (game.belt_dir[i] == MV_NONE)
3440 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
3442 #if USE_NEW_PLAYER_ASSIGNMENTS
3443 /* !!! SAME AS init.c:InitPlayerInfo() -- FIX THIS !!! */
3444 /* choose default local player */
3445 local_player = &stored_player[0];
3447 for (i = 0; i < MAX_PLAYERS; i++)
3448 stored_player[i].connected = FALSE;
3450 local_player->connected = TRUE;
3451 /* !!! SAME AS init.c:InitPlayerInfo() -- FIX THIS !!! */
3455 for (i = 0; i < MAX_PLAYERS; i++)
3456 stored_player[i].connected = tape.player_participates[i];
3458 else if (game.team_mode && !options.network)
3460 /* try to guess locally connected team mode players (needed for correct
3461 assignment of player figures from level to locally playing players) */
3463 for (i = 0; i < MAX_PLAYERS; i++)
3464 if (setup.input[i].use_joystick ||
3465 setup.input[i].key.left != KSYM_UNDEFINED)
3466 stored_player[i].connected = TRUE;
3469 #if DEBUG_INIT_PLAYER
3472 printf("Player status after level initialization:\n");
3474 for (i = 0; i < MAX_PLAYERS; i++)
3476 struct PlayerInfo *player = &stored_player[i];
3478 printf("- player %d: present == %d, connected == %d, active == %d",
3484 if (local_player == player)
3485 printf(" (local player)");
3492 #if DEBUG_INIT_PLAYER
3494 printf("Reassigning players ...\n");
3497 /* check if any connected player was not found in playfield */
3498 for (i = 0; i < MAX_PLAYERS; i++)
3500 struct PlayerInfo *player = &stored_player[i];
3502 if (player->connected && !player->present)
3504 struct PlayerInfo *field_player = NULL;
3506 #if DEBUG_INIT_PLAYER
3508 printf("- looking for field player for player %d ...\n", i + 1);
3511 /* assign first free player found that is present in the playfield */
3513 /* first try: look for unmapped playfield player that is not connected */
3514 for (j = 0; j < MAX_PLAYERS; j++)
3515 if (field_player == NULL &&
3516 stored_player[j].present &&
3517 !stored_player[j].mapped &&
3518 !stored_player[j].connected)
3519 field_player = &stored_player[j];
3521 /* second try: look for *any* unmapped playfield player */
3522 for (j = 0; j < MAX_PLAYERS; j++)
3523 if (field_player == NULL &&
3524 stored_player[j].present &&
3525 !stored_player[j].mapped)
3526 field_player = &stored_player[j];
3528 if (field_player != NULL)
3530 int jx = field_player->jx, jy = field_player->jy;
3532 #if DEBUG_INIT_PLAYER
3534 printf("- found player %d\n", field_player->index_nr + 1);
3537 player->present = FALSE;
3538 player->active = FALSE;
3540 field_player->present = TRUE;
3541 field_player->active = TRUE;
3544 player->initial_element = field_player->initial_element;
3545 player->artwork_element = field_player->artwork_element;
3547 player->block_last_field = field_player->block_last_field;
3548 player->block_delay_adjustment = field_player->block_delay_adjustment;
3551 StorePlayer[jx][jy] = field_player->element_nr;
3553 field_player->jx = field_player->last_jx = jx;
3554 field_player->jy = field_player->last_jy = jy;
3556 if (local_player == player)
3557 local_player = field_player;
3559 map_player_action[field_player->index_nr] = i;
3561 field_player->mapped = TRUE;
3563 #if DEBUG_INIT_PLAYER
3565 printf("- map_player_action[%d] == %d\n",
3566 field_player->index_nr + 1, i + 1);
3571 if (player->connected && player->present)
3572 player->mapped = TRUE;
3575 #if DEBUG_INIT_PLAYER
3578 printf("Player status after player assignment (first stage):\n");
3580 for (i = 0; i < MAX_PLAYERS; i++)
3582 struct PlayerInfo *player = &stored_player[i];
3584 printf("- player %d: present == %d, connected == %d, active == %d",
3590 if (local_player == player)
3591 printf(" (local player)");
3600 /* check if any connected player was not found in playfield */
3601 for (i = 0; i < MAX_PLAYERS; i++)
3603 struct PlayerInfo *player = &stored_player[i];
3605 if (player->connected && !player->present)
3607 for (j = 0; j < MAX_PLAYERS; j++)
3609 struct PlayerInfo *field_player = &stored_player[j];
3610 int jx = field_player->jx, jy = field_player->jy;
3612 /* assign first free player found that is present in the playfield */
3613 if (field_player->present && !field_player->connected)
3615 player->present = TRUE;
3616 player->active = TRUE;
3618 field_player->present = FALSE;
3619 field_player->active = FALSE;
3621 player->initial_element = field_player->initial_element;
3622 player->artwork_element = field_player->artwork_element;
3624 player->block_last_field = field_player->block_last_field;
3625 player->block_delay_adjustment = field_player->block_delay_adjustment;
3627 StorePlayer[jx][jy] = player->element_nr;
3629 player->jx = player->last_jx = jx;
3630 player->jy = player->last_jy = jy;
3640 printf("::: local_player->present == %d\n", local_player->present);
3645 /* when playing a tape, eliminate all players who do not participate */
3647 #if USE_NEW_PLAYER_ASSIGNMENTS
3649 if (!game.team_mode)
3651 for (i = 0; i < MAX_PLAYERS; i++)
3653 if (stored_player[i].active &&
3654 !tape.player_participates[map_player_action[i]])
3656 struct PlayerInfo *player = &stored_player[i];
3657 int jx = player->jx, jy = player->jy;
3659 #if DEBUG_INIT_PLAYER
3661 printf("Removing player %d at (%d, %d)\n", i + 1, jx, jy);
3664 player->active = FALSE;
3665 StorePlayer[jx][jy] = 0;
3666 Feld[jx][jy] = EL_EMPTY;
3673 for (i = 0; i < MAX_PLAYERS; i++)
3675 if (stored_player[i].active &&
3676 !tape.player_participates[i])
3678 struct PlayerInfo *player = &stored_player[i];
3679 int jx = player->jx, jy = player->jy;
3681 player->active = FALSE;
3682 StorePlayer[jx][jy] = 0;
3683 Feld[jx][jy] = EL_EMPTY;
3688 else if (!options.network && !game.team_mode) /* && !tape.playing */
3690 /* when in single player mode, eliminate all but the first active player */
3692 for (i = 0; i < MAX_PLAYERS; i++)
3694 if (stored_player[i].active)
3696 for (j = i + 1; j < MAX_PLAYERS; j++)
3698 if (stored_player[j].active)
3700 struct PlayerInfo *player = &stored_player[j];
3701 int jx = player->jx, jy = player->jy;
3703 player->active = FALSE;
3704 player->present = FALSE;
3706 StorePlayer[jx][jy] = 0;
3707 Feld[jx][jy] = EL_EMPTY;
3714 /* when recording the game, store which players take part in the game */
3717 #if USE_NEW_PLAYER_ASSIGNMENTS
3718 for (i = 0; i < MAX_PLAYERS; i++)
3719 if (stored_player[i].connected)
3720 tape.player_participates[i] = TRUE;
3722 for (i = 0; i < MAX_PLAYERS; i++)
3723 if (stored_player[i].active)
3724 tape.player_participates[i] = TRUE;
3728 #if DEBUG_INIT_PLAYER
3731 printf("Player status after player assignment (final stage):\n");
3733 for (i = 0; i < MAX_PLAYERS; i++)
3735 struct PlayerInfo *player = &stored_player[i];
3737 printf("- player %d: present == %d, connected == %d, active == %d",
3743 if (local_player == player)
3744 printf(" (local player)");
3751 if (BorderElement == EL_EMPTY)
3754 SBX_Right = lev_fieldx - SCR_FIELDX;
3756 SBY_Lower = lev_fieldy - SCR_FIELDY;
3761 SBX_Right = lev_fieldx - SCR_FIELDX + 1;
3763 SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
3766 if (full_lev_fieldx <= SCR_FIELDX)
3767 SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
3768 if (full_lev_fieldy <= SCR_FIELDY)
3769 SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
3771 if (EVEN(SCR_FIELDX) && full_lev_fieldx > SCR_FIELDX)
3773 if (EVEN(SCR_FIELDY) && full_lev_fieldy > SCR_FIELDY)
3776 /* if local player not found, look for custom element that might create
3777 the player (make some assumptions about the right custom element) */
3778 if (!local_player->present)
3780 int start_x = 0, start_y = 0;
3781 int found_rating = 0;
3782 int found_element = EL_UNDEFINED;
3783 int player_nr = local_player->index_nr;
3785 SCAN_PLAYFIELD(x, y)
3787 int element = Feld[x][y];
3792 if (level.use_start_element[player_nr] &&
3793 level.start_element[player_nr] == element &&
3800 found_element = element;
3803 if (!IS_CUSTOM_ELEMENT(element))
3806 if (CAN_CHANGE(element))
3808 for (i = 0; i < element_info[element].num_change_pages; i++)
3810 /* check for player created from custom element as single target */
3811 content = element_info[element].change_page[i].target_element;
3812 is_player = ELEM_IS_PLAYER(content);
3814 if (is_player && (found_rating < 3 ||
3815 (found_rating == 3 && element < found_element)))
3821 found_element = element;
3826 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
3828 /* check for player created from custom element as explosion content */
3829 content = element_info[element].content.e[xx][yy];
3830 is_player = ELEM_IS_PLAYER(content);
3832 if (is_player && (found_rating < 2 ||
3833 (found_rating == 2 && element < found_element)))
3835 start_x = x + xx - 1;
3836 start_y = y + yy - 1;
3839 found_element = element;
3842 if (!CAN_CHANGE(element))
3845 for (i = 0; i < element_info[element].num_change_pages; i++)
3847 /* check for player created from custom element as extended target */
3849 element_info[element].change_page[i].target_content.e[xx][yy];
3851 is_player = ELEM_IS_PLAYER(content);
3853 if (is_player && (found_rating < 1 ||
3854 (found_rating == 1 && element < found_element)))
3856 start_x = x + xx - 1;
3857 start_y = y + yy - 1;
3860 found_element = element;
3866 scroll_x = (start_x < SBX_Left + MIDPOSX ? SBX_Left :
3867 start_x > SBX_Right + MIDPOSX ? SBX_Right :
3870 scroll_y = (start_y < SBY_Upper + MIDPOSY ? SBY_Upper :
3871 start_y > SBY_Lower + MIDPOSY ? SBY_Lower :
3876 scroll_x = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
3877 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
3878 local_player->jx - MIDPOSX);
3880 scroll_y = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
3881 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
3882 local_player->jy - MIDPOSY);
3885 /* !!! FIX THIS (START) !!! */
3886 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
3888 InitGameEngine_EM();
3890 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
3892 InitGameEngine_SP();
3896 DrawLevel(REDRAW_FIELD);
3899 /* after drawing the level, correct some elements */
3900 if (game.timegate_time_left == 0)
3901 CloseAllOpenTimegates();
3904 /* blit playfield from scroll buffer to normal back buffer for fading in */
3905 BlitScreenToBitmap(backbuffer);
3907 redraw_mask |= REDRAW_FROM_BACKBUFFER;
3908 /* !!! FIX THIS (END) !!! */
3910 FadeIn(REDRAW_FIELD);
3912 if (!game.restart_level)
3914 /* copy default game door content to main double buffer */
3916 /* !!! CHECK AGAIN !!! */
3917 SetPanelBackground();
3918 // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
3919 DrawBackground(DX, DY, DXSIZE, DYSIZE);
3922 SetPanelBackground();
3923 SetDrawBackgroundMask(REDRAW_DOOR_1);
3925 UpdateAndDisplayGameControlValues();
3927 if (!game.restart_level)
3931 game_gadget[SOUND_CTRL_ID_MUSIC]->checked = setup.sound_music;
3932 game_gadget[SOUND_CTRL_ID_LOOPS]->checked = setup.sound_loops;
3933 game_gadget[SOUND_CTRL_ID_SIMPLE]->checked = setup.sound_simple;
3937 /* copy actual game door content to door double buffer for OpenDoor() */
3938 BlitBitmap(drawto, bitmap_db_door_1, DX, DY, DXSIZE, DYSIZE, 0, 0);
3940 OpenDoor(DOOR_OPEN_ALL);
3942 PlaySound(SND_GAME_STARTING);
3944 if (setup.sound_music)
3947 KeyboardAutoRepeatOffUnlessAutoplay();
3949 #if DEBUG_INIT_PLAYER
3952 printf("Player status (final):\n");
3954 for (i = 0; i < MAX_PLAYERS; i++)
3956 struct PlayerInfo *player = &stored_player[i];
3958 printf("- player %d: present == %d, connected == %d, active == %d",
3964 if (local_player == player)
3965 printf(" (local player)");
3978 if (!game.restart_level && !tape.playing)
3980 LevelStats_incPlayed(level_nr);
3982 SaveLevelSetup_SeriesInfo();
3985 game.restart_level = FALSE;
3988 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y)
3990 /* this is used for non-R'n'D game engines to update certain engine values */
3992 /* needed to determine if sounds are played within the visible screen area */
3993 scroll_x = actual_scroll_x;
3994 scroll_y = actual_scroll_y;
3997 void InitMovDir(int x, int y)
3999 int i, element = Feld[x][y];
4000 static int xy[4][2] =
4007 static int direction[3][4] =
4009 { MV_RIGHT, MV_UP, MV_LEFT, MV_DOWN },
4010 { MV_LEFT, MV_DOWN, MV_RIGHT, MV_UP },
4011 { MV_LEFT, MV_RIGHT, MV_UP, MV_DOWN }
4020 Feld[x][y] = EL_BUG;
4021 MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
4024 case EL_SPACESHIP_RIGHT:
4025 case EL_SPACESHIP_UP:
4026 case EL_SPACESHIP_LEFT:
4027 case EL_SPACESHIP_DOWN:
4028 Feld[x][y] = EL_SPACESHIP;
4029 MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
4032 case EL_BD_BUTTERFLY_RIGHT:
4033 case EL_BD_BUTTERFLY_UP:
4034 case EL_BD_BUTTERFLY_LEFT:
4035 case EL_BD_BUTTERFLY_DOWN:
4036 Feld[x][y] = EL_BD_BUTTERFLY;
4037 MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
4040 case EL_BD_FIREFLY_RIGHT:
4041 case EL_BD_FIREFLY_UP:
4042 case EL_BD_FIREFLY_LEFT:
4043 case EL_BD_FIREFLY_DOWN:
4044 Feld[x][y] = EL_BD_FIREFLY;
4045 MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
4048 case EL_PACMAN_RIGHT:
4050 case EL_PACMAN_LEFT:
4051 case EL_PACMAN_DOWN:
4052 Feld[x][y] = EL_PACMAN;
4053 MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
4056 case EL_YAMYAM_LEFT:
4057 case EL_YAMYAM_RIGHT:
4059 case EL_YAMYAM_DOWN:
4060 Feld[x][y] = EL_YAMYAM;
4061 MovDir[x][y] = direction[2][element - EL_YAMYAM_LEFT];
4064 case EL_SP_SNIKSNAK:
4065 MovDir[x][y] = MV_UP;
4068 case EL_SP_ELECTRON:
4069 MovDir[x][y] = MV_LEFT;
4076 Feld[x][y] = EL_MOLE;
4077 MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
4081 if (IS_CUSTOM_ELEMENT(element))
4083 struct ElementInfo *ei = &element_info[element];
4084 int move_direction_initial = ei->move_direction_initial;
4085 int move_pattern = ei->move_pattern;
4087 if (move_direction_initial == MV_START_PREVIOUS)
4089 if (MovDir[x][y] != MV_NONE)
4092 move_direction_initial = MV_START_AUTOMATIC;
4095 if (move_direction_initial == MV_START_RANDOM)
4096 MovDir[x][y] = 1 << RND(4);
4097 else if (move_direction_initial & MV_ANY_DIRECTION)
4098 MovDir[x][y] = move_direction_initial;
4099 else if (move_pattern == MV_ALL_DIRECTIONS ||
4100 move_pattern == MV_TURNING_LEFT ||
4101 move_pattern == MV_TURNING_RIGHT ||
4102 move_pattern == MV_TURNING_LEFT_RIGHT ||
4103 move_pattern == MV_TURNING_RIGHT_LEFT ||
4104 move_pattern == MV_TURNING_RANDOM)
4105 MovDir[x][y] = 1 << RND(4);
4106 else if (move_pattern == MV_HORIZONTAL)
4107 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
4108 else if (move_pattern == MV_VERTICAL)
4109 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
4110 else if (move_pattern & MV_ANY_DIRECTION)
4111 MovDir[x][y] = element_info[element].move_pattern;
4112 else if (move_pattern == MV_ALONG_LEFT_SIDE ||
4113 move_pattern == MV_ALONG_RIGHT_SIDE)
4115 /* use random direction as default start direction */
4116 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
4117 MovDir[x][y] = 1 << RND(4);
4119 for (i = 0; i < NUM_DIRECTIONS; i++)
4121 int x1 = x + xy[i][0];
4122 int y1 = y + xy[i][1];
4124 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4126 if (move_pattern == MV_ALONG_RIGHT_SIDE)
4127 MovDir[x][y] = direction[0][i];
4129 MovDir[x][y] = direction[1][i];
4138 MovDir[x][y] = 1 << RND(4);
4140 if (element != EL_BUG &&
4141 element != EL_SPACESHIP &&
4142 element != EL_BD_BUTTERFLY &&
4143 element != EL_BD_FIREFLY)
4146 for (i = 0; i < NUM_DIRECTIONS; i++)
4148 int x1 = x + xy[i][0];
4149 int y1 = y + xy[i][1];
4151 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4153 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
4155 MovDir[x][y] = direction[0][i];
4158 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
4159 element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4161 MovDir[x][y] = direction[1][i];
4170 GfxDir[x][y] = MovDir[x][y];
4173 void InitAmoebaNr(int x, int y)
4176 int group_nr = AmoebeNachbarNr(x, y);
4180 for (i = 1; i < MAX_NUM_AMOEBA; i++)
4182 if (AmoebaCnt[i] == 0)
4190 AmoebaNr[x][y] = group_nr;
4191 AmoebaCnt[group_nr]++;
4192 AmoebaCnt2[group_nr]++;
4195 static void PlayerWins(struct PlayerInfo *player)
4197 player->LevelSolved = TRUE;
4198 player->GameOver = TRUE;
4200 player->score_final = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
4201 level.native_em_level->lev->score : player->score);
4203 player->LevelSolved_CountingTime = (game.no_time_limit ? TimePlayed :
4205 player->LevelSolved_CountingScore = player->score_final;
4210 static int time, time_final;
4211 static int score, score_final;
4212 static int game_over_delay_1 = 0;
4213 static int game_over_delay_2 = 0;
4214 int game_over_delay_value_1 = 50;
4215 int game_over_delay_value_2 = 50;
4217 if (!local_player->LevelSolved_GameWon)
4221 /* do not start end game actions before the player stops moving (to exit) */
4222 if (local_player->MovPos)
4225 local_player->LevelSolved_GameWon = TRUE;
4226 local_player->LevelSolved_SaveTape = tape.recording;
4227 local_player->LevelSolved_SaveScore = !tape.playing;
4231 LevelStats_incSolved(level_nr);
4233 SaveLevelSetup_SeriesInfo();
4236 if (tape.auto_play) /* tape might already be stopped here */
4237 tape.auto_play_level_solved = TRUE;
4241 game_over_delay_1 = game_over_delay_value_1;
4242 game_over_delay_2 = game_over_delay_value_2;
4244 time = time_final = (game.no_time_limit ? TimePlayed : TimeLeft);
4245 score = score_final = local_player->score_final;
4250 score_final += TimeLeft * level.score[SC_TIME_BONUS];
4252 else if (game.no_time_limit && TimePlayed < 999)
4255 score_final += (999 - TimePlayed) * level.score[SC_TIME_BONUS];
4258 local_player->score_final = score_final;
4260 if (level_editor_test_game)
4263 score = score_final;
4265 local_player->LevelSolved_CountingTime = time;
4266 local_player->LevelSolved_CountingScore = score;
4268 game_panel_controls[GAME_PANEL_TIME].value = time;
4269 game_panel_controls[GAME_PANEL_SCORE].value = score;
4271 DisplayGameControlValues();
4274 if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
4276 if (ExitX >= 0 && ExitY >= 0) /* local player has left the level */
4278 /* close exit door after last player */
4279 if ((AllPlayersGone &&
4280 (Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
4281 Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN ||
4282 Feld[ExitX][ExitY] == EL_STEEL_EXIT_OPEN)) ||
4283 Feld[ExitX][ExitY] == EL_EM_EXIT_OPEN ||
4284 Feld[ExitX][ExitY] == EL_EM_STEEL_EXIT_OPEN)
4286 int element = Feld[ExitX][ExitY];
4288 Feld[ExitX][ExitY] =
4289 (element == EL_EXIT_OPEN ? EL_EXIT_CLOSING :
4290 element == EL_EM_EXIT_OPEN ? EL_EM_EXIT_CLOSING :
4291 element == EL_SP_EXIT_OPEN ? EL_SP_EXIT_CLOSING:
4292 element == EL_STEEL_EXIT_OPEN ? EL_STEEL_EXIT_CLOSING:
4293 EL_EM_STEEL_EXIT_CLOSING);
4295 PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING);
4298 /* player disappears */
4299 DrawLevelField(ExitX, ExitY);
4302 for (i = 0; i < MAX_PLAYERS; i++)
4304 struct PlayerInfo *player = &stored_player[i];
4306 if (player->present)
4308 RemovePlayer(player);
4310 /* player disappears */
4311 DrawLevelField(player->jx, player->jy);
4316 PlaySound(SND_GAME_WINNING);
4319 if (game_over_delay_1 > 0)
4321 game_over_delay_1--;
4326 if (time != time_final)
4328 int time_to_go = ABS(time_final - time);
4329 int time_count_dir = (time < time_final ? +1 : -1);
4330 int time_count_steps = (time_to_go > 100 && time_to_go % 10 == 0 ? 10 : 1);
4332 time += time_count_steps * time_count_dir;
4333 score += time_count_steps * level.score[SC_TIME_BONUS];
4335 local_player->LevelSolved_CountingTime = time;
4336 local_player->LevelSolved_CountingScore = score;
4338 game_panel_controls[GAME_PANEL_TIME].value = time;
4339 game_panel_controls[GAME_PANEL_SCORE].value = score;
4341 DisplayGameControlValues();
4343 if (time == time_final)
4344 StopSound(SND_GAME_LEVELTIME_BONUS);
4345 else if (setup.sound_loops)
4346 PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4348 PlaySound(SND_GAME_LEVELTIME_BONUS);
4353 local_player->LevelSolved_PanelOff = TRUE;
4355 if (game_over_delay_2 > 0)
4357 game_over_delay_2--;
4368 boolean raise_level = FALSE;
4370 local_player->LevelSolved_GameEnd = TRUE;
4372 CloseDoor(DOOR_CLOSE_1);
4374 if (local_player->LevelSolved_SaveTape)
4376 SaveTapeChecked(tape.level_nr); /* ask to save tape */
4379 if (level_editor_test_game)
4381 game_status = GAME_MODE_MAIN;
4383 DrawAndFadeInMainMenu(REDRAW_FIELD);
4388 if (!local_player->LevelSolved_SaveScore)
4390 FadeOut(REDRAW_FIELD);
4392 game_status = GAME_MODE_MAIN;
4394 DrawAndFadeInMainMenu(REDRAW_FIELD);
4399 if (level_nr == leveldir_current->handicap_level)
4401 leveldir_current->handicap_level++;
4403 SaveLevelSetup_SeriesInfo();
4406 if (level_nr < leveldir_current->last_level)
4407 raise_level = TRUE; /* advance to next level */
4409 if ((hi_pos = NewHiScore()) >= 0)
4411 game_status = GAME_MODE_SCORES;
4413 DrawHallOfFame(hi_pos);
4423 FadeOut(REDRAW_FIELD);
4425 game_status = GAME_MODE_MAIN;
4433 DrawAndFadeInMainMenu(REDRAW_FIELD);
4442 LoadScore(level_nr);
4444 if (strEqual(setup.player_name, EMPTY_PLAYER_NAME) ||
4445 local_player->score_final < highscore[MAX_SCORE_ENTRIES - 1].Score)
4448 for (k = 0; k < MAX_SCORE_ENTRIES; k++)
4450 if (local_player->score_final > highscore[k].Score)
4452 /* player has made it to the hall of fame */
4454 if (k < MAX_SCORE_ENTRIES - 1)
4456 int m = MAX_SCORE_ENTRIES - 1;
4459 for (l = k; l < MAX_SCORE_ENTRIES; l++)
4460 if (strEqual(setup.player_name, highscore[l].Name))
4462 if (m == k) /* player's new highscore overwrites his old one */
4466 for (l = m; l > k; l--)
4468 strcpy(highscore[l].Name, highscore[l - 1].Name);
4469 highscore[l].Score = highscore[l - 1].Score;
4476 strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
4477 highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
4478 highscore[k].Score = local_player->score_final;
4484 else if (!strncmp(setup.player_name, highscore[k].Name,
4485 MAX_PLAYER_NAME_LEN))
4486 break; /* player already there with a higher score */
4492 SaveScore(level_nr);
4497 inline static int getElementMoveStepsizeExt(int x, int y, int direction)
4499 int element = Feld[x][y];
4500 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
4501 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
4502 int horiz_move = (dx != 0);
4503 int sign = (horiz_move ? dx : dy);
4504 int step = sign * element_info[element].move_stepsize;
4506 /* special values for move stepsize for spring and things on conveyor belt */
4509 if (CAN_FALL(element) &&
4510 y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
4511 step = sign * MOVE_STEPSIZE_NORMAL / 2;
4512 else if (element == EL_SPRING)
4513 step = sign * MOVE_STEPSIZE_NORMAL * 2;
4519 inline static int getElementMoveStepsize(int x, int y)
4521 return getElementMoveStepsizeExt(x, y, MovDir[x][y]);
4524 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
4526 if (player->GfxAction != action || player->GfxDir != dir)
4528 player->GfxAction = action;
4529 player->GfxDir = dir;
4531 player->StepFrame = 0;
4535 static void ResetGfxFrame(int x, int y, boolean redraw)
4537 int element = Feld[x][y];
4538 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
4539 int last_gfx_frame = GfxFrame[x][y];
4541 if (graphic_info[graphic].anim_global_sync)
4542 GfxFrame[x][y] = FrameCounter;
4543 else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
4544 GfxFrame[x][y] = CustomValue[x][y];
4545 else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
4546 GfxFrame[x][y] = element_info[element].collect_score;
4547 else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
4548 GfxFrame[x][y] = ChangeDelay[x][y];
4550 if (redraw && GfxFrame[x][y] != last_gfx_frame)
4551 DrawLevelGraphicAnimation(x, y, graphic);
4554 static void ResetGfxAnimation(int x, int y)
4556 GfxAction[x][y] = ACTION_DEFAULT;
4557 GfxDir[x][y] = MovDir[x][y];
4560 ResetGfxFrame(x, y, FALSE);
4563 static void ResetRandomAnimationValue(int x, int y)
4565 GfxRandom[x][y] = INIT_GFX_RANDOM();
4568 void InitMovingField(int x, int y, int direction)
4570 int element = Feld[x][y];
4571 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
4572 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
4575 boolean is_moving_before, is_moving_after;
4577 /* check if element was/is moving or being moved before/after mode change */
4578 is_moving_before = (WasJustMoving[x][y] != 0);
4579 is_moving_after = (getElementMoveStepsizeExt(x, y, direction) != 0);
4581 /* reset animation only for moving elements which change direction of moving
4582 or which just started or stopped moving
4583 (else CEs with property "can move" / "not moving" are reset each frame) */
4584 if (is_moving_before != is_moving_after ||
4585 direction != MovDir[x][y])
4586 ResetGfxAnimation(x, y);
4588 MovDir[x][y] = direction;
4589 GfxDir[x][y] = direction;
4591 GfxAction[x][y] = (!is_moving_after ? ACTION_WAITING :
4592 direction == MV_DOWN && CAN_FALL(element) ?
4593 ACTION_FALLING : ACTION_MOVING);
4595 /* this is needed for CEs with property "can move" / "not moving" */
4597 if (is_moving_after)
4599 if (Feld[newx][newy] == EL_EMPTY)
4600 Feld[newx][newy] = EL_BLOCKED;
4602 MovDir[newx][newy] = MovDir[x][y];
4604 CustomValue[newx][newy] = CustomValue[x][y];
4606 GfxFrame[newx][newy] = GfxFrame[x][y];
4607 GfxRandom[newx][newy] = GfxRandom[x][y];
4608 GfxAction[newx][newy] = GfxAction[x][y];
4609 GfxDir[newx][newy] = GfxDir[x][y];
4613 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
4615 int direction = MovDir[x][y];
4616 int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0);
4617 int newy = y + (direction & MV_UP ? -1 : direction & MV_DOWN ? +1 : 0);
4623 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
4625 int oldx = x, oldy = y;
4626 int direction = MovDir[x][y];
4628 if (direction == MV_LEFT)
4630 else if (direction == MV_RIGHT)
4632 else if (direction == MV_UP)
4634 else if (direction == MV_DOWN)
4637 *comes_from_x = oldx;
4638 *comes_from_y = oldy;
4641 int MovingOrBlocked2Element(int x, int y)
4643 int element = Feld[x][y];
4645 if (element == EL_BLOCKED)
4649 Blocked2Moving(x, y, &oldx, &oldy);
4650 return Feld[oldx][oldy];
4656 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
4658 /* like MovingOrBlocked2Element(), but if element is moving
4659 and (x,y) is the field the moving element is just leaving,
4660 return EL_BLOCKED instead of the element value */
4661 int element = Feld[x][y];
4663 if (IS_MOVING(x, y))
4665 if (element == EL_BLOCKED)
4669 Blocked2Moving(x, y, &oldx, &oldy);
4670 return Feld[oldx][oldy];
4679 static void RemoveField(int x, int y)
4681 Feld[x][y] = EL_EMPTY;
4687 CustomValue[x][y] = 0;
4690 ChangeDelay[x][y] = 0;
4691 ChangePage[x][y] = -1;
4692 Pushed[x][y] = FALSE;
4694 GfxElement[x][y] = EL_UNDEFINED;
4695 GfxAction[x][y] = ACTION_DEFAULT;
4696 GfxDir[x][y] = MV_NONE;
4699 void RemoveMovingField(int x, int y)
4701 int oldx = x, oldy = y, newx = x, newy = y;
4702 int element = Feld[x][y];
4703 int next_element = EL_UNDEFINED;
4705 if (element != EL_BLOCKED && !IS_MOVING(x, y))
4708 if (IS_MOVING(x, y))
4710 Moving2Blocked(x, y, &newx, &newy);
4712 if (Feld[newx][newy] != EL_BLOCKED)
4714 /* element is moving, but target field is not free (blocked), but
4715 already occupied by something different (example: acid pool);
4716 in this case, only remove the moving field, but not the target */
4718 RemoveField(oldx, oldy);
4720 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
4722 TEST_DrawLevelField(oldx, oldy);
4727 else if (element == EL_BLOCKED)
4729 Blocked2Moving(x, y, &oldx, &oldy);
4730 if (!IS_MOVING(oldx, oldy))
4734 if (element == EL_BLOCKED &&
4735 (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
4736 Feld[oldx][oldy] == EL_QUICKSAND_FAST_EMPTYING ||
4737 Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
4738 Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
4739 Feld[oldx][oldy] == EL_DC_MAGIC_WALL_EMPTYING ||
4740 Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
4741 next_element = get_next_element(Feld[oldx][oldy]);
4743 RemoveField(oldx, oldy);
4744 RemoveField(newx, newy);
4746 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
4748 if (next_element != EL_UNDEFINED)
4749 Feld[oldx][oldy] = next_element;
4751 TEST_DrawLevelField(oldx, oldy);
4752 TEST_DrawLevelField(newx, newy);
4755 void DrawDynamite(int x, int y)
4757 int sx = SCREENX(x), sy = SCREENY(y);
4758 int graphic = el2img(Feld[x][y]);
4761 if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
4764 if (IS_WALKABLE_INSIDE(Back[x][y]))
4768 DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
4769 else if (Store[x][y])
4770 DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
4772 frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
4774 if (Back[x][y] || Store[x][y])
4775 DrawGraphicThruMask(sx, sy, graphic, frame);
4777 DrawGraphic(sx, sy, graphic, frame);
4780 void CheckDynamite(int x, int y)
4782 if (MovDelay[x][y] != 0) /* dynamite is still waiting to explode */
4786 if (MovDelay[x][y] != 0)
4789 PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
4795 StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
4800 static void setMinimalPlayerBoundaries(int *sx1, int *sy1, int *sx2, int *sy2)
4802 boolean num_checked_players = 0;
4805 for (i = 0; i < MAX_PLAYERS; i++)
4807 if (stored_player[i].active)
4809 int sx = stored_player[i].jx;
4810 int sy = stored_player[i].jy;
4812 if (num_checked_players == 0)
4819 *sx1 = MIN(*sx1, sx);
4820 *sy1 = MIN(*sy1, sy);
4821 *sx2 = MAX(*sx2, sx);
4822 *sy2 = MAX(*sy2, sy);
4825 num_checked_players++;
4830 static boolean checkIfAllPlayersFitToScreen_RND()
4832 int sx1 = 0, sy1 = 0, sx2 = 0, sy2 = 0;
4834 setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
4836 return (sx2 - sx1 < SCR_FIELDX &&
4837 sy2 - sy1 < SCR_FIELDY);
4840 static void setScreenCenteredToAllPlayers(int *sx, int *sy)
4842 int sx1 = scroll_x, sy1 = scroll_y, sx2 = scroll_x, sy2 = scroll_y;
4844 setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
4846 *sx = (sx1 + sx2) / 2;
4847 *sy = (sy1 + sy2) / 2;
4850 void DrawRelocateScreen(int old_x, int old_y, int x, int y, int move_dir,
4851 boolean center_screen, boolean quick_relocation)
4853 boolean ffwd_delay = (tape.playing && tape.fast_forward);
4854 boolean no_delay = (tape.warp_forward);
4855 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
4856 int wait_delay_value = (no_delay ? 0 : frame_delay_value);
4858 if (quick_relocation)
4860 if (!IN_VIS_FIELD(SCREENX(x), SCREENY(y)) || center_screen)
4862 if (!level.shifted_relocation || center_screen)
4864 /* quick relocation (without scrolling), with centering of screen */
4866 scroll_x = (x < SBX_Left + MIDPOSX ? SBX_Left :
4867 x > SBX_Right + MIDPOSX ? SBX_Right :
4870 scroll_y = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
4871 y > SBY_Lower + MIDPOSY ? SBY_Lower :
4876 /* quick relocation (without scrolling), but do not center screen */
4878 int center_scroll_x = (old_x < SBX_Left + MIDPOSX ? SBX_Left :
4879 old_x > SBX_Right + MIDPOSX ? SBX_Right :
4882 int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
4883 old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
4886 int offset_x = x + (scroll_x - center_scroll_x);
4887 int offset_y = y + (scroll_y - center_scroll_y);
4889 scroll_x = (offset_x < SBX_Left + MIDPOSX ? SBX_Left :
4890 offset_x > SBX_Right + MIDPOSX ? SBX_Right :
4891 offset_x - MIDPOSX);
4893 scroll_y = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
4894 offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
4895 offset_y - MIDPOSY);
4900 if (!level.shifted_relocation || center_screen)
4902 /* quick relocation (without scrolling), with centering of screen */
4904 scroll_x = (x < SBX_Left + MIDPOSX ? SBX_Left :
4905 x > SBX_Right + MIDPOSX ? SBX_Right :
4908 scroll_y = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
4909 y > SBY_Lower + MIDPOSY ? SBY_Lower :
4914 /* quick relocation (without scrolling), but do not center screen */
4916 int center_scroll_x = (old_x < SBX_Left + MIDPOSX ? SBX_Left :
4917 old_x > SBX_Right + MIDPOSX ? SBX_Right :
4920 int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
4921 old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
4924 int offset_x = x + (scroll_x - center_scroll_x);
4925 int offset_y = y + (scroll_y - center_scroll_y);
4927 scroll_x = (offset_x < SBX_Left + MIDPOSX ? SBX_Left :
4928 offset_x > SBX_Right + MIDPOSX ? SBX_Right :
4929 offset_x - MIDPOSX);
4931 scroll_y = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
4932 offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
4933 offset_y - MIDPOSY);
4937 RedrawPlayfield(TRUE, 0,0,0,0);
4941 int scroll_xx, scroll_yy;
4943 if (!level.shifted_relocation || center_screen)
4945 /* visible relocation (with scrolling), with centering of screen */
4947 scroll_xx = (x < SBX_Left + MIDPOSX ? SBX_Left :
4948 x > SBX_Right + MIDPOSX ? SBX_Right :
4951 scroll_yy = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
4952 y > SBY_Lower + MIDPOSY ? SBY_Lower :
4957 /* visible relocation (with scrolling), but do not center screen */
4959 int center_scroll_x = (old_x < SBX_Left + MIDPOSX ? SBX_Left :
4960 old_x > SBX_Right + MIDPOSX ? SBX_Right :
4963 int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
4964 old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
4967 int offset_x = x + (scroll_x - center_scroll_x);
4968 int offset_y = y + (scroll_y - center_scroll_y);
4970 scroll_xx = (offset_x < SBX_Left + MIDPOSX ? SBX_Left :
4971 offset_x > SBX_Right + MIDPOSX ? SBX_Right :
4972 offset_x - MIDPOSX);
4974 scroll_yy = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
4975 offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
4976 offset_y - MIDPOSY);
4980 ScrollScreen(NULL, SCROLL_GO_ON); /* scroll last frame to full tile */
4982 while (scroll_x != scroll_xx || scroll_y != scroll_yy)
4985 int fx = FX, fy = FY;
4987 dx = (scroll_xx < scroll_x ? +1 : scroll_xx > scroll_x ? -1 : 0);
4988 dy = (scroll_yy < scroll_y ? +1 : scroll_yy > scroll_y ? -1 : 0);
4990 if (dx == 0 && dy == 0) /* no scrolling needed at all */
4996 fx += dx * TILEX / 2;
4997 fy += dy * TILEY / 2;
4999 ScrollLevel(dx, dy);
5002 /* scroll in two steps of half tile size to make things smoother */
5003 BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
5004 Delay(wait_delay_value);
5006 /* scroll second step to align at full tile size */
5008 Delay(wait_delay_value);
5013 Delay(wait_delay_value);
5017 void RelocatePlayer(int jx, int jy, int el_player_raw)
5019 int el_player = GET_PLAYER_ELEMENT(el_player_raw);
5020 int player_nr = GET_PLAYER_NR(el_player);
5021 struct PlayerInfo *player = &stored_player[player_nr];
5022 boolean ffwd_delay = (tape.playing && tape.fast_forward);
5023 boolean no_delay = (tape.warp_forward);
5024 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5025 int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5026 int old_jx = player->jx;
5027 int old_jy = player->jy;
5028 int old_element = Feld[old_jx][old_jy];
5029 int element = Feld[jx][jy];
5030 boolean player_relocated = (old_jx != jx || old_jy != jy);
5032 int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
5033 int move_dir_vert = (jy < old_jy ? MV_UP : jy > old_jy ? MV_DOWN : 0);
5034 int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
5035 int enter_side_vert = MV_DIR_OPPOSITE(move_dir_vert);
5036 int leave_side_horiz = move_dir_horiz;
5037 int leave_side_vert = move_dir_vert;
5038 int enter_side = enter_side_horiz | enter_side_vert;
5039 int leave_side = leave_side_horiz | leave_side_vert;
5041 if (player->GameOver) /* do not reanimate dead player */
5044 if (!player_relocated) /* no need to relocate the player */
5047 if (IS_PLAYER(jx, jy)) /* player already placed at new position */
5049 RemoveField(jx, jy); /* temporarily remove newly placed player */
5050 DrawLevelField(jx, jy);
5053 if (player->present)
5055 while (player->MovPos)
5057 ScrollPlayer(player, SCROLL_GO_ON);
5058 ScrollScreen(NULL, SCROLL_GO_ON);
5060 AdvanceFrameAndPlayerCounters(player->index_nr);
5065 Delay(wait_delay_value);
5068 DrawPlayer(player); /* needed here only to cleanup last field */
5069 DrawLevelField(player->jx, player->jy); /* remove player graphic */
5071 player->is_moving = FALSE;
5074 if (IS_CUSTOM_ELEMENT(old_element))
5075 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
5077 player->index_bit, leave_side);
5079 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
5081 player->index_bit, leave_side);
5083 Feld[jx][jy] = el_player;
5084 InitPlayerField(jx, jy, el_player, TRUE);
5086 /* "InitPlayerField()" above sets Feld[jx][jy] to EL_EMPTY, but it may be
5087 possible that the relocation target field did not contain a player element,
5088 but a walkable element, to which the new player was relocated -- in this
5089 case, restore that (already initialized!) element on the player field */
5090 if (!ELEM_IS_PLAYER(element)) /* player may be set on walkable element */
5092 Feld[jx][jy] = element; /* restore previously existing element */
5095 /* only visually relocate centered player */
5096 DrawRelocateScreen(old_jx, old_jy, player->jx, player->jy, player->MovDir,
5097 FALSE, level.instant_relocation);
5099 TestIfPlayerTouchesBadThing(jx, jy);
5100 TestIfPlayerTouchesCustomElement(jx, jy);
5102 if (IS_CUSTOM_ELEMENT(element))
5103 CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
5104 player->index_bit, enter_side);
5106 CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
5107 player->index_bit, enter_side);
5109 if (player->is_switching)
5111 /* ensure that relocation while still switching an element does not cause
5112 a new element to be treated as also switched directly after relocation
5113 (this is important for teleporter switches that teleport the player to
5114 a place where another teleporter switch is in the same direction, which
5115 would then incorrectly be treated as immediately switched before the
5116 direction key that caused the switch was released) */
5118 player->switch_x += jx - old_jx;
5119 player->switch_y += jy - old_jy;
5123 void Explode(int ex, int ey, int phase, int mode)
5129 /* !!! eliminate this variable !!! */
5130 int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
5132 if (game.explosions_delayed)
5134 ExplodeField[ex][ey] = mode;
5138 if (phase == EX_PHASE_START) /* initialize 'Store[][]' field */
5140 int center_element = Feld[ex][ey];
5141 int artwork_element, explosion_element; /* set these values later */
5143 /* remove things displayed in background while burning dynamite */
5144 if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
5147 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
5149 /* put moving element to center field (and let it explode there) */
5150 center_element = MovingOrBlocked2Element(ex, ey);
5151 RemoveMovingField(ex, ey);
5152 Feld[ex][ey] = center_element;
5155 /* now "center_element" is finally determined -- set related values now */
5156 artwork_element = center_element; /* for custom player artwork */
5157 explosion_element = center_element; /* for custom player artwork */
5159 if (IS_PLAYER(ex, ey))
5161 int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
5163 artwork_element = stored_player[player_nr].artwork_element;
5165 if (level.use_explosion_element[player_nr])
5167 explosion_element = level.explosion_element[player_nr];
5168 artwork_element = explosion_element;
5172 if (mode == EX_TYPE_NORMAL ||
5173 mode == EX_TYPE_CENTER ||
5174 mode == EX_TYPE_CROSS)
5175 PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
5177 last_phase = element_info[explosion_element].explosion_delay + 1;
5179 for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
5181 int xx = x - ex + 1;
5182 int yy = y - ey + 1;
5185 if (!IN_LEV_FIELD(x, y) ||
5186 (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
5187 (mode == EX_TYPE_CROSS && (x != ex && y != ey)))
5190 element = Feld[x][y];
5192 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
5194 element = MovingOrBlocked2Element(x, y);
5196 if (!IS_EXPLOSION_PROOF(element))
5197 RemoveMovingField(x, y);
5200 /* indestructible elements can only explode in center (but not flames) */
5201 if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
5202 mode == EX_TYPE_BORDER)) ||
5203 element == EL_FLAMES)
5206 /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
5207 behaviour, for example when touching a yamyam that explodes to rocks
5208 with active deadly shield, a rock is created under the player !!! */
5209 /* (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8) */
5211 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
5212 (game.engine_version < VERSION_IDENT(3,1,0,0) ||
5213 (x == ex && y == ey && mode != EX_TYPE_BORDER)))
5215 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
5218 if (IS_ACTIVE_BOMB(element))
5220 /* re-activate things under the bomb like gate or penguin */
5221 Feld[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
5228 /* save walkable background elements while explosion on same tile */
5229 if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
5230 (x != ex || y != ey || mode == EX_TYPE_BORDER))
5231 Back[x][y] = element;
5233 /* ignite explodable elements reached by other explosion */
5234 if (element == EL_EXPLOSION)
5235 element = Store2[x][y];
5237 if (AmoebaNr[x][y] &&
5238 (element == EL_AMOEBA_FULL ||
5239 element == EL_BD_AMOEBA ||
5240 element == EL_AMOEBA_GROWING))
5242 AmoebaCnt[AmoebaNr[x][y]]--;
5243 AmoebaCnt2[AmoebaNr[x][y]]--;
5248 if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
5250 int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
5252 Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
5254 if (PLAYERINFO(ex, ey)->use_murphy)
5255 Store[x][y] = EL_EMPTY;
5258 /* !!! check this case -- currently needed for rnd_rado_negundo_v,
5259 !!! levels 015 018 019 020 021 022 023 026 027 028 !!! */
5260 else if (ELEM_IS_PLAYER(center_element))
5261 Store[x][y] = EL_EMPTY;
5262 else if (center_element == EL_YAMYAM)
5263 Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
5264 else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
5265 Store[x][y] = element_info[center_element].content.e[xx][yy];
5267 /* needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
5268 (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
5269 otherwise) -- FIX THIS !!! */
5270 else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
5271 Store[x][y] = element_info[element].content.e[1][1];
5273 else if (!CAN_EXPLODE(element))
5274 Store[x][y] = element_info[element].content.e[1][1];
5277 Store[x][y] = EL_EMPTY;
5279 if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
5280 center_element == EL_AMOEBA_TO_DIAMOND)
5281 Store2[x][y] = element;
5283 Feld[x][y] = EL_EXPLOSION;
5284 GfxElement[x][y] = artwork_element;
5286 ExplodePhase[x][y] = 1;
5287 ExplodeDelay[x][y] = last_phase;
5292 if (center_element == EL_YAMYAM)
5293 game.yamyam_content_nr =
5294 (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
5306 GfxFrame[x][y] = 0; /* restart explosion animation */
5308 last_phase = ExplodeDelay[x][y];
5310 ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
5312 /* this can happen if the player leaves an explosion just in time */
5313 if (GfxElement[x][y] == EL_UNDEFINED)
5314 GfxElement[x][y] = EL_EMPTY;
5316 border_element = Store2[x][y];
5317 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5318 border_element = StorePlayer[x][y];
5320 if (phase == element_info[border_element].ignition_delay ||
5321 phase == last_phase)
5323 boolean border_explosion = FALSE;
5325 if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
5326 !PLAYER_EXPLOSION_PROTECTED(x, y))
5328 KillPlayerUnlessExplosionProtected(x, y);
5329 border_explosion = TRUE;
5331 else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
5333 Feld[x][y] = Store2[x][y];
5336 border_explosion = TRUE;
5338 else if (border_element == EL_AMOEBA_TO_DIAMOND)
5340 AmoebeUmwandeln(x, y);
5342 border_explosion = TRUE;
5345 /* if an element just explodes due to another explosion (chain-reaction),
5346 do not immediately end the new explosion when it was the last frame of
5347 the explosion (as it would be done in the following "if"-statement!) */
5348 if (border_explosion && phase == last_phase)
5352 if (phase == last_phase)
5356 element = Feld[x][y] = Store[x][y];
5357 Store[x][y] = Store2[x][y] = 0;
5358 GfxElement[x][y] = EL_UNDEFINED;
5360 /* player can escape from explosions and might therefore be still alive */
5361 if (element >= EL_PLAYER_IS_EXPLODING_1 &&
5362 element <= EL_PLAYER_IS_EXPLODING_4)
5364 int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
5365 int explosion_element = EL_PLAYER_1 + player_nr;
5366 int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
5367 int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
5369 if (level.use_explosion_element[player_nr])
5370 explosion_element = level.explosion_element[player_nr];
5372 Feld[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
5373 element_info[explosion_element].content.e[xx][yy]);
5376 /* restore probably existing indestructible background element */
5377 if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
5378 element = Feld[x][y] = Back[x][y];
5381 MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
5382 GfxDir[x][y] = MV_NONE;
5383 ChangeDelay[x][y] = 0;
5384 ChangePage[x][y] = -1;
5386 CustomValue[x][y] = 0;
5388 InitField_WithBug2(x, y, FALSE);
5390 TEST_DrawLevelField(x, y);
5392 TestIfElementTouchesCustomElement(x, y);
5394 if (GFX_CRUMBLED(element))
5395 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
5397 if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
5398 StorePlayer[x][y] = 0;
5400 if (ELEM_IS_PLAYER(element))
5401 RelocatePlayer(x, y, element);
5403 else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
5405 int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
5406 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5409 TEST_DrawLevelFieldCrumbled(x, y);
5411 if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
5413 DrawLevelElement(x, y, Back[x][y]);
5414 DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
5416 else if (IS_WALKABLE_UNDER(Back[x][y]))
5418 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5419 DrawLevelElementThruMask(x, y, Back[x][y]);
5421 else if (!IS_WALKABLE_INSIDE(Back[x][y]))
5422 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5426 void DynaExplode(int ex, int ey)
5429 int dynabomb_element = Feld[ex][ey];
5430 int dynabomb_size = 1;
5431 boolean dynabomb_xl = FALSE;
5432 struct PlayerInfo *player;
5433 static int xy[4][2] =
5441 if (IS_ACTIVE_BOMB(dynabomb_element))
5443 player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
5444 dynabomb_size = player->dynabomb_size;
5445 dynabomb_xl = player->dynabomb_xl;
5446 player->dynabombs_left++;
5449 Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
5451 for (i = 0; i < NUM_DIRECTIONS; i++)
5453 for (j = 1; j <= dynabomb_size; j++)
5455 int x = ex + j * xy[i][0];
5456 int y = ey + j * xy[i][1];
5459 if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
5462 element = Feld[x][y];
5464 /* do not restart explosions of fields with active bombs */
5465 if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
5468 Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
5470 if (element != EL_EMPTY && element != EL_EXPLOSION &&
5471 !IS_DIGGABLE(element) && !dynabomb_xl)
5477 void Bang(int x, int y)
5479 int element = MovingOrBlocked2Element(x, y);
5480 int explosion_type = EX_TYPE_NORMAL;
5482 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5484 struct PlayerInfo *player = PLAYERINFO(x, y);
5486 element = Feld[x][y] = player->initial_element;
5488 if (level.use_explosion_element[player->index_nr])
5490 int explosion_element = level.explosion_element[player->index_nr];
5492 if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
5493 explosion_type = EX_TYPE_CROSS;
5494 else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
5495 explosion_type = EX_TYPE_CENTER;
5503 case EL_BD_BUTTERFLY:
5506 case EL_DARK_YAMYAM:
5510 RaiseScoreElement(element);
5513 case EL_DYNABOMB_PLAYER_1_ACTIVE:
5514 case EL_DYNABOMB_PLAYER_2_ACTIVE:
5515 case EL_DYNABOMB_PLAYER_3_ACTIVE:
5516 case EL_DYNABOMB_PLAYER_4_ACTIVE:
5517 case EL_DYNABOMB_INCREASE_NUMBER:
5518 case EL_DYNABOMB_INCREASE_SIZE:
5519 case EL_DYNABOMB_INCREASE_POWER:
5520 explosion_type = EX_TYPE_DYNA;
5523 case EL_DC_LANDMINE:
5524 explosion_type = EX_TYPE_CENTER;
5529 case EL_LAMP_ACTIVE:
5530 case EL_AMOEBA_TO_DIAMOND:
5531 if (!IS_PLAYER(x, y)) /* penguin and player may be at same field */
5532 explosion_type = EX_TYPE_CENTER;
5536 if (element_info[element].explosion_type == EXPLODES_CROSS)
5537 explosion_type = EX_TYPE_CROSS;
5538 else if (element_info[element].explosion_type == EXPLODES_1X1)
5539 explosion_type = EX_TYPE_CENTER;
5543 if (explosion_type == EX_TYPE_DYNA)
5546 Explode(x, y, EX_PHASE_START, explosion_type);
5548 CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
5551 void SplashAcid(int x, int y)
5553 if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
5554 (!IN_LEV_FIELD(x - 1, y - 2) ||
5555 !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
5556 Feld[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
5558 if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
5559 (!IN_LEV_FIELD(x + 1, y - 2) ||
5560 !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
5561 Feld[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
5563 PlayLevelSound(x, y, SND_ACID_SPLASHING);
5566 static void InitBeltMovement()
5568 static int belt_base_element[4] =
5570 EL_CONVEYOR_BELT_1_LEFT,
5571 EL_CONVEYOR_BELT_2_LEFT,
5572 EL_CONVEYOR_BELT_3_LEFT,
5573 EL_CONVEYOR_BELT_4_LEFT
5575 static int belt_base_active_element[4] =
5577 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
5578 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
5579 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
5580 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
5585 /* set frame order for belt animation graphic according to belt direction */
5586 for (i = 0; i < NUM_BELTS; i++)
5590 for (j = 0; j < NUM_BELT_PARTS; j++)
5592 int element = belt_base_active_element[belt_nr] + j;
5593 int graphic_1 = el2img(element);
5594 int graphic_2 = el2panelimg(element);
5596 if (game.belt_dir[i] == MV_LEFT)
5598 graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
5599 graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
5603 graphic_info[graphic_1].anim_mode |= ANIM_REVERSE;
5604 graphic_info[graphic_2].anim_mode |= ANIM_REVERSE;
5609 SCAN_PLAYFIELD(x, y)
5611 int element = Feld[x][y];
5613 for (i = 0; i < NUM_BELTS; i++)
5615 if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
5617 int e_belt_nr = getBeltNrFromBeltElement(element);
5620 if (e_belt_nr == belt_nr)
5622 int belt_part = Feld[x][y] - belt_base_element[belt_nr];
5624 Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
5631 static void ToggleBeltSwitch(int x, int y)
5633 static int belt_base_element[4] =
5635 EL_CONVEYOR_BELT_1_LEFT,
5636 EL_CONVEYOR_BELT_2_LEFT,
5637 EL_CONVEYOR_BELT_3_LEFT,
5638 EL_CONVEYOR_BELT_4_LEFT
5640 static int belt_base_active_element[4] =
5642 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
5643 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
5644 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
5645 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
5647 static int belt_base_switch_element[4] =
5649 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
5650 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
5651 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
5652 EL_CONVEYOR_BELT_4_SWITCH_LEFT
5654 static int belt_move_dir[4] =
5662 int element = Feld[x][y];
5663 int belt_nr = getBeltNrFromBeltSwitchElement(element);
5664 int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
5665 int belt_dir = belt_move_dir[belt_dir_nr];
5668 if (!IS_BELT_SWITCH(element))
5671 game.belt_dir_nr[belt_nr] = belt_dir_nr;
5672 game.belt_dir[belt_nr] = belt_dir;
5674 if (belt_dir_nr == 3)
5677 /* set frame order for belt animation graphic according to belt direction */
5678 for (i = 0; i < NUM_BELT_PARTS; i++)
5680 int element = belt_base_active_element[belt_nr] + i;
5681 int graphic_1 = el2img(element);
5682 int graphic_2 = el2panelimg(element);
5684 if (belt_dir == MV_LEFT)
5686 graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
5687 graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
5691 graphic_info[graphic_1].anim_mode |= ANIM_REVERSE;
5692 graphic_info[graphic_2].anim_mode |= ANIM_REVERSE;
5696 SCAN_PLAYFIELD(xx, yy)
5698 int element = Feld[xx][yy];
5700 if (IS_BELT_SWITCH(element))
5702 int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
5704 if (e_belt_nr == belt_nr)
5706 Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
5707 TEST_DrawLevelField(xx, yy);
5710 else if (IS_BELT(element) && belt_dir != MV_NONE)
5712 int e_belt_nr = getBeltNrFromBeltElement(element);
5714 if (e_belt_nr == belt_nr)
5716 int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
5718 Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
5719 TEST_DrawLevelField(xx, yy);
5722 else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
5724 int e_belt_nr = getBeltNrFromBeltActiveElement(element);
5726 if (e_belt_nr == belt_nr)
5728 int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
5730 Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
5731 TEST_DrawLevelField(xx, yy);
5737 static void ToggleSwitchgateSwitch(int x, int y)
5741 game.switchgate_pos = !game.switchgate_pos;
5743 SCAN_PLAYFIELD(xx, yy)
5745 int element = Feld[xx][yy];
5747 if (element == EL_SWITCHGATE_SWITCH_UP)
5749 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_DOWN;
5750 TEST_DrawLevelField(xx, yy);
5752 else if (element == EL_SWITCHGATE_SWITCH_DOWN)
5754 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP;
5755 TEST_DrawLevelField(xx, yy);
5757 else if (element == EL_DC_SWITCHGATE_SWITCH_UP)
5759 Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_DOWN;
5760 TEST_DrawLevelField(xx, yy);
5762 else if (element == EL_DC_SWITCHGATE_SWITCH_DOWN)
5764 Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP;
5765 TEST_DrawLevelField(xx, yy);
5767 else if (element == EL_SWITCHGATE_OPEN ||
5768 element == EL_SWITCHGATE_OPENING)
5770 Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
5772 PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
5774 else if (element == EL_SWITCHGATE_CLOSED ||
5775 element == EL_SWITCHGATE_CLOSING)
5777 Feld[xx][yy] = EL_SWITCHGATE_OPENING;
5779 PlayLevelSoundAction(xx, yy, ACTION_OPENING);
5784 static int getInvisibleActiveFromInvisibleElement(int element)
5786 return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
5787 element == EL_INVISIBLE_WALL ? EL_INVISIBLE_WALL_ACTIVE :
5788 element == EL_INVISIBLE_SAND ? EL_INVISIBLE_SAND_ACTIVE :
5792 static int getInvisibleFromInvisibleActiveElement(int element)
5794 return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
5795 element == EL_INVISIBLE_WALL_ACTIVE ? EL_INVISIBLE_WALL :
5796 element == EL_INVISIBLE_SAND_ACTIVE ? EL_INVISIBLE_SAND :
5800 static void RedrawAllLightSwitchesAndInvisibleElements()
5804 SCAN_PLAYFIELD(x, y)
5806 int element = Feld[x][y];
5808 if (element == EL_LIGHT_SWITCH &&
5809 game.light_time_left > 0)
5811 Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
5812 TEST_DrawLevelField(x, y);
5814 else if (element == EL_LIGHT_SWITCH_ACTIVE &&
5815 game.light_time_left == 0)
5817 Feld[x][y] = EL_LIGHT_SWITCH;
5818 TEST_DrawLevelField(x, y);
5820 else if (element == EL_EMC_DRIPPER &&
5821 game.light_time_left > 0)
5823 Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
5824 TEST_DrawLevelField(x, y);
5826 else if (element == EL_EMC_DRIPPER_ACTIVE &&
5827 game.light_time_left == 0)
5829 Feld[x][y] = EL_EMC_DRIPPER;
5830 TEST_DrawLevelField(x, y);
5832 else if (element == EL_INVISIBLE_STEELWALL ||
5833 element == EL_INVISIBLE_WALL ||
5834 element == EL_INVISIBLE_SAND)
5836 if (game.light_time_left > 0)
5837 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
5839 TEST_DrawLevelField(x, y);
5841 /* uncrumble neighbour fields, if needed */
5842 if (element == EL_INVISIBLE_SAND)
5843 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
5845 else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
5846 element == EL_INVISIBLE_WALL_ACTIVE ||
5847 element == EL_INVISIBLE_SAND_ACTIVE)
5849 if (game.light_time_left == 0)
5850 Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
5852 TEST_DrawLevelField(x, y);
5854 /* re-crumble neighbour fields, if needed */
5855 if (element == EL_INVISIBLE_SAND)
5856 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
5861 static void RedrawAllInvisibleElementsForLenses()
5865 SCAN_PLAYFIELD(x, y)
5867 int element = Feld[x][y];
5869 if (element == EL_EMC_DRIPPER &&
5870 game.lenses_time_left > 0)
5872 Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
5873 TEST_DrawLevelField(x, y);
5875 else if (element == EL_EMC_DRIPPER_ACTIVE &&
5876 game.lenses_time_left == 0)
5878 Feld[x][y] = EL_EMC_DRIPPER;
5879 TEST_DrawLevelField(x, y);
5881 else if (element == EL_INVISIBLE_STEELWALL ||
5882 element == EL_INVISIBLE_WALL ||
5883 element == EL_INVISIBLE_SAND)
5885 if (game.lenses_time_left > 0)
5886 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
5888 TEST_DrawLevelField(x, y);
5890 /* uncrumble neighbour fields, if needed */
5891 if (element == EL_INVISIBLE_SAND)
5892 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
5894 else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
5895 element == EL_INVISIBLE_WALL_ACTIVE ||
5896 element == EL_INVISIBLE_SAND_ACTIVE)
5898 if (game.lenses_time_left == 0)
5899 Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
5901 TEST_DrawLevelField(x, y);
5903 /* re-crumble neighbour fields, if needed */
5904 if (element == EL_INVISIBLE_SAND)
5905 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
5910 static void RedrawAllInvisibleElementsForMagnifier()
5914 SCAN_PLAYFIELD(x, y)
5916 int element = Feld[x][y];
5918 if (element == EL_EMC_FAKE_GRASS &&
5919 game.magnify_time_left > 0)
5921 Feld[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
5922 TEST_DrawLevelField(x, y);
5924 else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
5925 game.magnify_time_left == 0)
5927 Feld[x][y] = EL_EMC_FAKE_GRASS;
5928 TEST_DrawLevelField(x, y);
5930 else if (IS_GATE_GRAY(element) &&
5931 game.magnify_time_left > 0)
5933 Feld[x][y] = (IS_RND_GATE_GRAY(element) ?
5934 element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
5935 IS_EM_GATE_GRAY(element) ?
5936 element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
5937 IS_EMC_GATE_GRAY(element) ?
5938 element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
5939 IS_DC_GATE_GRAY(element) ?
5940 EL_DC_GATE_WHITE_GRAY_ACTIVE :
5942 TEST_DrawLevelField(x, y);
5944 else if (IS_GATE_GRAY_ACTIVE(element) &&
5945 game.magnify_time_left == 0)
5947 Feld[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
5948 element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
5949 IS_EM_GATE_GRAY_ACTIVE(element) ?
5950 element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
5951 IS_EMC_GATE_GRAY_ACTIVE(element) ?
5952 element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
5953 IS_DC_GATE_GRAY_ACTIVE(element) ?
5954 EL_DC_GATE_WHITE_GRAY :
5956 TEST_DrawLevelField(x, y);
5961 static void ToggleLightSwitch(int x, int y)
5963 int element = Feld[x][y];
5965 game.light_time_left =
5966 (element == EL_LIGHT_SWITCH ?
5967 level.time_light * FRAMES_PER_SECOND : 0);
5969 RedrawAllLightSwitchesAndInvisibleElements();
5972 static void ActivateTimegateSwitch(int x, int y)
5976 game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
5978 SCAN_PLAYFIELD(xx, yy)
5980 int element = Feld[xx][yy];
5982 if (element == EL_TIMEGATE_CLOSED ||
5983 element == EL_TIMEGATE_CLOSING)
5985 Feld[xx][yy] = EL_TIMEGATE_OPENING;
5986 PlayLevelSound(xx, yy, SND_CLASS_TIMEGATE_OPENING);
5990 else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
5992 Feld[xx][yy] = EL_TIMEGATE_SWITCH;
5993 TEST_DrawLevelField(xx, yy);
5999 Feld[x][y] = (Feld[x][y] == EL_TIMEGATE_SWITCH ? EL_TIMEGATE_SWITCH_ACTIVE :
6000 EL_DC_TIMEGATE_SWITCH_ACTIVE);
6003 void Impact(int x, int y)
6005 boolean last_line = (y == lev_fieldy - 1);
6006 boolean object_hit = FALSE;
6007 boolean impact = (last_line || object_hit);
6008 int element = Feld[x][y];
6009 int smashed = EL_STEELWALL;
6011 if (!last_line) /* check if element below was hit */
6013 if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
6016 object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
6017 MovDir[x][y + 1] != MV_DOWN ||
6018 MovPos[x][y + 1] <= TILEY / 2));
6020 /* do not smash moving elements that left the smashed field in time */
6021 if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
6022 ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
6025 #if USE_QUICKSAND_IMPACT_BUGFIX
6026 if (Feld[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
6028 RemoveMovingField(x, y + 1);
6029 Feld[x][y + 1] = EL_QUICKSAND_EMPTY;
6030 Feld[x][y + 2] = EL_ROCK;
6031 TEST_DrawLevelField(x, y + 2);
6036 if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTYING && object_hit == FALSE)
6038 RemoveMovingField(x, y + 1);
6039 Feld[x][y + 1] = EL_QUICKSAND_FAST_EMPTY;
6040 Feld[x][y + 2] = EL_ROCK;
6041 TEST_DrawLevelField(x, y + 2);
6048 smashed = MovingOrBlocked2Element(x, y + 1);
6050 impact = (last_line || object_hit);
6053 if (!last_line && smashed == EL_ACID) /* element falls into acid */
6055 SplashAcid(x, y + 1);
6059 /* !!! not sufficient for all cases -- see EL_PEARL below !!! */
6060 /* only reset graphic animation if graphic really changes after impact */
6062 el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
6064 ResetGfxAnimation(x, y);
6065 TEST_DrawLevelField(x, y);
6068 if (impact && CAN_EXPLODE_IMPACT(element))
6073 else if (impact && element == EL_PEARL &&
6074 smashed != EL_DC_MAGIC_WALL && smashed != EL_DC_MAGIC_WALL_ACTIVE)
6076 ResetGfxAnimation(x, y);
6078 Feld[x][y] = EL_PEARL_BREAKING;
6079 PlayLevelSound(x, y, SND_PEARL_BREAKING);
6082 else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
6084 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6089 if (impact && element == EL_AMOEBA_DROP)
6091 if (object_hit && IS_PLAYER(x, y + 1))
6092 KillPlayerUnlessEnemyProtected(x, y + 1);
6093 else if (object_hit && smashed == EL_PENGUIN)
6097 Feld[x][y] = EL_AMOEBA_GROWING;
6098 Store[x][y] = EL_AMOEBA_WET;
6100 ResetRandomAnimationValue(x, y);
6105 if (object_hit) /* check which object was hit */
6107 if ((CAN_PASS_MAGIC_WALL(element) &&
6108 (smashed == EL_MAGIC_WALL ||
6109 smashed == EL_BD_MAGIC_WALL)) ||
6110 (CAN_PASS_DC_MAGIC_WALL(element) &&
6111 smashed == EL_DC_MAGIC_WALL))
6114 int activated_magic_wall =
6115 (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
6116 smashed == EL_BD_MAGIC_WALL ? EL_BD_MAGIC_WALL_ACTIVE :
6117 EL_DC_MAGIC_WALL_ACTIVE);
6119 /* activate magic wall / mill */
6120 SCAN_PLAYFIELD(xx, yy)
6122 if (Feld[xx][yy] == smashed)
6123 Feld[xx][yy] = activated_magic_wall;
6126 game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
6127 game.magic_wall_active = TRUE;
6129 PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
6130 SND_MAGIC_WALL_ACTIVATING :
6131 smashed == EL_BD_MAGIC_WALL ?
6132 SND_BD_MAGIC_WALL_ACTIVATING :
6133 SND_DC_MAGIC_WALL_ACTIVATING));
6136 if (IS_PLAYER(x, y + 1))
6138 if (CAN_SMASH_PLAYER(element))
6140 KillPlayerUnlessEnemyProtected(x, y + 1);
6144 else if (smashed == EL_PENGUIN)
6146 if (CAN_SMASH_PLAYER(element))
6152 else if (element == EL_BD_DIAMOND)
6154 if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
6160 else if (((element == EL_SP_INFOTRON ||
6161 element == EL_SP_ZONK) &&
6162 (smashed == EL_SP_SNIKSNAK ||
6163 smashed == EL_SP_ELECTRON ||
6164 smashed == EL_SP_DISK_ORANGE)) ||
6165 (element == EL_SP_INFOTRON &&
6166 smashed == EL_SP_DISK_YELLOW))
6171 else if (CAN_SMASH_EVERYTHING(element))
6173 if (IS_CLASSIC_ENEMY(smashed) ||
6174 CAN_EXPLODE_SMASHED(smashed))
6179 else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
6181 if (smashed == EL_LAMP ||
6182 smashed == EL_LAMP_ACTIVE)
6187 else if (smashed == EL_NUT)
6189 Feld[x][y + 1] = EL_NUT_BREAKING;
6190 PlayLevelSound(x, y, SND_NUT_BREAKING);
6191 RaiseScoreElement(EL_NUT);
6194 else if (smashed == EL_PEARL)
6196 ResetGfxAnimation(x, y);
6198 Feld[x][y + 1] = EL_PEARL_BREAKING;
6199 PlayLevelSound(x, y, SND_PEARL_BREAKING);
6202 else if (smashed == EL_DIAMOND)
6204 Feld[x][y + 1] = EL_DIAMOND_BREAKING;
6205 PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
6208 else if (IS_BELT_SWITCH(smashed))
6210 ToggleBeltSwitch(x, y + 1);
6212 else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
6213 smashed == EL_SWITCHGATE_SWITCH_DOWN ||
6214 smashed == EL_DC_SWITCHGATE_SWITCH_UP ||
6215 smashed == EL_DC_SWITCHGATE_SWITCH_DOWN)
6217 ToggleSwitchgateSwitch(x, y + 1);
6219 else if (smashed == EL_LIGHT_SWITCH ||
6220 smashed == EL_LIGHT_SWITCH_ACTIVE)
6222 ToggleLightSwitch(x, y + 1);
6226 CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6228 CheckElementChangeBySide(x, y + 1, smashed, element,
6229 CE_SWITCHED, CH_SIDE_TOP);
6230 CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
6236 CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6241 /* play sound of magic wall / mill */
6243 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
6244 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ||
6245 Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE))
6247 if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
6248 PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
6249 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
6250 PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
6251 else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
6252 PlayLevelSound(x, y, SND_DC_MAGIC_WALL_FILLING);
6257 /* play sound of object that hits the ground */
6258 if (last_line || object_hit)
6259 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6262 inline static void TurnRoundExt(int x, int y)
6274 { 0, 0 }, { 0, 0 }, { 0, 0 },
6279 int left, right, back;
6283 { MV_DOWN, MV_UP, MV_RIGHT },
6284 { MV_UP, MV_DOWN, MV_LEFT },
6286 { MV_LEFT, MV_RIGHT, MV_DOWN },
6290 { MV_RIGHT, MV_LEFT, MV_UP }
6293 int element = Feld[x][y];
6294 int move_pattern = element_info[element].move_pattern;
6296 int old_move_dir = MovDir[x][y];
6297 int left_dir = turn[old_move_dir].left;
6298 int right_dir = turn[old_move_dir].right;
6299 int back_dir = turn[old_move_dir].back;
6301 int left_dx = move_xy[left_dir].dx, left_dy = move_xy[left_dir].dy;
6302 int right_dx = move_xy[right_dir].dx, right_dy = move_xy[right_dir].dy;
6303 int move_dx = move_xy[old_move_dir].dx, move_dy = move_xy[old_move_dir].dy;
6304 int back_dx = move_xy[back_dir].dx, back_dy = move_xy[back_dir].dy;
6306 int left_x = x + left_dx, left_y = y + left_dy;
6307 int right_x = x + right_dx, right_y = y + right_dy;
6308 int move_x = x + move_dx, move_y = y + move_dy;
6312 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
6314 TestIfBadThingTouchesOtherBadThing(x, y);
6316 if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
6317 MovDir[x][y] = right_dir;
6318 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6319 MovDir[x][y] = left_dir;
6321 if (element == EL_BUG && MovDir[x][y] != old_move_dir)
6323 else if (element == EL_BD_BUTTERFLY) /* && MovDir[x][y] == left_dir) */
6326 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
6328 TestIfBadThingTouchesOtherBadThing(x, y);
6330 if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
6331 MovDir[x][y] = left_dir;
6332 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6333 MovDir[x][y] = right_dir;
6335 if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
6337 else if (element == EL_BD_FIREFLY) /* && MovDir[x][y] == right_dir) */
6340 else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
6342 TestIfBadThingTouchesOtherBadThing(x, y);
6344 if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
6345 MovDir[x][y] = left_dir;
6346 else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
6347 MovDir[x][y] = right_dir;
6349 if (MovDir[x][y] != old_move_dir)
6352 else if (element == EL_YAMYAM)
6354 boolean can_turn_left = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
6355 boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
6357 if (can_turn_left && can_turn_right)
6358 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6359 else if (can_turn_left)
6360 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6361 else if (can_turn_right)
6362 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6364 MovDir[x][y] = back_dir;
6366 MovDelay[x][y] = 16 + 16 * RND(3);
6368 else if (element == EL_DARK_YAMYAM)
6370 boolean can_turn_left = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6372 boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6375 if (can_turn_left && can_turn_right)
6376 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6377 else if (can_turn_left)
6378 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6379 else if (can_turn_right)
6380 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6382 MovDir[x][y] = back_dir;
6384 MovDelay[x][y] = 16 + 16 * RND(3);
6386 else if (element == EL_PACMAN)
6388 boolean can_turn_left = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
6389 boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
6391 if (can_turn_left && can_turn_right)
6392 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6393 else if (can_turn_left)
6394 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6395 else if (can_turn_right)
6396 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6398 MovDir[x][y] = back_dir;
6400 MovDelay[x][y] = 6 + RND(40);
6402 else if (element == EL_PIG)
6404 boolean can_turn_left = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
6405 boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
6406 boolean can_move_on = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
6407 boolean should_turn_left, should_turn_right, should_move_on;
6409 int rnd = RND(rnd_value);
6411 should_turn_left = (can_turn_left &&
6413 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
6414 y + back_dy + left_dy)));
6415 should_turn_right = (can_turn_right &&
6417 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
6418 y + back_dy + right_dy)));
6419 should_move_on = (can_move_on &&
6422 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
6423 y + move_dy + left_dy) ||
6424 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
6425 y + move_dy + right_dy)));
6427 if (should_turn_left || should_turn_right || should_move_on)
6429 if (should_turn_left && should_turn_right && should_move_on)
6430 MovDir[x][y] = (rnd < rnd_value / 3 ? left_dir :
6431 rnd < 2 * rnd_value / 3 ? right_dir :
6433 else if (should_turn_left && should_turn_right)
6434 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6435 else if (should_turn_left && should_move_on)
6436 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
6437 else if (should_turn_right && should_move_on)
6438 MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
6439 else if (should_turn_left)
6440 MovDir[x][y] = left_dir;
6441 else if (should_turn_right)
6442 MovDir[x][y] = right_dir;
6443 else if (should_move_on)
6444 MovDir[x][y] = old_move_dir;
6446 else if (can_move_on && rnd > rnd_value / 8)
6447 MovDir[x][y] = old_move_dir;
6448 else if (can_turn_left && can_turn_right)
6449 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6450 else if (can_turn_left && rnd > rnd_value / 8)
6451 MovDir[x][y] = left_dir;
6452 else if (can_turn_right && rnd > rnd_value/8)
6453 MovDir[x][y] = right_dir;
6455 MovDir[x][y] = back_dir;
6457 xx = x + move_xy[MovDir[x][y]].dx;
6458 yy = y + move_xy[MovDir[x][y]].dy;
6460 if (!IN_LEV_FIELD(xx, yy) ||
6461 (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy])))
6462 MovDir[x][y] = old_move_dir;
6466 else if (element == EL_DRAGON)
6468 boolean can_turn_left = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
6469 boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
6470 boolean can_move_on = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
6472 int rnd = RND(rnd_value);
6474 if (can_move_on && rnd > rnd_value / 8)
6475 MovDir[x][y] = old_move_dir;
6476 else if (can_turn_left && can_turn_right)
6477 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6478 else if (can_turn_left && rnd > rnd_value / 8)
6479 MovDir[x][y] = left_dir;
6480 else if (can_turn_right && rnd > rnd_value / 8)
6481 MovDir[x][y] = right_dir;
6483 MovDir[x][y] = back_dir;
6485 xx = x + move_xy[MovDir[x][y]].dx;
6486 yy = y + move_xy[MovDir[x][y]].dy;
6488 if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
6489 MovDir[x][y] = old_move_dir;
6493 else if (element == EL_MOLE)
6495 boolean can_move_on =
6496 (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
6497 IS_AMOEBOID(Feld[move_x][move_y]) ||
6498 Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
6501 boolean can_turn_left =
6502 (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
6503 IS_AMOEBOID(Feld[left_x][left_y])));
6505 boolean can_turn_right =
6506 (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
6507 IS_AMOEBOID(Feld[right_x][right_y])));
6509 if (can_turn_left && can_turn_right)
6510 MovDir[x][y] = (RND(2) ? left_dir : right_dir);
6511 else if (can_turn_left)
6512 MovDir[x][y] = left_dir;
6514 MovDir[x][y] = right_dir;
6517 if (MovDir[x][y] != old_move_dir)
6520 else if (element == EL_BALLOON)
6522 MovDir[x][y] = game.wind_direction;
6525 else if (element == EL_SPRING)
6527 if (MovDir[x][y] & MV_HORIZONTAL)
6529 if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
6530 !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
6532 Feld[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
6533 ResetGfxAnimation(move_x, move_y);
6534 TEST_DrawLevelField(move_x, move_y);
6536 MovDir[x][y] = back_dir;
6538 else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
6539 SPRING_CAN_ENTER_FIELD(element, x, y + 1))
6540 MovDir[x][y] = MV_NONE;
6545 else if (element == EL_ROBOT ||
6546 element == EL_SATELLITE ||
6547 element == EL_PENGUIN ||
6548 element == EL_EMC_ANDROID)
6550 int attr_x = -1, attr_y = -1;
6561 for (i = 0; i < MAX_PLAYERS; i++)
6563 struct PlayerInfo *player = &stored_player[i];
6564 int jx = player->jx, jy = player->jy;
6566 if (!player->active)
6570 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
6578 if (element == EL_ROBOT && ZX >= 0 && ZY >= 0 &&
6579 (Feld[ZX][ZY] == EL_ROBOT_WHEEL_ACTIVE ||
6580 game.engine_version < VERSION_IDENT(3,1,0,0)))
6586 if (element == EL_PENGUIN)
6589 static int xy[4][2] =
6597 for (i = 0; i < NUM_DIRECTIONS; i++)
6599 int ex = x + xy[i][0];
6600 int ey = y + xy[i][1];
6602 if (IN_LEV_FIELD(ex, ey) && (Feld[ex][ey] == EL_EXIT_OPEN ||
6603 Feld[ex][ey] == EL_EM_EXIT_OPEN ||
6604 Feld[ex][ey] == EL_STEEL_EXIT_OPEN ||
6605 Feld[ex][ey] == EL_EM_STEEL_EXIT_OPEN))
6614 MovDir[x][y] = MV_NONE;
6616 MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
6617 else if (attr_x > x)
6618 MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
6620 MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
6621 else if (attr_y > y)
6622 MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
6624 if (element == EL_ROBOT)
6628 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6629 MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
6630 Moving2Blocked(x, y, &newx, &newy);
6632 if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
6633 MovDelay[x][y] = 8 + 8 * !RND(3);
6635 MovDelay[x][y] = 16;
6637 else if (element == EL_PENGUIN)
6643 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6645 boolean first_horiz = RND(2);
6646 int new_move_dir = MovDir[x][y];
6649 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6650 Moving2Blocked(x, y, &newx, &newy);
6652 if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
6656 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6657 Moving2Blocked(x, y, &newx, &newy);
6659 if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
6662 MovDir[x][y] = old_move_dir;
6666 else if (element == EL_SATELLITE)
6672 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6674 boolean first_horiz = RND(2);
6675 int new_move_dir = MovDir[x][y];
6678 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6679 Moving2Blocked(x, y, &newx, &newy);
6681 if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
6685 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6686 Moving2Blocked(x, y, &newx, &newy);
6688 if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
6691 MovDir[x][y] = old_move_dir;
6695 else if (element == EL_EMC_ANDROID)
6697 static int check_pos[16] =
6699 -1, /* 0 => (invalid) */
6700 7, /* 1 => MV_LEFT */
6701 3, /* 2 => MV_RIGHT */
6702 -1, /* 3 => (invalid) */
6704 0, /* 5 => MV_LEFT | MV_UP */
6705 2, /* 6 => MV_RIGHT | MV_UP */
6706 -1, /* 7 => (invalid) */
6707 5, /* 8 => MV_DOWN */
6708 6, /* 9 => MV_LEFT | MV_DOWN */
6709 4, /* 10 => MV_RIGHT | MV_DOWN */
6710 -1, /* 11 => (invalid) */
6711 -1, /* 12 => (invalid) */
6712 -1, /* 13 => (invalid) */
6713 -1, /* 14 => (invalid) */
6714 -1, /* 15 => (invalid) */
6722 { -1, -1, MV_LEFT | MV_UP },
6724 { +1, -1, MV_RIGHT | MV_UP },
6725 { +1, 0, MV_RIGHT },
6726 { +1, +1, MV_RIGHT | MV_DOWN },
6728 { -1, +1, MV_LEFT | MV_DOWN },
6731 int start_pos, check_order;
6732 boolean can_clone = FALSE;
6735 /* check if there is any free field around current position */
6736 for (i = 0; i < 8; i++)
6738 int newx = x + check_xy[i].dx;
6739 int newy = y + check_xy[i].dy;
6741 if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
6749 if (can_clone) /* randomly find an element to clone */
6753 start_pos = check_pos[RND(8)];
6754 check_order = (RND(2) ? -1 : +1);
6756 for (i = 0; i < 8; i++)
6758 int pos_raw = start_pos + i * check_order;
6759 int pos = (pos_raw + 8) % 8;
6760 int newx = x + check_xy[pos].dx;
6761 int newy = y + check_xy[pos].dy;
6763 if (ANDROID_CAN_CLONE_FIELD(newx, newy))
6765 element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
6766 element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
6768 Store[x][y] = Feld[newx][newy];
6777 if (can_clone) /* randomly find a direction to move */
6781 start_pos = check_pos[RND(8)];
6782 check_order = (RND(2) ? -1 : +1);
6784 for (i = 0; i < 8; i++)
6786 int pos_raw = start_pos + i * check_order;
6787 int pos = (pos_raw + 8) % 8;
6788 int newx = x + check_xy[pos].dx;
6789 int newy = y + check_xy[pos].dy;
6790 int new_move_dir = check_xy[pos].dir;
6792 if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
6794 MovDir[x][y] = new_move_dir;
6795 MovDelay[x][y] = level.android_clone_time * 8 + 1;
6804 if (can_clone) /* cloning and moving successful */
6807 /* cannot clone -- try to move towards player */
6809 start_pos = check_pos[MovDir[x][y] & 0x0f];
6810 check_order = (RND(2) ? -1 : +1);
6812 for (i = 0; i < 3; i++)
6814 /* first check start_pos, then previous/next or (next/previous) pos */
6815 int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
6816 int pos = (pos_raw + 8) % 8;
6817 int newx = x + check_xy[pos].dx;
6818 int newy = y + check_xy[pos].dy;
6819 int new_move_dir = check_xy[pos].dir;
6821 if (IS_PLAYER(newx, newy))
6824 if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
6826 MovDir[x][y] = new_move_dir;
6827 MovDelay[x][y] = level.android_move_time * 8 + 1;
6834 else if (move_pattern == MV_TURNING_LEFT ||
6835 move_pattern == MV_TURNING_RIGHT ||
6836 move_pattern == MV_TURNING_LEFT_RIGHT ||
6837 move_pattern == MV_TURNING_RIGHT_LEFT ||
6838 move_pattern == MV_TURNING_RANDOM ||
6839 move_pattern == MV_ALL_DIRECTIONS)
6841 boolean can_turn_left =
6842 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
6843 boolean can_turn_right =
6844 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
6846 if (element_info[element].move_stepsize == 0) /* "not moving" */
6849 if (move_pattern == MV_TURNING_LEFT)
6850 MovDir[x][y] = left_dir;
6851 else if (move_pattern == MV_TURNING_RIGHT)
6852 MovDir[x][y] = right_dir;
6853 else if (move_pattern == MV_TURNING_LEFT_RIGHT)
6854 MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
6855 else if (move_pattern == MV_TURNING_RIGHT_LEFT)
6856 MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
6857 else if (move_pattern == MV_TURNING_RANDOM)
6858 MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
6859 can_turn_right && !can_turn_left ? right_dir :
6860 RND(2) ? left_dir : right_dir);
6861 else if (can_turn_left && can_turn_right)
6862 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6863 else if (can_turn_left)
6864 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6865 else if (can_turn_right)
6866 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6868 MovDir[x][y] = back_dir;
6870 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6872 else if (move_pattern == MV_HORIZONTAL ||
6873 move_pattern == MV_VERTICAL)
6875 if (move_pattern & old_move_dir)
6876 MovDir[x][y] = back_dir;
6877 else if (move_pattern == MV_HORIZONTAL)
6878 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
6879 else if (move_pattern == MV_VERTICAL)
6880 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
6882 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6884 else if (move_pattern & MV_ANY_DIRECTION)
6886 MovDir[x][y] = move_pattern;
6887 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6889 else if (move_pattern & MV_WIND_DIRECTION)
6891 MovDir[x][y] = game.wind_direction;
6892 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6894 else if (move_pattern == MV_ALONG_LEFT_SIDE)
6896 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
6897 MovDir[x][y] = left_dir;
6898 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
6899 MovDir[x][y] = right_dir;
6901 if (MovDir[x][y] != old_move_dir)
6902 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6904 else if (move_pattern == MV_ALONG_RIGHT_SIDE)
6906 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
6907 MovDir[x][y] = right_dir;
6908 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
6909 MovDir[x][y] = left_dir;
6911 if (MovDir[x][y] != old_move_dir)
6912 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6914 else if (move_pattern == MV_TOWARDS_PLAYER ||
6915 move_pattern == MV_AWAY_FROM_PLAYER)
6917 int attr_x = -1, attr_y = -1;
6919 boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
6930 for (i = 0; i < MAX_PLAYERS; i++)
6932 struct PlayerInfo *player = &stored_player[i];
6933 int jx = player->jx, jy = player->jy;
6935 if (!player->active)
6939 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
6947 MovDir[x][y] = MV_NONE;
6949 MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
6950 else if (attr_x > x)
6951 MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
6953 MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
6954 else if (attr_y > y)
6955 MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
6957 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6959 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6961 boolean first_horiz = RND(2);
6962 int new_move_dir = MovDir[x][y];
6964 if (element_info[element].move_stepsize == 0) /* "not moving" */
6966 first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
6967 MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6973 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6974 Moving2Blocked(x, y, &newx, &newy);
6976 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
6980 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6981 Moving2Blocked(x, y, &newx, &newy);
6983 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
6986 MovDir[x][y] = old_move_dir;
6989 else if (move_pattern == MV_WHEN_PUSHED ||
6990 move_pattern == MV_WHEN_DROPPED)
6992 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
6993 MovDir[x][y] = MV_NONE;
6997 else if (move_pattern & MV_MAZE_RUNNER_STYLE)
6999 static int test_xy[7][2] =
7009 static int test_dir[7] =
7019 boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
7020 int move_preference = -1000000; /* start with very low preference */
7021 int new_move_dir = MV_NONE;
7022 int start_test = RND(4);
7025 for (i = 0; i < NUM_DIRECTIONS; i++)
7027 int move_dir = test_dir[start_test + i];
7028 int move_dir_preference;
7030 xx = x + test_xy[start_test + i][0];
7031 yy = y + test_xy[start_test + i][1];
7033 if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
7034 (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
7036 new_move_dir = move_dir;
7041 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
7044 move_dir_preference = -1 * RunnerVisit[xx][yy];
7045 if (hunter_mode && PlayerVisit[xx][yy] > 0)
7046 move_dir_preference = PlayerVisit[xx][yy];
7048 if (move_dir_preference > move_preference)
7050 /* prefer field that has not been visited for the longest time */
7051 move_preference = move_dir_preference;
7052 new_move_dir = move_dir;
7054 else if (move_dir_preference == move_preference &&
7055 move_dir == old_move_dir)
7057 /* prefer last direction when all directions are preferred equally */
7058 move_preference = move_dir_preference;
7059 new_move_dir = move_dir;
7063 MovDir[x][y] = new_move_dir;
7064 if (old_move_dir != new_move_dir)
7065 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7069 static void TurnRound(int x, int y)
7071 int direction = MovDir[x][y];
7075 GfxDir[x][y] = MovDir[x][y];
7077 if (direction != MovDir[x][y])
7081 GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
7083 ResetGfxFrame(x, y, FALSE);
7086 static boolean JustBeingPushed(int x, int y)
7090 for (i = 0; i < MAX_PLAYERS; i++)
7092 struct PlayerInfo *player = &stored_player[i];
7094 if (player->active && player->is_pushing && player->MovPos)
7096 int next_jx = player->jx + (player->jx - player->last_jx);
7097 int next_jy = player->jy + (player->jy - player->last_jy);
7099 if (x == next_jx && y == next_jy)
7107 void StartMoving(int x, int y)
7109 boolean started_moving = FALSE; /* some elements can fall _and_ move */
7110 int element = Feld[x][y];
7115 if (MovDelay[x][y] == 0)
7116 GfxAction[x][y] = ACTION_DEFAULT;
7118 if (CAN_FALL(element) && y < lev_fieldy - 1)
7120 if ((x > 0 && IS_PLAYER(x - 1, y)) ||
7121 (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
7122 if (JustBeingPushed(x, y))
7125 if (element == EL_QUICKSAND_FULL)
7127 if (IS_FREE(x, y + 1))
7129 InitMovingField(x, y, MV_DOWN);
7130 started_moving = TRUE;
7132 Feld[x][y] = EL_QUICKSAND_EMPTYING;
7133 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7134 if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7135 Store[x][y] = EL_ROCK;
7137 Store[x][y] = EL_ROCK;
7140 PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7142 else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7144 if (!MovDelay[x][y])
7146 MovDelay[x][y] = TILEY + 1;
7148 ResetGfxAnimation(x, y);
7149 ResetGfxAnimation(x, y + 1);
7154 DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7155 DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7162 Feld[x][y] = EL_QUICKSAND_EMPTY;
7163 Feld[x][y + 1] = EL_QUICKSAND_FULL;
7164 Store[x][y + 1] = Store[x][y];
7167 PlayLevelSoundAction(x, y, ACTION_FILLING);
7169 else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7171 if (!MovDelay[x][y])
7173 MovDelay[x][y] = TILEY + 1;
7175 ResetGfxAnimation(x, y);
7176 ResetGfxAnimation(x, y + 1);
7181 DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7182 DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7189 Feld[x][y] = EL_QUICKSAND_EMPTY;
7190 Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7191 Store[x][y + 1] = Store[x][y];
7194 PlayLevelSoundAction(x, y, ACTION_FILLING);
7197 else if (element == EL_QUICKSAND_FAST_FULL)
7199 if (IS_FREE(x, y + 1))
7201 InitMovingField(x, y, MV_DOWN);
7202 started_moving = TRUE;
7204 Feld[x][y] = EL_QUICKSAND_FAST_EMPTYING;
7205 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7206 if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7207 Store[x][y] = EL_ROCK;
7209 Store[x][y] = EL_ROCK;
7212 PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7214 else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7216 if (!MovDelay[x][y])
7218 MovDelay[x][y] = TILEY + 1;
7220 ResetGfxAnimation(x, y);
7221 ResetGfxAnimation(x, y + 1);
7226 DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7227 DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7234 Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
7235 Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7236 Store[x][y + 1] = Store[x][y];
7239 PlayLevelSoundAction(x, y, ACTION_FILLING);
7241 else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7243 if (!MovDelay[x][y])
7245 MovDelay[x][y] = TILEY + 1;
7247 ResetGfxAnimation(x, y);
7248 ResetGfxAnimation(x, y + 1);
7253 DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7254 DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7261 Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
7262 Feld[x][y + 1] = EL_QUICKSAND_FULL;
7263 Store[x][y + 1] = Store[x][y];
7266 PlayLevelSoundAction(x, y, ACTION_FILLING);
7269 else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7270 Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7272 InitMovingField(x, y, MV_DOWN);
7273 started_moving = TRUE;
7275 Feld[x][y] = EL_QUICKSAND_FILLING;
7276 Store[x][y] = element;
7278 PlayLevelSoundAction(x, y, ACTION_FILLING);
7280 else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7281 Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7283 InitMovingField(x, y, MV_DOWN);
7284 started_moving = TRUE;
7286 Feld[x][y] = EL_QUICKSAND_FAST_FILLING;
7287 Store[x][y] = element;
7289 PlayLevelSoundAction(x, y, ACTION_FILLING);
7291 else if (element == EL_MAGIC_WALL_FULL)
7293 if (IS_FREE(x, y + 1))
7295 InitMovingField(x, y, MV_DOWN);
7296 started_moving = TRUE;
7298 Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
7299 Store[x][y] = EL_CHANGED(Store[x][y]);
7301 else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
7303 if (!MovDelay[x][y])
7304 MovDelay[x][y] = TILEY / 4 + 1;
7313 Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
7314 Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
7315 Store[x][y + 1] = EL_CHANGED(Store[x][y]);
7319 else if (element == EL_BD_MAGIC_WALL_FULL)
7321 if (IS_FREE(x, y + 1))
7323 InitMovingField(x, y, MV_DOWN);
7324 started_moving = TRUE;
7326 Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
7327 Store[x][y] = EL_CHANGED_BD(Store[x][y]);
7329 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
7331 if (!MovDelay[x][y])
7332 MovDelay[x][y] = TILEY / 4 + 1;
7341 Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
7342 Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
7343 Store[x][y + 1] = EL_CHANGED_BD(Store[x][y]);
7347 else if (element == EL_DC_MAGIC_WALL_FULL)
7349 if (IS_FREE(x, y + 1))
7351 InitMovingField(x, y, MV_DOWN);
7352 started_moving = TRUE;
7354 Feld[x][y] = EL_DC_MAGIC_WALL_EMPTYING;
7355 Store[x][y] = EL_CHANGED_DC(Store[x][y]);
7357 else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
7359 if (!MovDelay[x][y])
7360 MovDelay[x][y] = TILEY / 4 + 1;
7369 Feld[x][y] = EL_DC_MAGIC_WALL_ACTIVE;
7370 Feld[x][y + 1] = EL_DC_MAGIC_WALL_FULL;
7371 Store[x][y + 1] = EL_CHANGED_DC(Store[x][y]);
7375 else if ((CAN_PASS_MAGIC_WALL(element) &&
7376 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
7377 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)) ||
7378 (CAN_PASS_DC_MAGIC_WALL(element) &&
7379 (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)))
7382 InitMovingField(x, y, MV_DOWN);
7383 started_moving = TRUE;
7386 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
7387 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ? EL_BD_MAGIC_WALL_FILLING :
7388 EL_DC_MAGIC_WALL_FILLING);
7389 Store[x][y] = element;
7391 else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
7393 SplashAcid(x, y + 1);
7395 InitMovingField(x, y, MV_DOWN);
7396 started_moving = TRUE;
7398 Store[x][y] = EL_ACID;
7401 (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7402 CheckImpact[x][y] && !IS_FREE(x, y + 1)) ||
7403 (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
7404 CAN_FALL(element) && WasJustFalling[x][y] &&
7405 (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
7407 (game.engine_version < VERSION_IDENT(2,2,0,7) &&
7408 CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
7409 (Feld[x][y + 1] == EL_BLOCKED)))
7411 /* this is needed for a special case not covered by calling "Impact()"
7412 from "ContinueMoving()": if an element moves to a tile directly below
7413 another element which was just falling on that tile (which was empty
7414 in the previous frame), the falling element above would just stop
7415 instead of smashing the element below (in previous version, the above
7416 element was just checked for "moving" instead of "falling", resulting
7417 in incorrect smashes caused by horizontal movement of the above
7418 element; also, the case of the player being the element to smash was
7419 simply not covered here... :-/ ) */
7421 CheckCollision[x][y] = 0;
7422 CheckImpact[x][y] = 0;
7426 else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
7428 if (MovDir[x][y] == MV_NONE)
7430 InitMovingField(x, y, MV_DOWN);
7431 started_moving = TRUE;
7434 else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
7436 if (WasJustFalling[x][y]) /* prevent animation from being restarted */
7437 MovDir[x][y] = MV_DOWN;
7439 InitMovingField(x, y, MV_DOWN);
7440 started_moving = TRUE;
7442 else if (element == EL_AMOEBA_DROP)
7444 Feld[x][y] = EL_AMOEBA_GROWING;
7445 Store[x][y] = EL_AMOEBA_WET;
7447 else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
7448 (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
7449 !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
7450 element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
7452 boolean can_fall_left = (x > 0 && IS_FREE(x - 1, y) &&
7453 (IS_FREE(x - 1, y + 1) ||
7454 Feld[x - 1][y + 1] == EL_ACID));
7455 boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
7456 (IS_FREE(x + 1, y + 1) ||
7457 Feld[x + 1][y + 1] == EL_ACID));
7458 boolean can_fall_any = (can_fall_left || can_fall_right);
7459 boolean can_fall_both = (can_fall_left && can_fall_right);
7460 int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
7462 if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
7464 if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
7465 can_fall_right = FALSE;
7466 else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
7467 can_fall_left = FALSE;
7468 else if (slippery_type == SLIPPERY_ONLY_LEFT)
7469 can_fall_right = FALSE;
7470 else if (slippery_type == SLIPPERY_ONLY_RIGHT)
7471 can_fall_left = FALSE;
7473 can_fall_any = (can_fall_left || can_fall_right);
7474 can_fall_both = FALSE;
7479 if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
7480 can_fall_right = FALSE; /* slip down on left side */
7482 can_fall_left = !(can_fall_right = RND(2));
7484 can_fall_both = FALSE;
7489 /* if not determined otherwise, prefer left side for slipping down */
7490 InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
7491 started_moving = TRUE;
7494 else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
7496 boolean left_is_free = (x > 0 && IS_FREE(x - 1, y));
7497 boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
7498 int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
7499 int belt_dir = game.belt_dir[belt_nr];
7501 if ((belt_dir == MV_LEFT && left_is_free) ||
7502 (belt_dir == MV_RIGHT && right_is_free))
7504 int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
7506 InitMovingField(x, y, belt_dir);
7507 started_moving = TRUE;
7509 Pushed[x][y] = TRUE;
7510 Pushed[nextx][y] = TRUE;
7512 GfxAction[x][y] = ACTION_DEFAULT;
7516 MovDir[x][y] = 0; /* if element was moving, stop it */
7521 /* not "else if" because of elements that can fall and move (EL_SPRING) */
7522 if (CAN_MOVE(element) && !started_moving)
7524 int move_pattern = element_info[element].move_pattern;
7527 Moving2Blocked(x, y, &newx, &newy);
7529 if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
7532 if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7533 CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7535 WasJustMoving[x][y] = 0;
7536 CheckCollision[x][y] = 0;
7538 TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
7540 if (Feld[x][y] != element) /* element has changed */
7544 if (!MovDelay[x][y]) /* start new movement phase */
7546 /* all objects that can change their move direction after each step
7547 (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
7549 if (element != EL_YAMYAM &&
7550 element != EL_DARK_YAMYAM &&
7551 element != EL_PACMAN &&
7552 !(move_pattern & MV_ANY_DIRECTION) &&
7553 move_pattern != MV_TURNING_LEFT &&
7554 move_pattern != MV_TURNING_RIGHT &&
7555 move_pattern != MV_TURNING_LEFT_RIGHT &&
7556 move_pattern != MV_TURNING_RIGHT_LEFT &&
7557 move_pattern != MV_TURNING_RANDOM)
7561 if (MovDelay[x][y] && (element == EL_BUG ||
7562 element == EL_SPACESHIP ||
7563 element == EL_SP_SNIKSNAK ||
7564 element == EL_SP_ELECTRON ||
7565 element == EL_MOLE))
7566 TEST_DrawLevelField(x, y);
7570 if (MovDelay[x][y]) /* wait some time before next movement */
7574 if (element == EL_ROBOT ||
7575 element == EL_YAMYAM ||
7576 element == EL_DARK_YAMYAM)
7578 DrawLevelElementAnimationIfNeeded(x, y, element);
7579 PlayLevelSoundAction(x, y, ACTION_WAITING);
7581 else if (element == EL_SP_ELECTRON)
7582 DrawLevelElementAnimationIfNeeded(x, y, element);
7583 else if (element == EL_DRAGON)
7586 int dir = MovDir[x][y];
7587 int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
7588 int dy = (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
7589 int graphic = (dir == MV_LEFT ? IMG_FLAMES_1_LEFT :
7590 dir == MV_RIGHT ? IMG_FLAMES_1_RIGHT :
7591 dir == MV_UP ? IMG_FLAMES_1_UP :
7592 dir == MV_DOWN ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
7593 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
7595 GfxAction[x][y] = ACTION_ATTACKING;
7597 if (IS_PLAYER(x, y))
7598 DrawPlayerField(x, y);
7600 TEST_DrawLevelField(x, y);
7602 PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
7604 for (i = 1; i <= 3; i++)
7606 int xx = x + i * dx;
7607 int yy = y + i * dy;
7608 int sx = SCREENX(xx);
7609 int sy = SCREENY(yy);
7610 int flame_graphic = graphic + (i - 1);
7612 if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
7617 int flamed = MovingOrBlocked2Element(xx, yy);
7619 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
7622 RemoveMovingField(xx, yy);
7624 ChangeDelay[xx][yy] = 0;
7626 Feld[xx][yy] = EL_FLAMES;
7628 if (IN_SCR_FIELD(sx, sy))
7630 TEST_DrawLevelFieldCrumbled(xx, yy);
7631 DrawGraphic(sx, sy, flame_graphic, frame);
7636 if (Feld[xx][yy] == EL_FLAMES)
7637 Feld[xx][yy] = EL_EMPTY;
7638 TEST_DrawLevelField(xx, yy);
7643 if (MovDelay[x][y]) /* element still has to wait some time */
7645 PlayLevelSoundAction(x, y, ACTION_WAITING);
7651 /* now make next step */
7653 Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
7655 if (DONT_COLLIDE_WITH(element) &&
7656 IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
7657 !PLAYER_ENEMY_PROTECTED(newx, newy))
7659 TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
7664 else if (CAN_MOVE_INTO_ACID(element) &&
7665 IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID &&
7666 !IS_MV_DIAGONAL(MovDir[x][y]) &&
7667 (MovDir[x][y] == MV_DOWN ||
7668 game.engine_version >= VERSION_IDENT(3,1,0,0)))
7670 SplashAcid(newx, newy);
7671 Store[x][y] = EL_ACID;
7673 else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
7675 if (Feld[newx][newy] == EL_EXIT_OPEN ||
7676 Feld[newx][newy] == EL_EM_EXIT_OPEN ||
7677 Feld[newx][newy] == EL_STEEL_EXIT_OPEN ||
7678 Feld[newx][newy] == EL_EM_STEEL_EXIT_OPEN)
7681 TEST_DrawLevelField(x, y);
7683 PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
7684 if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
7685 DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
7687 local_player->friends_still_needed--;
7688 if (!local_player->friends_still_needed &&
7689 !local_player->GameOver && AllPlayersGone)
7690 PlayerWins(local_player);
7694 else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
7696 if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MP_MOVING)
7697 TEST_DrawLevelField(newx, newy);
7699 GfxDir[x][y] = MovDir[x][y] = MV_NONE;
7701 else if (!IS_FREE(newx, newy))
7703 GfxAction[x][y] = ACTION_WAITING;
7705 if (IS_PLAYER(x, y))
7706 DrawPlayerField(x, y);
7708 TEST_DrawLevelField(x, y);
7713 else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
7715 if (IS_FOOD_PIG(Feld[newx][newy]))
7717 if (IS_MOVING(newx, newy))
7718 RemoveMovingField(newx, newy);
7721 Feld[newx][newy] = EL_EMPTY;
7722 TEST_DrawLevelField(newx, newy);
7725 PlayLevelSound(x, y, SND_PIG_DIGGING);
7727 else if (!IS_FREE(newx, newy))
7729 if (IS_PLAYER(x, y))
7730 DrawPlayerField(x, y);
7732 TEST_DrawLevelField(x, y);
7737 else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
7739 if (Store[x][y] != EL_EMPTY)
7741 boolean can_clone = FALSE;
7744 /* check if element to clone is still there */
7745 for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
7747 if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == Store[x][y])
7755 /* cannot clone or target field not free anymore -- do not clone */
7756 if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7757 Store[x][y] = EL_EMPTY;
7760 if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7762 if (IS_MV_DIAGONAL(MovDir[x][y]))
7764 int diagonal_move_dir = MovDir[x][y];
7765 int stored = Store[x][y];
7766 int change_delay = 8;
7769 /* android is moving diagonally */
7771 CreateField(x, y, EL_DIAGONAL_SHRINKING);
7773 Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
7774 GfxElement[x][y] = EL_EMC_ANDROID;
7775 GfxAction[x][y] = ACTION_SHRINKING;
7776 GfxDir[x][y] = diagonal_move_dir;
7777 ChangeDelay[x][y] = change_delay;
7779 graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
7782 DrawLevelGraphicAnimation(x, y, graphic);
7783 PlayLevelSoundAction(x, y, ACTION_SHRINKING);
7785 if (Feld[newx][newy] == EL_ACID)
7787 SplashAcid(newx, newy);
7792 CreateField(newx, newy, EL_DIAGONAL_GROWING);
7794 Store[newx][newy] = EL_EMC_ANDROID;
7795 GfxElement[newx][newy] = EL_EMC_ANDROID;
7796 GfxAction[newx][newy] = ACTION_GROWING;
7797 GfxDir[newx][newy] = diagonal_move_dir;
7798 ChangeDelay[newx][newy] = change_delay;
7800 graphic = el_act_dir2img(GfxElement[newx][newy],
7801 GfxAction[newx][newy], GfxDir[newx][newy]);
7803 DrawLevelGraphicAnimation(newx, newy, graphic);
7804 PlayLevelSoundAction(newx, newy, ACTION_GROWING);
7810 Feld[newx][newy] = EL_EMPTY;
7811 TEST_DrawLevelField(newx, newy);
7813 PlayLevelSoundAction(x, y, ACTION_DIGGING);
7816 else if (!IS_FREE(newx, newy))
7821 else if (IS_CUSTOM_ELEMENT(element) &&
7822 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7824 if (!DigFieldByCE(newx, newy, element))
7827 if (move_pattern & MV_MAZE_RUNNER_STYLE)
7829 RunnerVisit[x][y] = FrameCounter;
7830 PlayerVisit[x][y] /= 8; /* expire player visit path */
7833 else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
7835 if (!IS_FREE(newx, newy))
7837 if (IS_PLAYER(x, y))
7838 DrawPlayerField(x, y);
7840 TEST_DrawLevelField(x, y);
7846 boolean wanna_flame = !RND(10);
7847 int dx = newx - x, dy = newy - y;
7848 int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
7849 int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
7850 int element1 = (IN_LEV_FIELD(newx1, newy1) ?
7851 MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
7852 int element2 = (IN_LEV_FIELD(newx2, newy2) ?
7853 MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
7856 IS_CLASSIC_ENEMY(element1) ||
7857 IS_CLASSIC_ENEMY(element2)) &&
7858 element1 != EL_DRAGON && element2 != EL_DRAGON &&
7859 element1 != EL_FLAMES && element2 != EL_FLAMES)
7861 ResetGfxAnimation(x, y);
7862 GfxAction[x][y] = ACTION_ATTACKING;
7864 if (IS_PLAYER(x, y))
7865 DrawPlayerField(x, y);
7867 TEST_DrawLevelField(x, y);
7869 PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
7871 MovDelay[x][y] = 50;
7873 Feld[newx][newy] = EL_FLAMES;
7874 if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
7875 Feld[newx1][newy1] = EL_FLAMES;
7876 if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
7877 Feld[newx2][newy2] = EL_FLAMES;
7883 else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
7884 Feld[newx][newy] == EL_DIAMOND)
7886 if (IS_MOVING(newx, newy))
7887 RemoveMovingField(newx, newy);
7890 Feld[newx][newy] = EL_EMPTY;
7891 TEST_DrawLevelField(newx, newy);
7894 PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
7896 else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
7897 IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
7899 if (AmoebaNr[newx][newy])
7901 AmoebaCnt2[AmoebaNr[newx][newy]]--;
7902 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
7903 Feld[newx][newy] == EL_BD_AMOEBA)
7904 AmoebaCnt[AmoebaNr[newx][newy]]--;
7907 if (IS_MOVING(newx, newy))
7909 RemoveMovingField(newx, newy);
7913 Feld[newx][newy] = EL_EMPTY;
7914 TEST_DrawLevelField(newx, newy);
7917 PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
7919 else if ((element == EL_PACMAN || element == EL_MOLE)
7920 && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
7922 if (AmoebaNr[newx][newy])
7924 AmoebaCnt2[AmoebaNr[newx][newy]]--;
7925 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
7926 Feld[newx][newy] == EL_BD_AMOEBA)
7927 AmoebaCnt[AmoebaNr[newx][newy]]--;
7930 if (element == EL_MOLE)
7932 Feld[newx][newy] = EL_AMOEBA_SHRINKING;
7933 PlayLevelSound(x, y, SND_MOLE_DIGGING);
7935 ResetGfxAnimation(x, y);
7936 GfxAction[x][y] = ACTION_DIGGING;
7937 TEST_DrawLevelField(x, y);
7939 MovDelay[newx][newy] = 0; /* start amoeba shrinking delay */
7941 return; /* wait for shrinking amoeba */
7943 else /* element == EL_PACMAN */
7945 Feld[newx][newy] = EL_EMPTY;
7946 TEST_DrawLevelField(newx, newy);
7947 PlayLevelSound(x, y, SND_PACMAN_DIGGING);
7950 else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
7951 (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
7952 (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
7954 /* wait for shrinking amoeba to completely disappear */
7957 else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
7959 /* object was running against a wall */
7963 if (GFX_ELEMENT(element) != EL_SAND) /* !!! FIX THIS (crumble) !!! */
7964 DrawLevelElementAnimation(x, y, element);
7966 if (DONT_TOUCH(element))
7967 TestIfBadThingTouchesPlayer(x, y);
7972 InitMovingField(x, y, MovDir[x][y]);
7974 PlayLevelSoundAction(x, y, ACTION_MOVING);
7978 ContinueMoving(x, y);
7981 void ContinueMoving(int x, int y)
7983 int element = Feld[x][y];
7984 struct ElementInfo *ei = &element_info[element];
7985 int direction = MovDir[x][y];
7986 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
7987 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
7988 int newx = x + dx, newy = y + dy;
7989 int stored = Store[x][y];
7990 int stored_new = Store[newx][newy];
7991 boolean pushed_by_player = (Pushed[x][y] && IS_PLAYER(x, y));
7992 boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
7993 boolean last_line = (newy == lev_fieldy - 1);
7995 MovPos[x][y] += getElementMoveStepsize(x, y);
7997 if (pushed_by_player) /* special case: moving object pushed by player */
7998 MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
8000 if (ABS(MovPos[x][y]) < TILEX)
8002 TEST_DrawLevelField(x, y);
8004 return; /* element is still moving */
8007 /* element reached destination field */
8009 Feld[x][y] = EL_EMPTY;
8010 Feld[newx][newy] = element;
8011 MovPos[x][y] = 0; /* force "not moving" for "crumbled sand" */
8013 if (Store[x][y] == EL_ACID) /* element is moving into acid pool */
8015 element = Feld[newx][newy] = EL_ACID;
8017 else if (element == EL_MOLE)
8019 Feld[x][y] = EL_SAND;
8021 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8023 else if (element == EL_QUICKSAND_FILLING)
8025 element = Feld[newx][newy] = get_next_element(element);
8026 Store[newx][newy] = Store[x][y];
8028 else if (element == EL_QUICKSAND_EMPTYING)
8030 Feld[x][y] = get_next_element(element);
8031 element = Feld[newx][newy] = Store[x][y];
8033 else if (element == EL_QUICKSAND_FAST_FILLING)
8035 element = Feld[newx][newy] = get_next_element(element);
8036 Store[newx][newy] = Store[x][y];
8038 else if (element == EL_QUICKSAND_FAST_EMPTYING)
8040 Feld[x][y] = get_next_element(element);
8041 element = Feld[newx][newy] = Store[x][y];
8043 else if (element == EL_MAGIC_WALL_FILLING)
8045 element = Feld[newx][newy] = get_next_element(element);
8046 if (!game.magic_wall_active)
8047 element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
8048 Store[newx][newy] = Store[x][y];
8050 else if (element == EL_MAGIC_WALL_EMPTYING)
8052 Feld[x][y] = get_next_element(element);
8053 if (!game.magic_wall_active)
8054 Feld[x][y] = EL_MAGIC_WALL_DEAD;
8055 element = Feld[newx][newy] = Store[x][y];
8057 InitField(newx, newy, FALSE);
8059 else if (element == EL_BD_MAGIC_WALL_FILLING)
8061 element = Feld[newx][newy] = get_next_element(element);
8062 if (!game.magic_wall_active)
8063 element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
8064 Store[newx][newy] = Store[x][y];
8066 else if (element == EL_BD_MAGIC_WALL_EMPTYING)
8068 Feld[x][y] = get_next_element(element);
8069 if (!game.magic_wall_active)
8070 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
8071 element = Feld[newx][newy] = Store[x][y];
8073 InitField(newx, newy, FALSE);
8075 else if (element == EL_DC_MAGIC_WALL_FILLING)
8077 element = Feld[newx][newy] = get_next_element(element);
8078 if (!game.magic_wall_active)
8079 element = Feld[newx][newy] = EL_DC_MAGIC_WALL_DEAD;
8080 Store[newx][newy] = Store[x][y];
8082 else if (element == EL_DC_MAGIC_WALL_EMPTYING)
8084 Feld[x][y] = get_next_element(element);
8085 if (!game.magic_wall_active)
8086 Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
8087 element = Feld[newx][newy] = Store[x][y];
8089 InitField(newx, newy, FALSE);
8091 else if (element == EL_AMOEBA_DROPPING)
8093 Feld[x][y] = get_next_element(element);
8094 element = Feld[newx][newy] = Store[x][y];
8096 else if (element == EL_SOKOBAN_OBJECT)
8099 Feld[x][y] = Back[x][y];
8101 if (Back[newx][newy])
8102 Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
8104 Back[x][y] = Back[newx][newy] = 0;
8107 Store[x][y] = EL_EMPTY;
8112 MovDelay[newx][newy] = 0;
8114 if (CAN_CHANGE_OR_HAS_ACTION(element))
8116 /* copy element change control values to new field */
8117 ChangeDelay[newx][newy] = ChangeDelay[x][y];
8118 ChangePage[newx][newy] = ChangePage[x][y];
8119 ChangeCount[newx][newy] = ChangeCount[x][y];
8120 ChangeEvent[newx][newy] = ChangeEvent[x][y];
8123 CustomValue[newx][newy] = CustomValue[x][y];
8125 ChangeDelay[x][y] = 0;
8126 ChangePage[x][y] = -1;
8127 ChangeCount[x][y] = 0;
8128 ChangeEvent[x][y] = -1;
8130 CustomValue[x][y] = 0;
8132 /* copy animation control values to new field */
8133 GfxFrame[newx][newy] = GfxFrame[x][y];
8134 GfxRandom[newx][newy] = GfxRandom[x][y]; /* keep same random value */
8135 GfxAction[newx][newy] = GfxAction[x][y]; /* keep action one frame */
8136 GfxDir[newx][newy] = GfxDir[x][y]; /* keep element direction */
8138 Pushed[x][y] = Pushed[newx][newy] = FALSE;
8140 /* some elements can leave other elements behind after moving */
8141 if (ei->move_leave_element != EL_EMPTY &&
8142 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
8143 (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
8145 int move_leave_element = ei->move_leave_element;
8147 /* this makes it possible to leave the removed element again */
8148 if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
8149 move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
8151 Feld[x][y] = move_leave_element;
8153 if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
8154 MovDir[x][y] = direction;
8156 InitField(x, y, FALSE);
8158 if (GFX_CRUMBLED(Feld[x][y]))
8159 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8161 if (ELEM_IS_PLAYER(move_leave_element))
8162 RelocatePlayer(x, y, move_leave_element);
8165 /* do this after checking for left-behind element */
8166 ResetGfxAnimation(x, y); /* reset animation values for old field */
8168 if (!CAN_MOVE(element) ||
8169 (CAN_FALL(element) && direction == MV_DOWN &&
8170 (element == EL_SPRING ||
8171 element_info[element].move_pattern == MV_WHEN_PUSHED ||
8172 element_info[element].move_pattern == MV_WHEN_DROPPED)))
8173 GfxDir[x][y] = MovDir[newx][newy] = 0;
8175 TEST_DrawLevelField(x, y);
8176 TEST_DrawLevelField(newx, newy);
8178 Stop[newx][newy] = TRUE; /* ignore this element until the next frame */
8180 /* prevent pushed element from moving on in pushed direction */
8181 if (pushed_by_player && CAN_MOVE(element) &&
8182 element_info[element].move_pattern & MV_ANY_DIRECTION &&
8183 !(element_info[element].move_pattern & direction))
8184 TurnRound(newx, newy);
8186 /* prevent elements on conveyor belt from moving on in last direction */
8187 if (pushed_by_conveyor && CAN_FALL(element) &&
8188 direction & MV_HORIZONTAL)
8189 MovDir[newx][newy] = 0;
8191 if (!pushed_by_player)
8193 int nextx = newx + dx, nexty = newy + dy;
8194 boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
8196 WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
8198 if (CAN_FALL(element) && direction == MV_DOWN)
8199 WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
8201 if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
8202 CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
8204 if (CAN_FALL(element) && direction == MV_DOWN && check_collision_again)
8205 CheckImpact[newx][newy] = CHECK_DELAY_IMPACT;
8208 if (DONT_TOUCH(element)) /* object may be nasty to player or others */
8210 TestIfBadThingTouchesPlayer(newx, newy);
8211 TestIfBadThingTouchesFriend(newx, newy);
8213 if (!IS_CUSTOM_ELEMENT(element))
8214 TestIfBadThingTouchesOtherBadThing(newx, newy);
8216 else if (element == EL_PENGUIN)
8217 TestIfFriendTouchesBadThing(newx, newy);
8219 if (DONT_GET_HIT_BY(element))
8221 TestIfGoodThingGetsHitByBadThing(newx, newy, direction);
8224 /* give the player one last chance (one more frame) to move away */
8225 if (CAN_FALL(element) && direction == MV_DOWN &&
8226 (last_line || (!IS_FREE(x, newy + 1) &&
8227 (!IS_PLAYER(x, newy + 1) ||
8228 game.engine_version < VERSION_IDENT(3,1,1,0)))))
8231 if (pushed_by_player && !game.use_change_when_pushing_bug)
8233 int push_side = MV_DIR_OPPOSITE(direction);
8234 struct PlayerInfo *player = PLAYERINFO(x, y);
8236 CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
8237 player->index_bit, push_side);
8238 CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
8239 player->index_bit, push_side);
8242 if (element == EL_EMC_ANDROID && pushed_by_player) /* make another move */
8243 MovDelay[newx][newy] = 1;
8245 CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
8247 TestIfElementTouchesCustomElement(x, y); /* empty or new element */
8248 TestIfElementHitsCustomElement(newx, newy, direction);
8249 TestIfPlayerTouchesCustomElement(newx, newy);
8250 TestIfElementTouchesCustomElement(newx, newy);
8252 if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
8253 IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
8254 CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
8255 MV_DIR_OPPOSITE(direction));
8258 int AmoebeNachbarNr(int ax, int ay)
8261 int element = Feld[ax][ay];
8263 static int xy[4][2] =
8271 for (i = 0; i < NUM_DIRECTIONS; i++)
8273 int x = ax + xy[i][0];
8274 int y = ay + xy[i][1];
8276 if (!IN_LEV_FIELD(x, y))
8279 if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
8280 group_nr = AmoebaNr[x][y];
8286 void AmoebenVereinigen(int ax, int ay)
8288 int i, x, y, xx, yy;
8289 int new_group_nr = AmoebaNr[ax][ay];
8290 static int xy[4][2] =
8298 if (new_group_nr == 0)
8301 for (i = 0; i < NUM_DIRECTIONS; i++)
8306 if (!IN_LEV_FIELD(x, y))
8309 if ((Feld[x][y] == EL_AMOEBA_FULL ||
8310 Feld[x][y] == EL_BD_AMOEBA ||
8311 Feld[x][y] == EL_AMOEBA_DEAD) &&
8312 AmoebaNr[x][y] != new_group_nr)
8314 int old_group_nr = AmoebaNr[x][y];
8316 if (old_group_nr == 0)
8319 AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
8320 AmoebaCnt[old_group_nr] = 0;
8321 AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
8322 AmoebaCnt2[old_group_nr] = 0;
8324 SCAN_PLAYFIELD(xx, yy)
8326 if (AmoebaNr[xx][yy] == old_group_nr)
8327 AmoebaNr[xx][yy] = new_group_nr;
8333 void AmoebeUmwandeln(int ax, int ay)
8337 if (Feld[ax][ay] == EL_AMOEBA_DEAD)
8339 int group_nr = AmoebaNr[ax][ay];
8344 printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
8345 printf("AmoebeUmwandeln(): This should never happen!\n");
8350 SCAN_PLAYFIELD(x, y)
8352 if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
8355 Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
8359 PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
8360 SND_AMOEBA_TURNING_TO_GEM :
8361 SND_AMOEBA_TURNING_TO_ROCK));
8366 static int xy[4][2] =
8374 for (i = 0; i < NUM_DIRECTIONS; i++)
8379 if (!IN_LEV_FIELD(x, y))
8382 if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
8384 PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
8385 SND_AMOEBA_TURNING_TO_GEM :
8386 SND_AMOEBA_TURNING_TO_ROCK));
8393 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
8396 int group_nr = AmoebaNr[ax][ay];
8397 boolean done = FALSE;
8402 printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
8403 printf("AmoebeUmwandelnBD(): This should never happen!\n");
8408 SCAN_PLAYFIELD(x, y)
8410 if (AmoebaNr[x][y] == group_nr &&
8411 (Feld[x][y] == EL_AMOEBA_DEAD ||
8412 Feld[x][y] == EL_BD_AMOEBA ||
8413 Feld[x][y] == EL_AMOEBA_GROWING))
8416 Feld[x][y] = new_element;
8417 InitField(x, y, FALSE);
8418 TEST_DrawLevelField(x, y);
8424 PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
8425 SND_BD_AMOEBA_TURNING_TO_ROCK :
8426 SND_BD_AMOEBA_TURNING_TO_GEM));
8429 void AmoebeWaechst(int x, int y)
8431 static unsigned int sound_delay = 0;
8432 static unsigned int sound_delay_value = 0;
8434 if (!MovDelay[x][y]) /* start new growing cycle */
8438 if (DelayReached(&sound_delay, sound_delay_value))
8440 PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
8441 sound_delay_value = 30;
8445 if (MovDelay[x][y]) /* wait some time before growing bigger */
8448 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8450 int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
8451 6 - MovDelay[x][y]);
8453 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
8456 if (!MovDelay[x][y])
8458 Feld[x][y] = Store[x][y];
8460 TEST_DrawLevelField(x, y);
8465 void AmoebaDisappearing(int x, int y)
8467 static unsigned int sound_delay = 0;
8468 static unsigned int sound_delay_value = 0;
8470 if (!MovDelay[x][y]) /* start new shrinking cycle */
8474 if (DelayReached(&sound_delay, sound_delay_value))
8475 sound_delay_value = 30;
8478 if (MovDelay[x][y]) /* wait some time before shrinking */
8481 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8483 int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
8484 6 - MovDelay[x][y]);
8486 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
8489 if (!MovDelay[x][y])
8491 Feld[x][y] = EL_EMPTY;
8492 TEST_DrawLevelField(x, y);
8494 /* don't let mole enter this field in this cycle;
8495 (give priority to objects falling to this field from above) */
8501 void AmoebeAbleger(int ax, int ay)
8504 int element = Feld[ax][ay];
8505 int graphic = el2img(element);
8506 int newax = ax, neway = ay;
8507 boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
8508 static int xy[4][2] =
8516 if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
8518 Feld[ax][ay] = EL_AMOEBA_DEAD;
8519 TEST_DrawLevelField(ax, ay);
8523 if (IS_ANIMATED(graphic))
8524 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
8526 if (!MovDelay[ax][ay]) /* start making new amoeba field */
8527 MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
8529 if (MovDelay[ax][ay]) /* wait some time before making new amoeba */
8532 if (MovDelay[ax][ay])
8536 if (can_drop) /* EL_AMOEBA_WET or EL_EMC_DRIPPER */
8539 int x = ax + xy[start][0];
8540 int y = ay + xy[start][1];
8542 if (!IN_LEV_FIELD(x, y))
8545 if (IS_FREE(x, y) ||
8546 CAN_GROW_INTO(Feld[x][y]) ||
8547 Feld[x][y] == EL_QUICKSAND_EMPTY ||
8548 Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
8554 if (newax == ax && neway == ay)
8557 else /* normal or "filled" (BD style) amoeba */
8560 boolean waiting_for_player = FALSE;
8562 for (i = 0; i < NUM_DIRECTIONS; i++)
8564 int j = (start + i) % 4;
8565 int x = ax + xy[j][0];
8566 int y = ay + xy[j][1];
8568 if (!IN_LEV_FIELD(x, y))
8571 if (IS_FREE(x, y) ||
8572 CAN_GROW_INTO(Feld[x][y]) ||
8573 Feld[x][y] == EL_QUICKSAND_EMPTY ||
8574 Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
8580 else if (IS_PLAYER(x, y))
8581 waiting_for_player = TRUE;
8584 if (newax == ax && neway == ay) /* amoeba cannot grow */
8586 if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
8588 Feld[ax][ay] = EL_AMOEBA_DEAD;
8589 TEST_DrawLevelField(ax, ay);
8590 AmoebaCnt[AmoebaNr[ax][ay]]--;
8592 if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0) /* amoeba is completely dead */
8594 if (element == EL_AMOEBA_FULL)
8595 AmoebeUmwandeln(ax, ay);
8596 else if (element == EL_BD_AMOEBA)
8597 AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
8602 else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
8604 /* amoeba gets larger by growing in some direction */
8606 int new_group_nr = AmoebaNr[ax][ay];
8609 if (new_group_nr == 0)
8611 printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
8612 printf("AmoebeAbleger(): This should never happen!\n");
8617 AmoebaNr[newax][neway] = new_group_nr;
8618 AmoebaCnt[new_group_nr]++;
8619 AmoebaCnt2[new_group_nr]++;
8621 /* if amoeba touches other amoeba(s) after growing, unify them */
8622 AmoebenVereinigen(newax, neway);
8624 if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
8626 AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
8632 if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
8633 (neway == lev_fieldy - 1 && newax != ax))
8635 Feld[newax][neway] = EL_AMOEBA_GROWING; /* creation of new amoeba */
8636 Store[newax][neway] = element;
8638 else if (neway == ay || element == EL_EMC_DRIPPER)
8640 Feld[newax][neway] = EL_AMOEBA_DROP; /* drop left/right of amoeba */
8642 PlayLevelSoundAction(newax, neway, ACTION_GROWING);
8646 InitMovingField(ax, ay, MV_DOWN); /* drop dripping from amoeba */
8647 Feld[ax][ay] = EL_AMOEBA_DROPPING;
8648 Store[ax][ay] = EL_AMOEBA_DROP;
8649 ContinueMoving(ax, ay);
8653 TEST_DrawLevelField(newax, neway);
8656 void Life(int ax, int ay)
8660 int element = Feld[ax][ay];
8661 int graphic = el2img(element);
8662 int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
8664 boolean changed = FALSE;
8666 if (IS_ANIMATED(graphic))
8667 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
8672 if (!MovDelay[ax][ay]) /* start new "game of life" cycle */
8673 MovDelay[ax][ay] = life_time;
8675 if (MovDelay[ax][ay]) /* wait some time before next cycle */
8678 if (MovDelay[ax][ay])
8682 for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
8684 int xx = ax+x1, yy = ay+y1;
8687 if (!IN_LEV_FIELD(xx, yy))
8690 for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
8692 int x = xx+x2, y = yy+y2;
8694 if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
8697 if (((Feld[x][y] == element ||
8698 (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
8700 (IS_FREE(x, y) && Stop[x][y]))
8704 if (xx == ax && yy == ay) /* field in the middle */
8706 if (nachbarn < life_parameter[0] ||
8707 nachbarn > life_parameter[1])
8709 Feld[xx][yy] = EL_EMPTY;
8711 TEST_DrawLevelField(xx, yy);
8712 Stop[xx][yy] = TRUE;
8716 else if (IS_FREE(xx, yy) || CAN_GROW_INTO(Feld[xx][yy]))
8717 { /* free border field */
8718 if (nachbarn >= life_parameter[2] &&
8719 nachbarn <= life_parameter[3])
8721 Feld[xx][yy] = element;
8722 MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
8724 TEST_DrawLevelField(xx, yy);
8725 Stop[xx][yy] = TRUE;
8732 PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
8733 SND_GAME_OF_LIFE_GROWING);
8736 static void InitRobotWheel(int x, int y)
8738 ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
8741 static void RunRobotWheel(int x, int y)
8743 PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
8746 static void StopRobotWheel(int x, int y)
8748 if (ZX == x && ZY == y)
8752 game.robot_wheel_active = FALSE;
8756 static void InitTimegateWheel(int x, int y)
8758 ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
8761 static void RunTimegateWheel(int x, int y)
8763 PlayLevelSound(x, y, SND_CLASS_TIMEGATE_SWITCH_ACTIVE);
8766 static void InitMagicBallDelay(int x, int y)
8768 ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
8771 static void ActivateMagicBall(int bx, int by)
8775 if (level.ball_random)
8777 int pos_border = RND(8); /* select one of the eight border elements */
8778 int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
8779 int xx = pos_content % 3;
8780 int yy = pos_content / 3;
8785 if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
8786 CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
8790 for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
8792 int xx = x - bx + 1;
8793 int yy = y - by + 1;
8795 if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
8796 CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
8800 game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
8803 void CheckExit(int x, int y)
8805 if (local_player->gems_still_needed > 0 ||
8806 local_player->sokobanfields_still_needed > 0 ||
8807 local_player->lights_still_needed > 0)
8809 int element = Feld[x][y];
8810 int graphic = el2img(element);
8812 if (IS_ANIMATED(graphic))
8813 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8818 if (AllPlayersGone) /* do not re-open exit door closed after last player */
8821 Feld[x][y] = EL_EXIT_OPENING;
8823 PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
8826 void CheckExitEM(int x, int y)
8828 if (local_player->gems_still_needed > 0 ||
8829 local_player->sokobanfields_still_needed > 0 ||
8830 local_player->lights_still_needed > 0)
8832 int element = Feld[x][y];
8833 int graphic = el2img(element);
8835 if (IS_ANIMATED(graphic))
8836 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8841 if (AllPlayersGone) /* do not re-open exit door closed after last player */
8844 Feld[x][y] = EL_EM_EXIT_OPENING;
8846 PlayLevelSoundNearest(x, y, SND_CLASS_EM_EXIT_OPENING);
8849 void CheckExitSteel(int x, int y)
8851 if (local_player->gems_still_needed > 0 ||
8852 local_player->sokobanfields_still_needed > 0 ||
8853 local_player->lights_still_needed > 0)
8855 int element = Feld[x][y];
8856 int graphic = el2img(element);
8858 if (IS_ANIMATED(graphic))
8859 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8864 if (AllPlayersGone) /* do not re-open exit door closed after last player */
8867 Feld[x][y] = EL_STEEL_EXIT_OPENING;
8869 PlayLevelSoundNearest(x, y, SND_CLASS_STEEL_EXIT_OPENING);
8872 void CheckExitSteelEM(int x, int y)
8874 if (local_player->gems_still_needed > 0 ||
8875 local_player->sokobanfields_still_needed > 0 ||
8876 local_player->lights_still_needed > 0)
8878 int element = Feld[x][y];
8879 int graphic = el2img(element);
8881 if (IS_ANIMATED(graphic))
8882 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8887 if (AllPlayersGone) /* do not re-open exit door closed after last player */
8890 Feld[x][y] = EL_EM_STEEL_EXIT_OPENING;
8892 PlayLevelSoundNearest(x, y, SND_CLASS_EM_STEEL_EXIT_OPENING);
8895 void CheckExitSP(int x, int y)
8897 if (local_player->gems_still_needed > 0)
8899 int element = Feld[x][y];
8900 int graphic = el2img(element);
8902 if (IS_ANIMATED(graphic))
8903 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8908 if (AllPlayersGone) /* do not re-open exit door closed after last player */
8911 Feld[x][y] = EL_SP_EXIT_OPENING;
8913 PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
8916 static void CloseAllOpenTimegates()
8920 SCAN_PLAYFIELD(x, y)
8922 int element = Feld[x][y];
8924 if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
8926 Feld[x][y] = EL_TIMEGATE_CLOSING;
8928 PlayLevelSoundAction(x, y, ACTION_CLOSING);
8933 void DrawTwinkleOnField(int x, int y)
8935 if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
8938 if (Feld[x][y] == EL_BD_DIAMOND)
8941 if (MovDelay[x][y] == 0) /* next animation frame */
8942 MovDelay[x][y] = 11 * !GetSimpleRandom(500);
8944 if (MovDelay[x][y] != 0) /* wait some time before next frame */
8948 DrawLevelElementAnimation(x, y, Feld[x][y]);
8950 if (MovDelay[x][y] != 0)
8952 int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
8953 10 - MovDelay[x][y]);
8955 DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
8960 void MauerWaechst(int x, int y)
8964 if (!MovDelay[x][y]) /* next animation frame */
8965 MovDelay[x][y] = 3 * delay;
8967 if (MovDelay[x][y]) /* wait some time before next frame */
8971 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8973 int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
8974 int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
8976 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
8979 if (!MovDelay[x][y])
8981 if (MovDir[x][y] == MV_LEFT)
8983 if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
8984 TEST_DrawLevelField(x - 1, y);
8986 else if (MovDir[x][y] == MV_RIGHT)
8988 if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
8989 TEST_DrawLevelField(x + 1, y);
8991 else if (MovDir[x][y] == MV_UP)
8993 if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
8994 TEST_DrawLevelField(x, y - 1);
8998 if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
8999 TEST_DrawLevelField(x, y + 1);
9002 Feld[x][y] = Store[x][y];
9004 GfxDir[x][y] = MovDir[x][y] = MV_NONE;
9005 TEST_DrawLevelField(x, y);
9010 void MauerAbleger(int ax, int ay)
9012 int element = Feld[ax][ay];
9013 int graphic = el2img(element);
9014 boolean oben_frei = FALSE, unten_frei = FALSE;
9015 boolean links_frei = FALSE, rechts_frei = FALSE;
9016 boolean oben_massiv = FALSE, unten_massiv = FALSE;
9017 boolean links_massiv = FALSE, rechts_massiv = FALSE;
9018 boolean new_wall = FALSE;
9020 if (IS_ANIMATED(graphic))
9021 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9023 if (!MovDelay[ax][ay]) /* start building new wall */
9024 MovDelay[ax][ay] = 6;
9026 if (MovDelay[ax][ay]) /* wait some time before building new wall */
9029 if (MovDelay[ax][ay])
9033 if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9035 if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9037 if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9039 if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9042 if (element == EL_EXPANDABLE_WALL_VERTICAL ||
9043 element == EL_EXPANDABLE_WALL_ANY)
9047 Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
9048 Store[ax][ay-1] = element;
9049 GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9050 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9051 DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9052 IMG_EXPANDABLE_WALL_GROWING_UP, 0);
9057 Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
9058 Store[ax][ay+1] = element;
9059 GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9060 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9061 DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9062 IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
9067 if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9068 element == EL_EXPANDABLE_WALL_ANY ||
9069 element == EL_EXPANDABLE_WALL ||
9070 element == EL_BD_EXPANDABLE_WALL)
9074 Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
9075 Store[ax-1][ay] = element;
9076 GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9077 if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9078 DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9079 IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
9085 Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
9086 Store[ax+1][ay] = element;
9087 GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9088 if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9089 DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9090 IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
9095 if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
9096 TEST_DrawLevelField(ax, ay);
9098 if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
9100 if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
9101 unten_massiv = TRUE;
9102 if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
9103 links_massiv = TRUE;
9104 if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
9105 rechts_massiv = TRUE;
9107 if (((oben_massiv && unten_massiv) ||
9108 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9109 element == EL_EXPANDABLE_WALL) &&
9110 ((links_massiv && rechts_massiv) ||
9111 element == EL_EXPANDABLE_WALL_VERTICAL))
9112 Feld[ax][ay] = EL_WALL;
9115 PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9118 void MauerAblegerStahl(int ax, int ay)
9120 int element = Feld[ax][ay];
9121 int graphic = el2img(element);
9122 boolean oben_frei = FALSE, unten_frei = FALSE;
9123 boolean links_frei = FALSE, rechts_frei = FALSE;
9124 boolean oben_massiv = FALSE, unten_massiv = FALSE;
9125 boolean links_massiv = FALSE, rechts_massiv = FALSE;
9126 boolean new_wall = FALSE;
9128 if (IS_ANIMATED(graphic))
9129 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9131 if (!MovDelay[ax][ay]) /* start building new wall */
9132 MovDelay[ax][ay] = 6;
9134 if (MovDelay[ax][ay]) /* wait some time before building new wall */
9137 if (MovDelay[ax][ay])
9141 if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9143 if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9145 if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9147 if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9150 if (element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
9151 element == EL_EXPANDABLE_STEELWALL_ANY)
9155 Feld[ax][ay-1] = EL_EXPANDABLE_STEELWALL_GROWING;
9156 Store[ax][ay-1] = element;
9157 GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9158 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9159 DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9160 IMG_EXPANDABLE_STEELWALL_GROWING_UP, 0);
9165 Feld[ax][ay+1] = EL_EXPANDABLE_STEELWALL_GROWING;
9166 Store[ax][ay+1] = element;
9167 GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9168 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9169 DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9170 IMG_EXPANDABLE_STEELWALL_GROWING_DOWN, 0);
9175 if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
9176 element == EL_EXPANDABLE_STEELWALL_ANY)
9180 Feld[ax-1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9181 Store[ax-1][ay] = element;
9182 GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9183 if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9184 DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9185 IMG_EXPANDABLE_STEELWALL_GROWING_LEFT, 0);
9191 Feld[ax+1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9192 Store[ax+1][ay] = element;
9193 GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9194 if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9195 DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9196 IMG_EXPANDABLE_STEELWALL_GROWING_RIGHT, 0);
9201 if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
9203 if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
9204 unten_massiv = TRUE;
9205 if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
9206 links_massiv = TRUE;
9207 if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
9208 rechts_massiv = TRUE;
9210 if (((oben_massiv && unten_massiv) ||
9211 element == EL_EXPANDABLE_STEELWALL_HORIZONTAL) &&
9212 ((links_massiv && rechts_massiv) ||
9213 element == EL_EXPANDABLE_STEELWALL_VERTICAL))
9214 Feld[ax][ay] = EL_STEELWALL;
9217 PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9220 void CheckForDragon(int x, int y)
9223 boolean dragon_found = FALSE;
9224 static int xy[4][2] =
9232 for (i = 0; i < NUM_DIRECTIONS; i++)
9234 for (j = 0; j < 4; j++)
9236 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9238 if (IN_LEV_FIELD(xx, yy) &&
9239 (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
9241 if (Feld[xx][yy] == EL_DRAGON)
9242 dragon_found = TRUE;
9251 for (i = 0; i < NUM_DIRECTIONS; i++)
9253 for (j = 0; j < 3; j++)
9255 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9257 if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
9259 Feld[xx][yy] = EL_EMPTY;
9260 TEST_DrawLevelField(xx, yy);
9269 static void InitBuggyBase(int x, int y)
9271 int element = Feld[x][y];
9272 int activating_delay = FRAMES_PER_SECOND / 4;
9275 (element == EL_SP_BUGGY_BASE ?
9276 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
9277 element == EL_SP_BUGGY_BASE_ACTIVATING ?
9279 element == EL_SP_BUGGY_BASE_ACTIVE ?
9280 1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
9283 static void WarnBuggyBase(int x, int y)
9286 static int xy[4][2] =
9294 for (i = 0; i < NUM_DIRECTIONS; i++)
9296 int xx = x + xy[i][0];
9297 int yy = y + xy[i][1];
9299 if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
9301 PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
9308 static void InitTrap(int x, int y)
9310 ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
9313 static void ActivateTrap(int x, int y)
9315 PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
9318 static void ChangeActiveTrap(int x, int y)
9320 int graphic = IMG_TRAP_ACTIVE;
9322 /* if new animation frame was drawn, correct crumbled sand border */
9323 if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
9324 TEST_DrawLevelFieldCrumbled(x, y);
9327 static int getSpecialActionElement(int element, int number, int base_element)
9329 return (element != EL_EMPTY ? element :
9330 number != -1 ? base_element + number - 1 :
9334 static int getModifiedActionNumber(int value_old, int operator, int operand,
9335 int value_min, int value_max)
9337 int value_new = (operator == CA_MODE_SET ? operand :
9338 operator == CA_MODE_ADD ? value_old + operand :
9339 operator == CA_MODE_SUBTRACT ? value_old - operand :
9340 operator == CA_MODE_MULTIPLY ? value_old * operand :
9341 operator == CA_MODE_DIVIDE ? value_old / MAX(1, operand) :
9342 operator == CA_MODE_MODULO ? value_old % MAX(1, operand) :
9345 return (value_new < value_min ? value_min :
9346 value_new > value_max ? value_max :
9350 static void ExecuteCustomElementAction(int x, int y, int element, int page)
9352 struct ElementInfo *ei = &element_info[element];
9353 struct ElementChangeInfo *change = &ei->change_page[page];
9354 int target_element = change->target_element;
9355 int action_type = change->action_type;
9356 int action_mode = change->action_mode;
9357 int action_arg = change->action_arg;
9358 int action_element = change->action_element;
9361 if (!change->has_action)
9364 /* ---------- determine action paramater values -------------------------- */
9366 int level_time_value =
9367 (level.time > 0 ? TimeLeft :
9370 int action_arg_element_raw =
9371 (action_arg == CA_ARG_PLAYER_TRIGGER ? change->actual_trigger_player :
9372 action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
9373 action_arg == CA_ARG_ELEMENT_TARGET ? change->target_element :
9374 action_arg == CA_ARG_ELEMENT_ACTION ? change->action_element :
9375 action_arg == CA_ARG_INVENTORY_RM_TRIGGER ? change->actual_trigger_element:
9376 action_arg == CA_ARG_INVENTORY_RM_TARGET ? change->target_element :
9377 action_arg == CA_ARG_INVENTORY_RM_ACTION ? change->action_element :
9379 int action_arg_element = GetElementFromGroupElement(action_arg_element_raw);
9381 int action_arg_direction =
9382 (action_arg >= CA_ARG_DIRECTION_LEFT &&
9383 action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
9384 action_arg == CA_ARG_DIRECTION_TRIGGER ?
9385 change->actual_trigger_side :
9386 action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
9387 MV_DIR_OPPOSITE(change->actual_trigger_side) :
9390 int action_arg_number_min =
9391 (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
9394 int action_arg_number_max =
9395 (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
9396 action_type == CA_SET_LEVEL_GEMS ? 999 :
9397 action_type == CA_SET_LEVEL_TIME ? 9999 :
9398 action_type == CA_SET_LEVEL_SCORE ? 99999 :
9399 action_type == CA_SET_CE_VALUE ? 9999 :
9400 action_type == CA_SET_CE_SCORE ? 9999 :
9403 int action_arg_number_reset =
9404 (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize[0] :
9405 action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
9406 action_type == CA_SET_LEVEL_TIME ? level.time :
9407 action_type == CA_SET_LEVEL_SCORE ? 0 :
9408 action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
9409 action_type == CA_SET_CE_SCORE ? 0 :
9412 int action_arg_number =
9413 (action_arg <= CA_ARG_MAX ? action_arg :
9414 action_arg >= CA_ARG_SPEED_NOT_MOVING &&
9415 action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
9416 action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
9417 action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
9418 action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
9419 action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
9420 action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
9421 action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
9422 action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
9423 action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
9424 action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? local_player->gems_still_needed :
9425 action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? local_player->score :
9426 action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
9427 action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
9428 action_arg == CA_ARG_ELEMENT_CV_ACTION ? GET_NEW_CE_VALUE(action_element):
9429 action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
9430 action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
9431 action_arg == CA_ARG_ELEMENT_CS_ACTION ? GET_CE_SCORE(action_element) :
9432 action_arg == CA_ARG_ELEMENT_NR_TARGET ? change->target_element :
9433 action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
9434 action_arg == CA_ARG_ELEMENT_NR_ACTION ? change->action_element :
9437 int action_arg_number_old =
9438 (action_type == CA_SET_LEVEL_GEMS ? local_player->gems_still_needed :
9439 action_type == CA_SET_LEVEL_TIME ? TimeLeft :
9440 action_type == CA_SET_LEVEL_SCORE ? local_player->score :
9441 action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
9442 action_type == CA_SET_CE_SCORE ? ei->collect_score :
9445 int action_arg_number_new =
9446 getModifiedActionNumber(action_arg_number_old,
9447 action_mode, action_arg_number,
9448 action_arg_number_min, action_arg_number_max);
9450 int trigger_player_bits =
9451 (change->actual_trigger_player_bits != CH_PLAYER_NONE ?
9452 change->actual_trigger_player_bits : change->trigger_player);
9454 int action_arg_player_bits =
9455 (action_arg >= CA_ARG_PLAYER_1 &&
9456 action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
9457 action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
9458 action_arg == CA_ARG_PLAYER_ACTION ? 1 << GET_PLAYER_NR(action_element) :
9461 /* ---------- execute action -------------------------------------------- */
9463 switch (action_type)
9470 /* ---------- level actions ------------------------------------------- */
9472 case CA_RESTART_LEVEL:
9474 game.restart_level = TRUE;
9479 case CA_SHOW_ENVELOPE:
9481 int element = getSpecialActionElement(action_arg_element,
9482 action_arg_number, EL_ENVELOPE_1);
9484 if (IS_ENVELOPE(element))
9485 local_player->show_envelope = element;
9490 case CA_SET_LEVEL_TIME:
9492 if (level.time > 0) /* only modify limited time value */
9494 TimeLeft = action_arg_number_new;
9496 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
9498 DisplayGameControlValues();
9500 if (!TimeLeft && setup.time_limit)
9501 for (i = 0; i < MAX_PLAYERS; i++)
9502 KillPlayer(&stored_player[i]);
9508 case CA_SET_LEVEL_SCORE:
9510 local_player->score = action_arg_number_new;
9512 game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
9514 DisplayGameControlValues();
9519 case CA_SET_LEVEL_GEMS:
9521 local_player->gems_still_needed = action_arg_number_new;
9523 game_panel_controls[GAME_PANEL_GEMS].value =
9524 local_player->gems_still_needed;
9526 DisplayGameControlValues();
9531 case CA_SET_LEVEL_WIND:
9533 game.wind_direction = action_arg_direction;
9538 case CA_SET_LEVEL_RANDOM_SEED:
9540 /* ensure that setting a new random seed while playing is predictable */
9541 InitRND(action_arg_number_new ? action_arg_number_new : RND(1000000) + 1);
9546 /* ---------- player actions ------------------------------------------ */
9548 case CA_MOVE_PLAYER:
9550 /* automatically move to the next field in specified direction */
9551 for (i = 0; i < MAX_PLAYERS; i++)
9552 if (trigger_player_bits & (1 << i))
9553 stored_player[i].programmed_action = action_arg_direction;
9558 case CA_EXIT_PLAYER:
9560 for (i = 0; i < MAX_PLAYERS; i++)
9561 if (action_arg_player_bits & (1 << i))
9562 PlayerWins(&stored_player[i]);
9567 case CA_KILL_PLAYER:
9569 for (i = 0; i < MAX_PLAYERS; i++)
9570 if (action_arg_player_bits & (1 << i))
9571 KillPlayer(&stored_player[i]);
9576 case CA_SET_PLAYER_KEYS:
9578 int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
9579 int element = getSpecialActionElement(action_arg_element,
9580 action_arg_number, EL_KEY_1);
9582 if (IS_KEY(element))
9584 for (i = 0; i < MAX_PLAYERS; i++)
9586 if (trigger_player_bits & (1 << i))
9588 stored_player[i].key[KEY_NR(element)] = key_state;
9590 DrawGameDoorValues();
9598 case CA_SET_PLAYER_SPEED:
9600 for (i = 0; i < MAX_PLAYERS; i++)
9602 if (trigger_player_bits & (1 << i))
9604 int move_stepsize = TILEX / stored_player[i].move_delay_value;
9606 if (action_arg == CA_ARG_SPEED_FASTER &&
9607 stored_player[i].cannot_move)
9609 action_arg_number = STEPSIZE_VERY_SLOW;
9611 else if (action_arg == CA_ARG_SPEED_SLOWER ||
9612 action_arg == CA_ARG_SPEED_FASTER)
9614 action_arg_number = 2;
9615 action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
9618 else if (action_arg == CA_ARG_NUMBER_RESET)
9620 action_arg_number = level.initial_player_stepsize[i];
9624 getModifiedActionNumber(move_stepsize,
9627 action_arg_number_min,
9628 action_arg_number_max);
9630 SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
9637 case CA_SET_PLAYER_SHIELD:
9639 for (i = 0; i < MAX_PLAYERS; i++)
9641 if (trigger_player_bits & (1 << i))
9643 if (action_arg == CA_ARG_SHIELD_OFF)
9645 stored_player[i].shield_normal_time_left = 0;
9646 stored_player[i].shield_deadly_time_left = 0;
9648 else if (action_arg == CA_ARG_SHIELD_NORMAL)
9650 stored_player[i].shield_normal_time_left = 999999;
9652 else if (action_arg == CA_ARG_SHIELD_DEADLY)
9654 stored_player[i].shield_normal_time_left = 999999;
9655 stored_player[i].shield_deadly_time_left = 999999;
9663 case CA_SET_PLAYER_GRAVITY:
9665 for (i = 0; i < MAX_PLAYERS; i++)
9667 if (trigger_player_bits & (1 << i))
9669 stored_player[i].gravity =
9670 (action_arg == CA_ARG_GRAVITY_OFF ? FALSE :
9671 action_arg == CA_ARG_GRAVITY_ON ? TRUE :
9672 action_arg == CA_ARG_GRAVITY_TOGGLE ? !stored_player[i].gravity :
9673 stored_player[i].gravity);
9680 case CA_SET_PLAYER_ARTWORK:
9682 for (i = 0; i < MAX_PLAYERS; i++)
9684 if (trigger_player_bits & (1 << i))
9686 int artwork_element = action_arg_element;
9688 if (action_arg == CA_ARG_ELEMENT_RESET)
9690 (level.use_artwork_element[i] ? level.artwork_element[i] :
9691 stored_player[i].element_nr);
9693 if (stored_player[i].artwork_element != artwork_element)
9694 stored_player[i].Frame = 0;
9696 stored_player[i].artwork_element = artwork_element;
9698 SetPlayerWaiting(&stored_player[i], FALSE);
9700 /* set number of special actions for bored and sleeping animation */
9701 stored_player[i].num_special_action_bored =
9702 get_num_special_action(artwork_element,
9703 ACTION_BORING_1, ACTION_BORING_LAST);
9704 stored_player[i].num_special_action_sleeping =
9705 get_num_special_action(artwork_element,
9706 ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
9713 case CA_SET_PLAYER_INVENTORY:
9715 for (i = 0; i < MAX_PLAYERS; i++)
9717 struct PlayerInfo *player = &stored_player[i];
9720 if (trigger_player_bits & (1 << i))
9722 int inventory_element = action_arg_element;
9724 if (action_arg == CA_ARG_ELEMENT_TARGET ||
9725 action_arg == CA_ARG_ELEMENT_TRIGGER ||
9726 action_arg == CA_ARG_ELEMENT_ACTION)
9728 int element = inventory_element;
9729 int collect_count = element_info[element].collect_count_initial;
9731 if (!IS_CUSTOM_ELEMENT(element))
9734 if (collect_count == 0)
9735 player->inventory_infinite_element = element;
9737 for (k = 0; k < collect_count; k++)
9738 if (player->inventory_size < MAX_INVENTORY_SIZE)
9739 player->inventory_element[player->inventory_size++] =
9742 else if (action_arg == CA_ARG_INVENTORY_RM_TARGET ||
9743 action_arg == CA_ARG_INVENTORY_RM_TRIGGER ||
9744 action_arg == CA_ARG_INVENTORY_RM_ACTION)
9746 if (player->inventory_infinite_element != EL_UNDEFINED &&
9747 IS_EQUAL_OR_IN_GROUP(player->inventory_infinite_element,
9748 action_arg_element_raw))
9749 player->inventory_infinite_element = EL_UNDEFINED;
9751 for (k = 0, j = 0; j < player->inventory_size; j++)
9753 if (!IS_EQUAL_OR_IN_GROUP(player->inventory_element[j],
9754 action_arg_element_raw))
9755 player->inventory_element[k++] = player->inventory_element[j];
9758 player->inventory_size = k;
9760 else if (action_arg == CA_ARG_INVENTORY_RM_FIRST)
9762 if (player->inventory_size > 0)
9764 for (j = 0; j < player->inventory_size - 1; j++)
9765 player->inventory_element[j] = player->inventory_element[j + 1];
9767 player->inventory_size--;
9770 else if (action_arg == CA_ARG_INVENTORY_RM_LAST)
9772 if (player->inventory_size > 0)
9773 player->inventory_size--;
9775 else if (action_arg == CA_ARG_INVENTORY_RM_ALL)
9777 player->inventory_infinite_element = EL_UNDEFINED;
9778 player->inventory_size = 0;
9780 else if (action_arg == CA_ARG_INVENTORY_RESET)
9782 player->inventory_infinite_element = EL_UNDEFINED;
9783 player->inventory_size = 0;
9785 if (level.use_initial_inventory[i])
9787 for (j = 0; j < level.initial_inventory_size[i]; j++)
9789 int element = level.initial_inventory_content[i][j];
9790 int collect_count = element_info[element].collect_count_initial;
9792 if (!IS_CUSTOM_ELEMENT(element))
9795 if (collect_count == 0)
9796 player->inventory_infinite_element = element;
9798 for (k = 0; k < collect_count; k++)
9799 if (player->inventory_size < MAX_INVENTORY_SIZE)
9800 player->inventory_element[player->inventory_size++] =
9811 /* ---------- CE actions ---------------------------------------------- */
9813 case CA_SET_CE_VALUE:
9815 int last_ce_value = CustomValue[x][y];
9817 CustomValue[x][y] = action_arg_number_new;
9819 if (CustomValue[x][y] != last_ce_value)
9821 CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_CHANGES);
9822 CheckTriggeredElementChange(x, y, element, CE_VALUE_CHANGES_OF_X);
9824 if (CustomValue[x][y] == 0)
9826 CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
9827 CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
9834 case CA_SET_CE_SCORE:
9836 int last_ce_score = ei->collect_score;
9838 ei->collect_score = action_arg_number_new;
9840 if (ei->collect_score != last_ce_score)
9842 CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
9843 CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
9845 if (ei->collect_score == 0)
9849 CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
9850 CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
9853 This is a very special case that seems to be a mixture between
9854 CheckElementChange() and CheckTriggeredElementChange(): while
9855 the first one only affects single elements that are triggered
9856 directly, the second one affects multiple elements in the playfield
9857 that are triggered indirectly by another element. This is a third
9858 case: Changing the CE score always affects multiple identical CEs,
9859 so every affected CE must be checked, not only the single CE for
9860 which the CE score was changed in the first place (as every instance
9861 of that CE shares the same CE score, and therefore also can change)!
9863 SCAN_PLAYFIELD(xx, yy)
9865 if (Feld[xx][yy] == element)
9866 CheckElementChange(xx, yy, element, EL_UNDEFINED,
9867 CE_SCORE_GETS_ZERO);
9875 case CA_SET_CE_ARTWORK:
9877 int artwork_element = action_arg_element;
9878 boolean reset_frame = FALSE;
9881 if (action_arg == CA_ARG_ELEMENT_RESET)
9882 artwork_element = (ei->use_gfx_element ? ei->gfx_element_initial :
9885 if (ei->gfx_element != artwork_element)
9888 ei->gfx_element = artwork_element;
9890 SCAN_PLAYFIELD(xx, yy)
9892 if (Feld[xx][yy] == element)
9896 ResetGfxAnimation(xx, yy);
9897 ResetRandomAnimationValue(xx, yy);
9900 TEST_DrawLevelField(xx, yy);
9907 /* ---------- engine actions ------------------------------------------ */
9909 case CA_SET_ENGINE_SCAN_MODE:
9911 InitPlayfieldScanMode(action_arg);
9921 static void CreateFieldExt(int x, int y, int element, boolean is_change)
9923 int old_element = Feld[x][y];
9924 int new_element = GetElementFromGroupElement(element);
9925 int previous_move_direction = MovDir[x][y];
9926 int last_ce_value = CustomValue[x][y];
9927 boolean player_explosion_protected = PLAYER_EXPLOSION_PROTECTED(x, y);
9928 boolean new_element_is_player = ELEM_IS_PLAYER(new_element);
9929 boolean add_player_onto_element = (new_element_is_player &&
9930 new_element != EL_SOKOBAN_FIELD_PLAYER &&
9931 IS_WALKABLE(old_element));
9933 if (!add_player_onto_element)
9935 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
9936 RemoveMovingField(x, y);
9940 Feld[x][y] = new_element;
9942 if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
9943 MovDir[x][y] = previous_move_direction;
9945 if (element_info[new_element].use_last_ce_value)
9946 CustomValue[x][y] = last_ce_value;
9948 InitField_WithBug1(x, y, FALSE);
9950 new_element = Feld[x][y]; /* element may have changed */
9952 ResetGfxAnimation(x, y);
9953 ResetRandomAnimationValue(x, y);
9955 TEST_DrawLevelField(x, y);
9957 if (GFX_CRUMBLED(new_element))
9958 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
9961 /* check if element under the player changes from accessible to unaccessible
9962 (needed for special case of dropping element which then changes) */
9963 /* (must be checked after creating new element for walkable group elements) */
9964 if (IS_PLAYER(x, y) && !player_explosion_protected &&
9965 IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
9972 /* "ChangeCount" not set yet to allow "entered by player" change one time */
9973 if (new_element_is_player)
9974 RelocatePlayer(x, y, new_element);
9977 ChangeCount[x][y]++; /* count number of changes in the same frame */
9979 TestIfBadThingTouchesPlayer(x, y);
9980 TestIfPlayerTouchesCustomElement(x, y);
9981 TestIfElementTouchesCustomElement(x, y);
9984 static void CreateField(int x, int y, int element)
9986 CreateFieldExt(x, y, element, FALSE);
9989 static void CreateElementFromChange(int x, int y, int element)
9991 element = GET_VALID_RUNTIME_ELEMENT(element);
9993 if (game.engine_version >= VERSION_IDENT(3,2,0,7))
9995 int old_element = Feld[x][y];
9997 /* prevent changed element from moving in same engine frame
9998 unless both old and new element can either fall or move */
9999 if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
10000 (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
10004 CreateFieldExt(x, y, element, TRUE);
10007 static boolean ChangeElement(int x, int y, int element, int page)
10009 struct ElementInfo *ei = &element_info[element];
10010 struct ElementChangeInfo *change = &ei->change_page[page];
10011 int ce_value = CustomValue[x][y];
10012 int ce_score = ei->collect_score;
10013 int target_element;
10014 int old_element = Feld[x][y];
10016 /* always use default change event to prevent running into a loop */
10017 if (ChangeEvent[x][y] == -1)
10018 ChangeEvent[x][y] = CE_DELAY;
10020 if (ChangeEvent[x][y] == CE_DELAY)
10022 /* reset actual trigger element, trigger player and action element */
10023 change->actual_trigger_element = EL_EMPTY;
10024 change->actual_trigger_player = EL_EMPTY;
10025 change->actual_trigger_player_bits = CH_PLAYER_NONE;
10026 change->actual_trigger_side = CH_SIDE_NONE;
10027 change->actual_trigger_ce_value = 0;
10028 change->actual_trigger_ce_score = 0;
10031 /* do not change elements more than a specified maximum number of changes */
10032 if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
10035 ChangeCount[x][y]++; /* count number of changes in the same frame */
10037 if (change->explode)
10044 if (change->use_target_content)
10046 boolean complete_replace = TRUE;
10047 boolean can_replace[3][3];
10050 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10053 boolean is_walkable;
10054 boolean is_diggable;
10055 boolean is_collectible;
10056 boolean is_removable;
10057 boolean is_destructible;
10058 int ex = x + xx - 1;
10059 int ey = y + yy - 1;
10060 int content_element = change->target_content.e[xx][yy];
10063 can_replace[xx][yy] = TRUE;
10065 if (ex == x && ey == y) /* do not check changing element itself */
10068 if (content_element == EL_EMPTY_SPACE)
10070 can_replace[xx][yy] = FALSE; /* do not replace border with space */
10075 if (!IN_LEV_FIELD(ex, ey))
10077 can_replace[xx][yy] = FALSE;
10078 complete_replace = FALSE;
10085 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10086 e = MovingOrBlocked2Element(ex, ey);
10088 is_empty = (IS_FREE(ex, ey) ||
10089 (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
10091 is_walkable = (is_empty || IS_WALKABLE(e));
10092 is_diggable = (is_empty || IS_DIGGABLE(e));
10093 is_collectible = (is_empty || IS_COLLECTIBLE(e));
10094 is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
10095 is_removable = (is_diggable || is_collectible);
10097 can_replace[xx][yy] =
10098 (((change->replace_when == CP_WHEN_EMPTY && is_empty) ||
10099 (change->replace_when == CP_WHEN_WALKABLE && is_walkable) ||
10100 (change->replace_when == CP_WHEN_DIGGABLE && is_diggable) ||
10101 (change->replace_when == CP_WHEN_COLLECTIBLE && is_collectible) ||
10102 (change->replace_when == CP_WHEN_REMOVABLE && is_removable) ||
10103 (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
10104 !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
10106 if (!can_replace[xx][yy])
10107 complete_replace = FALSE;
10110 if (!change->only_if_complete || complete_replace)
10112 boolean something_has_changed = FALSE;
10114 if (change->only_if_complete && change->use_random_replace &&
10115 RND(100) < change->random_percentage)
10118 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10120 int ex = x + xx - 1;
10121 int ey = y + yy - 1;
10122 int content_element;
10124 if (can_replace[xx][yy] && (!change->use_random_replace ||
10125 RND(100) < change->random_percentage))
10127 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10128 RemoveMovingField(ex, ey);
10130 ChangeEvent[ex][ey] = ChangeEvent[x][y];
10132 content_element = change->target_content.e[xx][yy];
10133 target_element = GET_TARGET_ELEMENT(element, content_element, change,
10134 ce_value, ce_score);
10136 CreateElementFromChange(ex, ey, target_element);
10138 something_has_changed = TRUE;
10140 /* for symmetry reasons, freeze newly created border elements */
10141 if (ex != x || ey != y)
10142 Stop[ex][ey] = TRUE; /* no more moving in this frame */
10146 if (something_has_changed)
10148 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10149 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10155 target_element = GET_TARGET_ELEMENT(element, change->target_element, change,
10156 ce_value, ce_score);
10158 if (element == EL_DIAGONAL_GROWING ||
10159 element == EL_DIAGONAL_SHRINKING)
10161 target_element = Store[x][y];
10163 Store[x][y] = EL_EMPTY;
10166 CreateElementFromChange(x, y, target_element);
10168 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10169 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10172 /* this uses direct change before indirect change */
10173 CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
10178 static void HandleElementChange(int x, int y, int page)
10180 int element = MovingOrBlocked2Element(x, y);
10181 struct ElementInfo *ei = &element_info[element];
10182 struct ElementChangeInfo *change = &ei->change_page[page];
10183 boolean handle_action_before_change = FALSE;
10186 if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
10187 !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
10190 printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
10191 x, y, element, element_info[element].token_name);
10192 printf("HandleElementChange(): This should never happen!\n");
10197 /* this can happen with classic bombs on walkable, changing elements */
10198 if (!CAN_CHANGE_OR_HAS_ACTION(element))
10203 if (ChangeDelay[x][y] == 0) /* initialize element change */
10205 ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
10207 if (change->can_change)
10209 /* !!! not clear why graphic animation should be reset at all here !!! */
10210 /* !!! UPDATE: but is needed for correct Snake Bite tail animation !!! */
10211 /* when a custom element is about to change (for example by change delay),
10212 do not reset graphic animation when the custom element is moving */
10213 if (!IS_MOVING(x, y))
10215 ResetGfxAnimation(x, y);
10216 ResetRandomAnimationValue(x, y);
10219 if (change->pre_change_function)
10220 change->pre_change_function(x, y);
10224 ChangeDelay[x][y]--;
10226 if (ChangeDelay[x][y] != 0) /* continue element change */
10228 if (change->can_change)
10230 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10232 if (IS_ANIMATED(graphic))
10233 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10235 if (change->change_function)
10236 change->change_function(x, y);
10239 else /* finish element change */
10241 if (ChangePage[x][y] != -1) /* remember page from delayed change */
10243 page = ChangePage[x][y];
10244 ChangePage[x][y] = -1;
10246 change = &ei->change_page[page];
10249 if (IS_MOVING(x, y)) /* never change a running system ;-) */
10251 ChangeDelay[x][y] = 1; /* try change after next move step */
10252 ChangePage[x][y] = page; /* remember page to use for change */
10257 /* special case: set new level random seed before changing element */
10258 if (change->has_action && change->action_type == CA_SET_LEVEL_RANDOM_SEED)
10259 handle_action_before_change = TRUE;
10261 if (change->has_action && handle_action_before_change)
10262 ExecuteCustomElementAction(x, y, element, page);
10264 if (change->can_change)
10266 if (ChangeElement(x, y, element, page))
10268 if (change->post_change_function)
10269 change->post_change_function(x, y);
10273 if (change->has_action && !handle_action_before_change)
10274 ExecuteCustomElementAction(x, y, element, page);
10278 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
10279 int trigger_element,
10281 int trigger_player,
10285 boolean change_done_any = FALSE;
10286 int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
10289 if (!(trigger_events[trigger_element][trigger_event]))
10292 RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
10294 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
10296 int element = EL_CUSTOM_START + i;
10297 boolean change_done = FALSE;
10300 if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
10301 !HAS_ANY_CHANGE_EVENT(element, trigger_event))
10304 for (p = 0; p < element_info[element].num_change_pages; p++)
10306 struct ElementChangeInfo *change = &element_info[element].change_page[p];
10308 if (change->can_change_or_has_action &&
10309 change->has_event[trigger_event] &&
10310 change->trigger_side & trigger_side &&
10311 change->trigger_player & trigger_player &&
10312 change->trigger_page & trigger_page_bits &&
10313 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
10315 change->actual_trigger_element = trigger_element;
10316 change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
10317 change->actual_trigger_player_bits = trigger_player;
10318 change->actual_trigger_side = trigger_side;
10319 change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
10320 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10322 if ((change->can_change && !change_done) || change->has_action)
10326 SCAN_PLAYFIELD(x, y)
10328 if (Feld[x][y] == element)
10330 if (change->can_change && !change_done)
10332 /* if element already changed in this frame, not only prevent
10333 another element change (checked in ChangeElement()), but
10334 also prevent additional element actions for this element */
10336 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
10337 !level.use_action_after_change_bug)
10340 ChangeDelay[x][y] = 1;
10341 ChangeEvent[x][y] = trigger_event;
10343 HandleElementChange(x, y, p);
10345 else if (change->has_action)
10347 /* if element already changed in this frame, not only prevent
10348 another element change (checked in ChangeElement()), but
10349 also prevent additional element actions for this element */
10351 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
10352 !level.use_action_after_change_bug)
10355 ExecuteCustomElementAction(x, y, element, p);
10356 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10361 if (change->can_change)
10363 change_done = TRUE;
10364 change_done_any = TRUE;
10371 RECURSION_LOOP_DETECTION_END();
10373 return change_done_any;
10376 static boolean CheckElementChangeExt(int x, int y,
10378 int trigger_element,
10380 int trigger_player,
10383 boolean change_done = FALSE;
10386 if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
10387 !HAS_ANY_CHANGE_EVENT(element, trigger_event))
10390 if (Feld[x][y] == EL_BLOCKED)
10392 Blocked2Moving(x, y, &x, &y);
10393 element = Feld[x][y];
10396 /* check if element has already changed or is about to change after moving */
10397 if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
10398 Feld[x][y] != element) ||
10400 (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
10401 (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
10402 ChangePage[x][y] != -1)))
10405 RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
10407 for (p = 0; p < element_info[element].num_change_pages; p++)
10409 struct ElementChangeInfo *change = &element_info[element].change_page[p];
10411 /* check trigger element for all events where the element that is checked
10412 for changing interacts with a directly adjacent element -- this is
10413 different to element changes that affect other elements to change on the
10414 whole playfield (which is handeld by CheckTriggeredElementChangeExt()) */
10415 boolean check_trigger_element =
10416 (trigger_event == CE_TOUCHING_X ||
10417 trigger_event == CE_HITTING_X ||
10418 trigger_event == CE_HIT_BY_X ||
10419 trigger_event == CE_DIGGING_X); /* this one was forgotten until 3.2.3 */
10421 if (change->can_change_or_has_action &&
10422 change->has_event[trigger_event] &&
10423 change->trigger_side & trigger_side &&
10424 change->trigger_player & trigger_player &&
10425 (!check_trigger_element ||
10426 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
10428 change->actual_trigger_element = trigger_element;
10429 change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
10430 change->actual_trigger_player_bits = trigger_player;
10431 change->actual_trigger_side = trigger_side;
10432 change->actual_trigger_ce_value = CustomValue[x][y];
10433 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10435 /* special case: trigger element not at (x,y) position for some events */
10436 if (check_trigger_element)
10448 { 0, 0 }, { 0, 0 }, { 0, 0 },
10452 int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
10453 int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
10455 change->actual_trigger_ce_value = CustomValue[xx][yy];
10456 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10459 if (change->can_change && !change_done)
10461 ChangeDelay[x][y] = 1;
10462 ChangeEvent[x][y] = trigger_event;
10464 HandleElementChange(x, y, p);
10466 change_done = TRUE;
10468 else if (change->has_action)
10470 ExecuteCustomElementAction(x, y, element, p);
10471 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10476 RECURSION_LOOP_DETECTION_END();
10478 return change_done;
10481 static void PlayPlayerSound(struct PlayerInfo *player)
10483 int jx = player->jx, jy = player->jy;
10484 int sound_element = player->artwork_element;
10485 int last_action = player->last_action_waiting;
10486 int action = player->action_waiting;
10488 if (player->is_waiting)
10490 if (action != last_action)
10491 PlayLevelSoundElementAction(jx, jy, sound_element, action);
10493 PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
10497 if (action != last_action)
10498 StopSound(element_info[sound_element].sound[last_action]);
10500 if (last_action == ACTION_SLEEPING)
10501 PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
10505 static void PlayAllPlayersSound()
10509 for (i = 0; i < MAX_PLAYERS; i++)
10510 if (stored_player[i].active)
10511 PlayPlayerSound(&stored_player[i]);
10514 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
10516 boolean last_waiting = player->is_waiting;
10517 int move_dir = player->MovDir;
10519 player->dir_waiting = move_dir;
10520 player->last_action_waiting = player->action_waiting;
10524 if (!last_waiting) /* not waiting -> waiting */
10526 player->is_waiting = TRUE;
10528 player->frame_counter_bored =
10530 game.player_boring_delay_fixed +
10531 GetSimpleRandom(game.player_boring_delay_random);
10532 player->frame_counter_sleeping =
10534 game.player_sleeping_delay_fixed +
10535 GetSimpleRandom(game.player_sleeping_delay_random);
10537 InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
10540 if (game.player_sleeping_delay_fixed +
10541 game.player_sleeping_delay_random > 0 &&
10542 player->anim_delay_counter == 0 &&
10543 player->post_delay_counter == 0 &&
10544 FrameCounter >= player->frame_counter_sleeping)
10545 player->is_sleeping = TRUE;
10546 else if (game.player_boring_delay_fixed +
10547 game.player_boring_delay_random > 0 &&
10548 FrameCounter >= player->frame_counter_bored)
10549 player->is_bored = TRUE;
10551 player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
10552 player->is_bored ? ACTION_BORING :
10555 if (player->is_sleeping && player->use_murphy)
10557 /* special case for sleeping Murphy when leaning against non-free tile */
10559 if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
10560 (Feld[player->jx - 1][player->jy] != EL_EMPTY &&
10561 !IS_MOVING(player->jx - 1, player->jy)))
10562 move_dir = MV_LEFT;
10563 else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
10564 (Feld[player->jx + 1][player->jy] != EL_EMPTY &&
10565 !IS_MOVING(player->jx + 1, player->jy)))
10566 move_dir = MV_RIGHT;
10568 player->is_sleeping = FALSE;
10570 player->dir_waiting = move_dir;
10573 if (player->is_sleeping)
10575 if (player->num_special_action_sleeping > 0)
10577 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
10579 int last_special_action = player->special_action_sleeping;
10580 int num_special_action = player->num_special_action_sleeping;
10581 int special_action =
10582 (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
10583 last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
10584 last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
10585 last_special_action + 1 : ACTION_SLEEPING);
10586 int special_graphic =
10587 el_act_dir2img(player->artwork_element, special_action, move_dir);
10589 player->anim_delay_counter =
10590 graphic_info[special_graphic].anim_delay_fixed +
10591 GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
10592 player->post_delay_counter =
10593 graphic_info[special_graphic].post_delay_fixed +
10594 GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
10596 player->special_action_sleeping = special_action;
10599 if (player->anim_delay_counter > 0)
10601 player->action_waiting = player->special_action_sleeping;
10602 player->anim_delay_counter--;
10604 else if (player->post_delay_counter > 0)
10606 player->post_delay_counter--;
10610 else if (player->is_bored)
10612 if (player->num_special_action_bored > 0)
10614 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
10616 int special_action =
10617 ACTION_BORING_1 + GetSimpleRandom(player->num_special_action_bored);
10618 int special_graphic =
10619 el_act_dir2img(player->artwork_element, special_action, move_dir);
10621 player->anim_delay_counter =
10622 graphic_info[special_graphic].anim_delay_fixed +
10623 GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
10624 player->post_delay_counter =
10625 graphic_info[special_graphic].post_delay_fixed +
10626 GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
10628 player->special_action_bored = special_action;
10631 if (player->anim_delay_counter > 0)
10633 player->action_waiting = player->special_action_bored;
10634 player->anim_delay_counter--;
10636 else if (player->post_delay_counter > 0)
10638 player->post_delay_counter--;
10643 else if (last_waiting) /* waiting -> not waiting */
10645 player->is_waiting = FALSE;
10646 player->is_bored = FALSE;
10647 player->is_sleeping = FALSE;
10649 player->frame_counter_bored = -1;
10650 player->frame_counter_sleeping = -1;
10652 player->anim_delay_counter = 0;
10653 player->post_delay_counter = 0;
10655 player->dir_waiting = player->MovDir;
10656 player->action_waiting = ACTION_DEFAULT;
10658 player->special_action_bored = ACTION_DEFAULT;
10659 player->special_action_sleeping = ACTION_DEFAULT;
10663 static void CheckSingleStepMode(struct PlayerInfo *player)
10665 if (tape.single_step && tape.recording && !tape.pausing)
10667 /* as it is called "single step mode", just return to pause mode when the
10668 player stopped moving after one tile (or never starts moving at all) */
10669 if (!player->is_moving && !player->is_pushing)
10671 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
10672 SnapField(player, 0, 0); /* stop snapping */
10677 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
10679 int left = player_action & JOY_LEFT;
10680 int right = player_action & JOY_RIGHT;
10681 int up = player_action & JOY_UP;
10682 int down = player_action & JOY_DOWN;
10683 int button1 = player_action & JOY_BUTTON_1;
10684 int button2 = player_action & JOY_BUTTON_2;
10685 int dx = (left ? -1 : right ? 1 : 0);
10686 int dy = (up ? -1 : down ? 1 : 0);
10688 if (!player->active || tape.pausing)
10694 SnapField(player, dx, dy);
10698 DropElement(player);
10700 MovePlayer(player, dx, dy);
10703 CheckSingleStepMode(player);
10705 SetPlayerWaiting(player, FALSE);
10707 return player_action;
10711 /* no actions for this player (no input at player's configured device) */
10713 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
10714 SnapField(player, 0, 0);
10715 CheckGravityMovementWhenNotMoving(player);
10717 if (player->MovPos == 0)
10718 SetPlayerWaiting(player, TRUE);
10720 if (player->MovPos == 0) /* needed for tape.playing */
10721 player->is_moving = FALSE;
10723 player->is_dropping = FALSE;
10724 player->is_dropping_pressed = FALSE;
10725 player->drop_pressed_delay = 0;
10727 CheckSingleStepMode(player);
10733 static void CheckLevelTime()
10737 /* !!! SAME CODE AS IN "GameActions()" -- FIX THIS !!! */
10738 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
10740 if (level.native_em_level->lev->home == 0) /* all players at home */
10742 PlayerWins(local_player);
10744 AllPlayersGone = TRUE;
10746 level.native_em_level->lev->home = -1;
10749 if (level.native_em_level->ply[0]->alive == 0 &&
10750 level.native_em_level->ply[1]->alive == 0 &&
10751 level.native_em_level->ply[2]->alive == 0 &&
10752 level.native_em_level->ply[3]->alive == 0) /* all dead */
10753 AllPlayersGone = TRUE;
10755 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
10757 if (game_sp.LevelSolved &&
10758 !game_sp.GameOver) /* game won */
10760 PlayerWins(local_player);
10762 game_sp.GameOver = TRUE;
10764 AllPlayersGone = TRUE;
10767 if (game_sp.GameOver) /* game lost */
10768 AllPlayersGone = TRUE;
10771 if (TimeFrames >= FRAMES_PER_SECOND)
10776 for (i = 0; i < MAX_PLAYERS; i++)
10778 struct PlayerInfo *player = &stored_player[i];
10780 if (SHIELD_ON(player))
10782 player->shield_normal_time_left--;
10784 if (player->shield_deadly_time_left > 0)
10785 player->shield_deadly_time_left--;
10789 if (!local_player->LevelSolved && !level.use_step_counter)
10797 if (TimeLeft <= 10 && setup.time_limit)
10798 PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
10800 /* this does not make sense: game_panel_controls[GAME_PANEL_TIME].value
10801 is reset from other values in UpdateGameDoorValues() -- FIX THIS */
10803 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
10805 if (!TimeLeft && setup.time_limit)
10807 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
10808 level.native_em_level->lev->killed_out_of_time = TRUE;
10810 for (i = 0; i < MAX_PLAYERS; i++)
10811 KillPlayer(&stored_player[i]);
10814 else if (game.no_time_limit && !AllPlayersGone) /* level w/o time limit */
10816 game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
10819 level.native_em_level->lev->time =
10820 (game.no_time_limit ? TimePlayed : TimeLeft);
10823 if (tape.recording || tape.playing)
10824 DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
10827 UpdateAndDisplayGameControlValues();
10830 void AdvanceFrameAndPlayerCounters(int player_nr)
10834 /* advance frame counters (global frame counter and time frame counter) */
10838 /* advance player counters (counters for move delay, move animation etc.) */
10839 for (i = 0; i < MAX_PLAYERS; i++)
10841 boolean advance_player_counters = (player_nr == -1 || player_nr == i);
10842 int move_delay_value = stored_player[i].move_delay_value;
10843 int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
10845 if (!advance_player_counters) /* not all players may be affected */
10848 if (move_frames == 0) /* less than one move per game frame */
10850 int stepsize = TILEX / move_delay_value;
10851 int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
10852 int count = (stored_player[i].is_moving ?
10853 ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
10855 if (count % delay == 0)
10859 stored_player[i].Frame += move_frames;
10861 if (stored_player[i].MovPos != 0)
10862 stored_player[i].StepFrame += move_frames;
10864 if (stored_player[i].move_delay > 0)
10865 stored_player[i].move_delay--;
10867 /* due to bugs in previous versions, counter must count up, not down */
10868 if (stored_player[i].push_delay != -1)
10869 stored_player[i].push_delay++;
10871 if (stored_player[i].drop_delay > 0)
10872 stored_player[i].drop_delay--;
10874 if (stored_player[i].is_dropping_pressed)
10875 stored_player[i].drop_pressed_delay++;
10879 void StartGameActions(boolean init_network_game, boolean record_tape,
10882 unsigned int new_random_seed = InitRND(random_seed);
10885 TapeStartRecording(new_random_seed);
10887 #if defined(NETWORK_AVALIABLE)
10888 if (init_network_game)
10890 SendToServer_StartPlaying();
10901 static unsigned int game_frame_delay = 0;
10902 unsigned int game_frame_delay_value;
10903 byte *recorded_player_action;
10904 byte summarized_player_action = 0;
10905 byte tape_action[MAX_PLAYERS];
10908 /* detect endless loops, caused by custom element programming */
10909 if (recursion_loop_detected && recursion_loop_depth == 0)
10911 char *message = getStringCat3("Internal Error! Element ",
10912 EL_NAME(recursion_loop_element),
10913 " caused endless loop! Quit the game?");
10915 Error(ERR_WARN, "element '%s' caused endless loop in game engine",
10916 EL_NAME(recursion_loop_element));
10918 RequestQuitGameExt(FALSE, level_editor_test_game, message);
10920 recursion_loop_detected = FALSE; /* if game should be continued */
10927 if (game.restart_level)
10928 StartGameActions(options.network, setup.autorecord, level.random_seed);
10930 /* !!! SAME CODE AS IN "CheckLevelTime()" -- FIX THIS !!! */
10931 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
10933 if (level.native_em_level->lev->home == 0) /* all players at home */
10935 PlayerWins(local_player);
10937 AllPlayersGone = TRUE;
10939 level.native_em_level->lev->home = -1;
10942 if (level.native_em_level->ply[0]->alive == 0 &&
10943 level.native_em_level->ply[1]->alive == 0 &&
10944 level.native_em_level->ply[2]->alive == 0 &&
10945 level.native_em_level->ply[3]->alive == 0) /* all dead */
10946 AllPlayersGone = TRUE;
10948 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
10950 if (game_sp.LevelSolved &&
10951 !game_sp.GameOver) /* game won */
10953 PlayerWins(local_player);
10955 game_sp.GameOver = TRUE;
10957 AllPlayersGone = TRUE;
10960 if (game_sp.GameOver) /* game lost */
10961 AllPlayersGone = TRUE;
10964 if (local_player->LevelSolved && !local_player->LevelSolved_GameEnd)
10967 if (AllPlayersGone && !TAPE_IS_STOPPED(tape))
10970 if (game_status != GAME_MODE_PLAYING) /* status might have changed */
10973 game_frame_delay_value =
10974 (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
10976 if (tape.playing && tape.warp_forward && !tape.pausing)
10977 game_frame_delay_value = 0;
10979 /* ---------- main game synchronization point ---------- */
10981 WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
10983 if (network_playing && !network_player_action_received)
10985 /* try to get network player actions in time */
10987 #if defined(NETWORK_AVALIABLE)
10988 /* last chance to get network player actions without main loop delay */
10989 HandleNetworking();
10992 /* game was quit by network peer */
10993 if (game_status != GAME_MODE_PLAYING)
10996 if (!network_player_action_received)
10997 return; /* failed to get network player actions in time */
10999 /* do not yet reset "network_player_action_received" (for tape.pausing) */
11005 /* at this point we know that we really continue executing the game */
11007 network_player_action_received = FALSE;
11009 /* when playing tape, read previously recorded player input from tape data */
11010 recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
11012 /* TapePlayAction() may return NULL when toggling to "pause before death" */
11016 if (tape.set_centered_player)
11018 game.centered_player_nr_next = tape.centered_player_nr_next;
11019 game.set_centered_player = TRUE;
11022 for (i = 0; i < MAX_PLAYERS; i++)
11024 summarized_player_action |= stored_player[i].action;
11026 if (!network_playing && (game.team_mode || tape.playing))
11027 stored_player[i].effective_action = stored_player[i].action;
11030 #if defined(NETWORK_AVALIABLE)
11031 if (network_playing)
11032 SendToServer_MovePlayer(summarized_player_action);
11035 if (!options.network && !game.team_mode)
11036 local_player->effective_action = summarized_player_action;
11038 if (tape.recording &&
11040 setup.input_on_focus &&
11041 game.centered_player_nr != -1)
11043 for (i = 0; i < MAX_PLAYERS; i++)
11044 stored_player[i].effective_action =
11045 (i == game.centered_player_nr ? summarized_player_action : 0);
11048 if (recorded_player_action != NULL)
11049 for (i = 0; i < MAX_PLAYERS; i++)
11050 stored_player[i].effective_action = recorded_player_action[i];
11052 for (i = 0; i < MAX_PLAYERS; i++)
11054 tape_action[i] = stored_player[i].effective_action;
11056 /* (this may happen in the RND game engine if a player was not present on
11057 the playfield on level start, but appeared later from a custom element */
11058 if (setup.team_mode &&
11061 !tape.player_participates[i])
11062 tape.player_participates[i] = TRUE;
11065 /* only record actions from input devices, but not programmed actions */
11066 if (tape.recording)
11067 TapeRecordAction(tape_action);
11069 #if USE_NEW_PLAYER_ASSIGNMENTS
11070 // !!! also map player actions in single player mode !!!
11071 // if (game.team_mode)
11073 byte mapped_action[MAX_PLAYERS];
11075 #if DEBUG_PLAYER_ACTIONS
11077 for (i = 0; i < MAX_PLAYERS; i++)
11078 printf(" %d, ", stored_player[i].effective_action);
11081 for (i = 0; i < MAX_PLAYERS; i++)
11082 mapped_action[i] = stored_player[map_player_action[i]].effective_action;
11084 for (i = 0; i < MAX_PLAYERS; i++)
11085 stored_player[i].effective_action = mapped_action[i];
11087 #if DEBUG_PLAYER_ACTIONS
11089 for (i = 0; i < MAX_PLAYERS; i++)
11090 printf(" %d, ", stored_player[i].effective_action);
11094 #if DEBUG_PLAYER_ACTIONS
11098 for (i = 0; i < MAX_PLAYERS; i++)
11099 printf(" %d, ", stored_player[i].effective_action);
11105 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11107 GameActions_EM_Main();
11109 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11111 GameActions_SP_Main();
11119 void GameActions_EM_Main()
11121 byte effective_action[MAX_PLAYERS];
11122 boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11125 for (i = 0; i < MAX_PLAYERS; i++)
11126 effective_action[i] = stored_player[i].effective_action;
11128 GameActions_EM(effective_action, warp_mode);
11132 AdvanceFrameAndPlayerCounters(-1); /* advance counters for all players */
11135 void GameActions_SP_Main()
11137 byte effective_action[MAX_PLAYERS];
11138 boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11141 for (i = 0; i < MAX_PLAYERS; i++)
11142 effective_action[i] = stored_player[i].effective_action;
11144 GameActions_SP(effective_action, warp_mode);
11148 AdvanceFrameAndPlayerCounters(-1); /* advance counters for all players */
11151 void GameActions_RND()
11153 int magic_wall_x = 0, magic_wall_y = 0;
11154 int i, x, y, element, graphic;
11156 InitPlayfieldScanModeVars();
11158 if (game.engine_version >= VERSION_IDENT(3,2,0,7))
11160 SCAN_PLAYFIELD(x, y)
11162 ChangeCount[x][y] = 0;
11163 ChangeEvent[x][y] = -1;
11167 if (game.set_centered_player)
11169 boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
11171 /* switching to "all players" only possible if all players fit to screen */
11172 if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
11174 game.centered_player_nr_next = game.centered_player_nr;
11175 game.set_centered_player = FALSE;
11178 /* do not switch focus to non-existing (or non-active) player */
11179 if (game.centered_player_nr_next >= 0 &&
11180 !stored_player[game.centered_player_nr_next].active)
11182 game.centered_player_nr_next = game.centered_player_nr;
11183 game.set_centered_player = FALSE;
11187 if (game.set_centered_player &&
11188 ScreenMovPos == 0) /* screen currently aligned at tile position */
11192 if (game.centered_player_nr_next == -1)
11194 setScreenCenteredToAllPlayers(&sx, &sy);
11198 sx = stored_player[game.centered_player_nr_next].jx;
11199 sy = stored_player[game.centered_player_nr_next].jy;
11202 game.centered_player_nr = game.centered_player_nr_next;
11203 game.set_centered_player = FALSE;
11205 DrawRelocateScreen(0, 0, sx, sy, MV_NONE, TRUE, setup.quick_switch);
11206 DrawGameDoorValues();
11209 for (i = 0; i < MAX_PLAYERS; i++)
11211 int actual_player_action = stored_player[i].effective_action;
11214 /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
11215 - rnd_equinox_tetrachloride 048
11216 - rnd_equinox_tetrachloride_ii 096
11217 - rnd_emanuel_schmieg 002
11218 - doctor_sloan_ww 001, 020
11220 if (stored_player[i].MovPos == 0)
11221 CheckGravityMovement(&stored_player[i]);
11224 /* overwrite programmed action with tape action */
11225 if (stored_player[i].programmed_action)
11226 actual_player_action = stored_player[i].programmed_action;
11228 PlayerActions(&stored_player[i], actual_player_action);
11230 ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
11233 ScrollScreen(NULL, SCROLL_GO_ON);
11235 /* for backwards compatibility, the following code emulates a fixed bug that
11236 occured when pushing elements (causing elements that just made their last
11237 pushing step to already (if possible) make their first falling step in the
11238 same game frame, which is bad); this code is also needed to use the famous
11239 "spring push bug" which is used in older levels and might be wanted to be
11240 used also in newer levels, but in this case the buggy pushing code is only
11241 affecting the "spring" element and no other elements */
11243 if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
11245 for (i = 0; i < MAX_PLAYERS; i++)
11247 struct PlayerInfo *player = &stored_player[i];
11248 int x = player->jx;
11249 int y = player->jy;
11251 if (player->active && player->is_pushing && player->is_moving &&
11253 (game.engine_version < VERSION_IDENT(2,2,0,7) ||
11254 Feld[x][y] == EL_SPRING))
11256 ContinueMoving(x, y);
11258 /* continue moving after pushing (this is actually a bug) */
11259 if (!IS_MOVING(x, y))
11260 Stop[x][y] = FALSE;
11265 SCAN_PLAYFIELD(x, y)
11267 ChangeCount[x][y] = 0;
11268 ChangeEvent[x][y] = -1;
11270 /* this must be handled before main playfield loop */
11271 if (Feld[x][y] == EL_PLAYER_IS_LEAVING)
11274 if (MovDelay[x][y] <= 0)
11278 if (Feld[x][y] == EL_ELEMENT_SNAPPING)
11281 if (MovDelay[x][y] <= 0)
11284 TEST_DrawLevelField(x, y);
11286 TestIfElementTouchesCustomElement(x, y); /* for empty space */
11291 if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
11293 printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
11294 printf("GameActions(): This should never happen!\n");
11296 ChangePage[x][y] = -1;
11300 Stop[x][y] = FALSE;
11301 if (WasJustMoving[x][y] > 0)
11302 WasJustMoving[x][y]--;
11303 if (WasJustFalling[x][y] > 0)
11304 WasJustFalling[x][y]--;
11305 if (CheckCollision[x][y] > 0)
11306 CheckCollision[x][y]--;
11307 if (CheckImpact[x][y] > 0)
11308 CheckImpact[x][y]--;
11312 /* reset finished pushing action (not done in ContinueMoving() to allow
11313 continuous pushing animation for elements with zero push delay) */
11314 if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
11316 ResetGfxAnimation(x, y);
11317 TEST_DrawLevelField(x, y);
11321 if (IS_BLOCKED(x, y))
11325 Blocked2Moving(x, y, &oldx, &oldy);
11326 if (!IS_MOVING(oldx, oldy))
11328 printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
11329 printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
11330 printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
11331 printf("GameActions(): This should never happen!\n");
11337 SCAN_PLAYFIELD(x, y)
11339 element = Feld[x][y];
11340 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11342 ResetGfxFrame(x, y, TRUE);
11344 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
11345 IS_NEXT_FRAME(GfxFrame[x][y], graphic))
11346 ResetRandomAnimationValue(x, y);
11348 SetRandomAnimationValue(x, y);
11350 PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
11352 if (IS_INACTIVE(element))
11354 if (IS_ANIMATED(graphic))
11355 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11360 /* this may take place after moving, so 'element' may have changed */
11361 if (IS_CHANGING(x, y) &&
11362 (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
11364 int page = element_info[element].event_page_nr[CE_DELAY];
11366 HandleElementChange(x, y, page);
11368 element = Feld[x][y];
11369 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11372 if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
11376 element = Feld[x][y];
11377 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11379 if (IS_ANIMATED(graphic) &&
11380 !IS_MOVING(x, y) &&
11382 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11384 if (IS_GEM(element) || element == EL_SP_INFOTRON)
11385 TEST_DrawTwinkleOnField(x, y);
11387 else if ((element == EL_ACID ||
11388 element == EL_EXIT_OPEN ||
11389 element == EL_EM_EXIT_OPEN ||
11390 element == EL_SP_EXIT_OPEN ||
11391 element == EL_STEEL_EXIT_OPEN ||
11392 element == EL_EM_STEEL_EXIT_OPEN ||
11393 element == EL_SP_TERMINAL ||
11394 element == EL_SP_TERMINAL_ACTIVE ||
11395 element == EL_EXTRA_TIME ||
11396 element == EL_SHIELD_NORMAL ||
11397 element == EL_SHIELD_DEADLY) &&
11398 IS_ANIMATED(graphic))
11399 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11400 else if (IS_MOVING(x, y))
11401 ContinueMoving(x, y);
11402 else if (IS_ACTIVE_BOMB(element))
11403 CheckDynamite(x, y);
11404 else if (element == EL_AMOEBA_GROWING)
11405 AmoebeWaechst(x, y);
11406 else if (element == EL_AMOEBA_SHRINKING)
11407 AmoebaDisappearing(x, y);
11409 #if !USE_NEW_AMOEBA_CODE
11410 else if (IS_AMOEBALIVE(element))
11411 AmoebeAbleger(x, y);
11414 else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
11416 else if (element == EL_EXIT_CLOSED)
11418 else if (element == EL_EM_EXIT_CLOSED)
11420 else if (element == EL_STEEL_EXIT_CLOSED)
11421 CheckExitSteel(x, y);
11422 else if (element == EL_EM_STEEL_EXIT_CLOSED)
11423 CheckExitSteelEM(x, y);
11424 else if (element == EL_SP_EXIT_CLOSED)
11426 else if (element == EL_EXPANDABLE_WALL_GROWING ||
11427 element == EL_EXPANDABLE_STEELWALL_GROWING)
11428 MauerWaechst(x, y);
11429 else if (element == EL_EXPANDABLE_WALL ||
11430 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
11431 element == EL_EXPANDABLE_WALL_VERTICAL ||
11432 element == EL_EXPANDABLE_WALL_ANY ||
11433 element == EL_BD_EXPANDABLE_WALL)
11434 MauerAbleger(x, y);
11435 else if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
11436 element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
11437 element == EL_EXPANDABLE_STEELWALL_ANY)
11438 MauerAblegerStahl(x, y);
11439 else if (element == EL_FLAMES)
11440 CheckForDragon(x, y);
11441 else if (element == EL_EXPLOSION)
11442 ; /* drawing of correct explosion animation is handled separately */
11443 else if (element == EL_ELEMENT_SNAPPING ||
11444 element == EL_DIAGONAL_SHRINKING ||
11445 element == EL_DIAGONAL_GROWING)
11447 graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
11449 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11451 else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
11452 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11454 if (IS_BELT_ACTIVE(element))
11455 PlayLevelSoundAction(x, y, ACTION_ACTIVE);
11457 if (game.magic_wall_active)
11459 int jx = local_player->jx, jy = local_player->jy;
11461 /* play the element sound at the position nearest to the player */
11462 if ((element == EL_MAGIC_WALL_FULL ||
11463 element == EL_MAGIC_WALL_ACTIVE ||
11464 element == EL_MAGIC_WALL_EMPTYING ||
11465 element == EL_BD_MAGIC_WALL_FULL ||
11466 element == EL_BD_MAGIC_WALL_ACTIVE ||
11467 element == EL_BD_MAGIC_WALL_EMPTYING ||
11468 element == EL_DC_MAGIC_WALL_FULL ||
11469 element == EL_DC_MAGIC_WALL_ACTIVE ||
11470 element == EL_DC_MAGIC_WALL_EMPTYING) &&
11471 ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
11479 #if USE_NEW_AMOEBA_CODE
11480 /* new experimental amoeba growth stuff */
11481 if (!(FrameCounter % 8))
11483 static unsigned int random = 1684108901;
11485 for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
11487 x = RND(lev_fieldx);
11488 y = RND(lev_fieldy);
11489 element = Feld[x][y];
11491 if (!IS_PLAYER(x,y) &&
11492 (element == EL_EMPTY ||
11493 CAN_GROW_INTO(element) ||
11494 element == EL_QUICKSAND_EMPTY ||
11495 element == EL_QUICKSAND_FAST_EMPTY ||
11496 element == EL_ACID_SPLASH_LEFT ||
11497 element == EL_ACID_SPLASH_RIGHT))
11499 if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
11500 (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
11501 (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
11502 (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
11503 Feld[x][y] = EL_AMOEBA_DROP;
11506 random = random * 129 + 1;
11511 game.explosions_delayed = FALSE;
11513 SCAN_PLAYFIELD(x, y)
11515 element = Feld[x][y];
11517 if (ExplodeField[x][y])
11518 Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
11519 else if (element == EL_EXPLOSION)
11520 Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
11522 ExplodeField[x][y] = EX_TYPE_NONE;
11525 game.explosions_delayed = TRUE;
11527 if (game.magic_wall_active)
11529 if (!(game.magic_wall_time_left % 4))
11531 int element = Feld[magic_wall_x][magic_wall_y];
11533 if (element == EL_BD_MAGIC_WALL_FULL ||
11534 element == EL_BD_MAGIC_WALL_ACTIVE ||
11535 element == EL_BD_MAGIC_WALL_EMPTYING)
11536 PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
11537 else if (element == EL_DC_MAGIC_WALL_FULL ||
11538 element == EL_DC_MAGIC_WALL_ACTIVE ||
11539 element == EL_DC_MAGIC_WALL_EMPTYING)
11540 PlayLevelSound(magic_wall_x, magic_wall_y, SND_DC_MAGIC_WALL_ACTIVE);
11542 PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
11545 if (game.magic_wall_time_left > 0)
11547 game.magic_wall_time_left--;
11549 if (!game.magic_wall_time_left)
11551 SCAN_PLAYFIELD(x, y)
11553 element = Feld[x][y];
11555 if (element == EL_MAGIC_WALL_ACTIVE ||
11556 element == EL_MAGIC_WALL_FULL)
11558 Feld[x][y] = EL_MAGIC_WALL_DEAD;
11559 TEST_DrawLevelField(x, y);
11561 else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
11562 element == EL_BD_MAGIC_WALL_FULL)
11564 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
11565 TEST_DrawLevelField(x, y);
11567 else if (element == EL_DC_MAGIC_WALL_ACTIVE ||
11568 element == EL_DC_MAGIC_WALL_FULL)
11570 Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
11571 TEST_DrawLevelField(x, y);
11575 game.magic_wall_active = FALSE;
11580 if (game.light_time_left > 0)
11582 game.light_time_left--;
11584 if (game.light_time_left == 0)
11585 RedrawAllLightSwitchesAndInvisibleElements();
11588 if (game.timegate_time_left > 0)
11590 game.timegate_time_left--;
11592 if (game.timegate_time_left == 0)
11593 CloseAllOpenTimegates();
11596 if (game.lenses_time_left > 0)
11598 game.lenses_time_left--;
11600 if (game.lenses_time_left == 0)
11601 RedrawAllInvisibleElementsForLenses();
11604 if (game.magnify_time_left > 0)
11606 game.magnify_time_left--;
11608 if (game.magnify_time_left == 0)
11609 RedrawAllInvisibleElementsForMagnifier();
11612 for (i = 0; i < MAX_PLAYERS; i++)
11614 struct PlayerInfo *player = &stored_player[i];
11616 if (SHIELD_ON(player))
11618 if (player->shield_deadly_time_left)
11619 PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
11620 else if (player->shield_normal_time_left)
11621 PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
11625 #if USE_DELAYED_GFX_REDRAW
11626 SCAN_PLAYFIELD(x, y)
11628 if (GfxRedraw[x][y] != GFX_REDRAW_NONE)
11630 /* !!! PROBLEM: THIS REDRAWS THE PLAYFIELD _AFTER_ THE SCAN, BUT TILES
11631 !!! MAY HAVE CHANGED AFTER BEING DRAWN DURING PLAYFIELD SCAN !!! */
11633 if (GfxRedraw[x][y] & GFX_REDRAW_TILE)
11634 DrawLevelField(x, y);
11636 if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED)
11637 DrawLevelFieldCrumbled(x, y);
11639 if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS)
11640 DrawLevelFieldCrumbledNeighbours(x, y);
11642 if (GfxRedraw[x][y] & GFX_REDRAW_TILE_TWINKLED)
11643 DrawTwinkleOnField(x, y);
11646 GfxRedraw[x][y] = GFX_REDRAW_NONE;
11653 PlayAllPlayersSound();
11655 if (options.debug) /* calculate frames per second */
11657 static unsigned int fps_counter = 0;
11658 static int fps_frames = 0;
11659 unsigned int fps_delay_ms = Counter() - fps_counter;
11663 if (fps_delay_ms >= 500) /* calculate fps every 0.5 seconds */
11665 global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
11668 fps_counter = Counter();
11671 redraw_mask |= REDRAW_FPS;
11674 AdvanceFrameAndPlayerCounters(-1); /* advance counters for all players */
11676 if (local_player->show_envelope != 0 && local_player->MovPos == 0)
11678 ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
11680 local_player->show_envelope = 0;
11683 /* use random number generator in every frame to make it less predictable */
11684 if (game.engine_version >= VERSION_IDENT(3,1,1,0))
11688 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
11690 int min_x = x, min_y = y, max_x = x, max_y = y;
11693 for (i = 0; i < MAX_PLAYERS; i++)
11695 int jx = stored_player[i].jx, jy = stored_player[i].jy;
11697 if (!stored_player[i].active || &stored_player[i] == player)
11700 min_x = MIN(min_x, jx);
11701 min_y = MIN(min_y, jy);
11702 max_x = MAX(max_x, jx);
11703 max_y = MAX(max_y, jy);
11706 return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
11709 static boolean AllPlayersInVisibleScreen()
11713 for (i = 0; i < MAX_PLAYERS; i++)
11715 int jx = stored_player[i].jx, jy = stored_player[i].jy;
11717 if (!stored_player[i].active)
11720 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
11727 void ScrollLevel(int dx, int dy)
11729 int softscroll_offset = (setup.soft_scrolling ? 2 * TILEX_VAR : 0);
11732 BlitBitmap(drawto_field, drawto_field,
11733 FX + TILEX_VAR * (dx == -1) - softscroll_offset,
11734 FY + TILEY_VAR * (dy == -1) - softscroll_offset,
11735 SXSIZE - TILEX_VAR * (dx != 0) + 2 * softscroll_offset,
11736 SYSIZE - TILEY_VAR * (dy != 0) + 2 * softscroll_offset,
11737 FX + TILEX_VAR * (dx == 1) - softscroll_offset,
11738 FY + TILEY_VAR * (dy == 1) - softscroll_offset);
11742 x = (dx == 1 ? BX1 : BX2);
11743 for (y = BY1; y <= BY2; y++)
11744 DrawScreenField(x, y);
11749 y = (dy == 1 ? BY1 : BY2);
11750 for (x = BX1; x <= BX2; x++)
11751 DrawScreenField(x, y);
11754 redraw_mask |= REDRAW_FIELD;
11757 static boolean canFallDown(struct PlayerInfo *player)
11759 int jx = player->jx, jy = player->jy;
11761 return (IN_LEV_FIELD(jx, jy + 1) &&
11762 (IS_FREE(jx, jy + 1) ||
11763 (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
11764 IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
11765 !IS_WALKABLE_INSIDE(Feld[jx][jy]));
11768 static boolean canPassField(int x, int y, int move_dir)
11770 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
11771 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
11772 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
11773 int nextx = x + dx;
11774 int nexty = y + dy;
11775 int element = Feld[x][y];
11777 return (IS_PASSABLE_FROM(element, opposite_dir) &&
11778 !CAN_MOVE(element) &&
11779 IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
11780 IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
11781 (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
11784 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
11786 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
11787 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
11788 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
11792 return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
11793 IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
11794 (IS_DIGGABLE(Feld[newx][newy]) ||
11795 IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
11796 canPassField(newx, newy, move_dir)));
11799 static void CheckGravityMovement(struct PlayerInfo *player)
11801 if (player->gravity && !player->programmed_action)
11803 int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
11804 int move_dir_vertical = player->effective_action & MV_VERTICAL;
11805 boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
11806 int jx = player->jx, jy = player->jy;
11807 boolean player_is_moving_to_valid_field =
11808 (!player_is_snapping &&
11809 (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
11810 canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
11811 boolean player_can_fall_down = canFallDown(player);
11813 if (player_can_fall_down &&
11814 !player_is_moving_to_valid_field)
11815 player->programmed_action = MV_DOWN;
11819 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
11821 return CheckGravityMovement(player);
11823 if (player->gravity && !player->programmed_action)
11825 int jx = player->jx, jy = player->jy;
11826 boolean field_under_player_is_free =
11827 (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
11828 boolean player_is_standing_on_valid_field =
11829 (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
11830 (IS_WALKABLE(Feld[jx][jy]) &&
11831 !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
11833 if (field_under_player_is_free && !player_is_standing_on_valid_field)
11834 player->programmed_action = MV_DOWN;
11839 MovePlayerOneStep()
11840 -----------------------------------------------------------------------------
11841 dx, dy: direction (non-diagonal) to try to move the player to
11842 real_dx, real_dy: direction as read from input device (can be diagonal)
11845 boolean MovePlayerOneStep(struct PlayerInfo *player,
11846 int dx, int dy, int real_dx, int real_dy)
11848 int jx = player->jx, jy = player->jy;
11849 int new_jx = jx + dx, new_jy = jy + dy;
11851 boolean player_can_move = !player->cannot_move;
11853 if (!player->active || (!dx && !dy))
11854 return MP_NO_ACTION;
11856 player->MovDir = (dx < 0 ? MV_LEFT :
11857 dx > 0 ? MV_RIGHT :
11859 dy > 0 ? MV_DOWN : MV_NONE);
11861 if (!IN_LEV_FIELD(new_jx, new_jy))
11862 return MP_NO_ACTION;
11864 if (!player_can_move)
11866 if (player->MovPos == 0)
11868 player->is_moving = FALSE;
11869 player->is_digging = FALSE;
11870 player->is_collecting = FALSE;
11871 player->is_snapping = FALSE;
11872 player->is_pushing = FALSE;
11876 if (!options.network && game.centered_player_nr == -1 &&
11877 !AllPlayersInSight(player, new_jx, new_jy))
11878 return MP_NO_ACTION;
11880 can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
11881 if (can_move != MP_MOVING)
11884 /* check if DigField() has caused relocation of the player */
11885 if (player->jx != jx || player->jy != jy)
11886 return MP_NO_ACTION; /* <-- !!! CHECK THIS [-> MP_ACTION ?] !!! */
11888 StorePlayer[jx][jy] = 0;
11889 player->last_jx = jx;
11890 player->last_jy = jy;
11891 player->jx = new_jx;
11892 player->jy = new_jy;
11893 StorePlayer[new_jx][new_jy] = player->element_nr;
11895 if (player->move_delay_value_next != -1)
11897 player->move_delay_value = player->move_delay_value_next;
11898 player->move_delay_value_next = -1;
11902 (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
11904 player->step_counter++;
11906 PlayerVisit[jx][jy] = FrameCounter;
11908 player->is_moving = TRUE;
11911 /* should better be called in MovePlayer(), but this breaks some tapes */
11912 ScrollPlayer(player, SCROLL_INIT);
11918 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
11920 int jx = player->jx, jy = player->jy;
11921 int old_jx = jx, old_jy = jy;
11922 int moved = MP_NO_ACTION;
11924 if (!player->active)
11929 if (player->MovPos == 0)
11931 player->is_moving = FALSE;
11932 player->is_digging = FALSE;
11933 player->is_collecting = FALSE;
11934 player->is_snapping = FALSE;
11935 player->is_pushing = FALSE;
11941 if (player->move_delay > 0)
11944 player->move_delay = -1; /* set to "uninitialized" value */
11946 /* store if player is automatically moved to next field */
11947 player->is_auto_moving = (player->programmed_action != MV_NONE);
11949 /* remove the last programmed player action */
11950 player->programmed_action = 0;
11952 if (player->MovPos)
11954 /* should only happen if pre-1.2 tape recordings are played */
11955 /* this is only for backward compatibility */
11957 int original_move_delay_value = player->move_delay_value;
11960 printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%d]\n",
11964 /* scroll remaining steps with finest movement resolution */
11965 player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
11967 while (player->MovPos)
11969 ScrollPlayer(player, SCROLL_GO_ON);
11970 ScrollScreen(NULL, SCROLL_GO_ON);
11972 AdvanceFrameAndPlayerCounters(player->index_nr);
11978 player->move_delay_value = original_move_delay_value;
11981 player->is_active = FALSE;
11983 if (player->last_move_dir & MV_HORIZONTAL)
11985 if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
11986 moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
11990 if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
11991 moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
11994 if (!moved && !player->is_active)
11996 player->is_moving = FALSE;
11997 player->is_digging = FALSE;
11998 player->is_collecting = FALSE;
11999 player->is_snapping = FALSE;
12000 player->is_pushing = FALSE;
12006 if (moved & MP_MOVING && !ScreenMovPos &&
12007 (player->index_nr == game.centered_player_nr ||
12008 game.centered_player_nr == -1))
12010 int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
12011 int offset = game.scroll_delay_value;
12013 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12015 /* actual player has left the screen -- scroll in that direction */
12016 if (jx != old_jx) /* player has moved horizontally */
12017 scroll_x += (jx - old_jx);
12018 else /* player has moved vertically */
12019 scroll_y += (jy - old_jy);
12023 if (jx != old_jx) /* player has moved horizontally */
12025 if ((player->MovDir == MV_LEFT && scroll_x > jx - MIDPOSX + offset) ||
12026 (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
12027 scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
12029 /* don't scroll over playfield boundaries */
12030 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
12031 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
12033 /* don't scroll more than one field at a time */
12034 scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
12036 /* don't scroll against the player's moving direction */
12037 if ((player->MovDir == MV_LEFT && scroll_x > old_scroll_x) ||
12038 (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
12039 scroll_x = old_scroll_x;
12041 else /* player has moved vertically */
12043 if ((player->MovDir == MV_UP && scroll_y > jy - MIDPOSY + offset) ||
12044 (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
12045 scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
12047 /* don't scroll over playfield boundaries */
12048 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
12049 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
12051 /* don't scroll more than one field at a time */
12052 scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
12054 /* don't scroll against the player's moving direction */
12055 if ((player->MovDir == MV_UP && scroll_y > old_scroll_y) ||
12056 (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
12057 scroll_y = old_scroll_y;
12061 if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
12063 if (!options.network && game.centered_player_nr == -1 &&
12064 !AllPlayersInVisibleScreen())
12066 scroll_x = old_scroll_x;
12067 scroll_y = old_scroll_y;
12071 ScrollScreen(player, SCROLL_INIT);
12072 ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
12077 player->StepFrame = 0;
12079 if (moved & MP_MOVING)
12081 if (old_jx != jx && old_jy == jy)
12082 player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
12083 else if (old_jx == jx && old_jy != jy)
12084 player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
12086 TEST_DrawLevelField(jx, jy); /* for "crumbled sand" */
12088 player->last_move_dir = player->MovDir;
12089 player->is_moving = TRUE;
12090 player->is_snapping = FALSE;
12091 player->is_switching = FALSE;
12092 player->is_dropping = FALSE;
12093 player->is_dropping_pressed = FALSE;
12094 player->drop_pressed_delay = 0;
12097 /* should better be called here than above, but this breaks some tapes */
12098 ScrollPlayer(player, SCROLL_INIT);
12103 CheckGravityMovementWhenNotMoving(player);
12105 player->is_moving = FALSE;
12107 /* at this point, the player is allowed to move, but cannot move right now
12108 (e.g. because of something blocking the way) -- ensure that the player
12109 is also allowed to move in the next frame (in old versions before 3.1.1,
12110 the player was forced to wait again for eight frames before next try) */
12112 if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12113 player->move_delay = 0; /* allow direct movement in the next frame */
12116 if (player->move_delay == -1) /* not yet initialized by DigField() */
12117 player->move_delay = player->move_delay_value;
12119 if (game.engine_version < VERSION_IDENT(3,0,7,0))
12121 TestIfPlayerTouchesBadThing(jx, jy);
12122 TestIfPlayerTouchesCustomElement(jx, jy);
12125 if (!player->active)
12126 RemovePlayer(player);
12131 void ScrollPlayer(struct PlayerInfo *player, int mode)
12133 int jx = player->jx, jy = player->jy;
12134 int last_jx = player->last_jx, last_jy = player->last_jy;
12135 int move_stepsize = TILEX / player->move_delay_value;
12137 if (!player->active)
12140 if (player->MovPos == 0 && mode == SCROLL_GO_ON) /* player not moving */
12143 if (mode == SCROLL_INIT)
12145 player->actual_frame_counter = FrameCounter;
12146 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
12148 if ((player->block_last_field || player->block_delay_adjustment > 0) &&
12149 Feld[last_jx][last_jy] == EL_EMPTY)
12151 int last_field_block_delay = 0; /* start with no blocking at all */
12152 int block_delay_adjustment = player->block_delay_adjustment;
12154 /* if player blocks last field, add delay for exactly one move */
12155 if (player->block_last_field)
12157 last_field_block_delay += player->move_delay_value;
12159 /* when blocking enabled, prevent moving up despite gravity */
12160 if (player->gravity && player->MovDir == MV_UP)
12161 block_delay_adjustment = -1;
12164 /* add block delay adjustment (also possible when not blocking) */
12165 last_field_block_delay += block_delay_adjustment;
12167 Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
12168 MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
12171 if (player->MovPos != 0) /* player has not yet reached destination */
12174 else if (!FrameReached(&player->actual_frame_counter, 1))
12177 if (player->MovPos != 0)
12179 player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
12180 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
12182 /* before DrawPlayer() to draw correct player graphic for this case */
12183 if (player->MovPos == 0)
12184 CheckGravityMovement(player);
12187 if (player->MovPos == 0) /* player reached destination field */
12189 if (player->move_delay_reset_counter > 0)
12191 player->move_delay_reset_counter--;
12193 if (player->move_delay_reset_counter == 0)
12195 /* continue with normal speed after quickly moving through gate */
12196 HALVE_PLAYER_SPEED(player);
12198 /* be able to make the next move without delay */
12199 player->move_delay = 0;
12203 player->last_jx = jx;
12204 player->last_jy = jy;
12206 if (Feld[jx][jy] == EL_EXIT_OPEN ||
12207 Feld[jx][jy] == EL_EM_EXIT_OPEN ||
12208 Feld[jx][jy] == EL_EM_EXIT_OPENING ||
12209 Feld[jx][jy] == EL_STEEL_EXIT_OPEN ||
12210 Feld[jx][jy] == EL_EM_STEEL_EXIT_OPEN ||
12211 Feld[jx][jy] == EL_EM_STEEL_EXIT_OPENING ||
12212 Feld[jx][jy] == EL_SP_EXIT_OPEN ||
12213 Feld[jx][jy] == EL_SP_EXIT_OPENING) /* <-- special case */
12215 DrawPlayer(player); /* needed here only to cleanup last field */
12216 RemovePlayer(player);
12218 if (local_player->friends_still_needed == 0 ||
12219 IS_SP_ELEMENT(Feld[jx][jy]))
12220 PlayerWins(player);
12223 /* this breaks one level: "machine", level 000 */
12225 int move_direction = player->MovDir;
12226 int enter_side = MV_DIR_OPPOSITE(move_direction);
12227 int leave_side = move_direction;
12228 int old_jx = last_jx;
12229 int old_jy = last_jy;
12230 int old_element = Feld[old_jx][old_jy];
12231 int new_element = Feld[jx][jy];
12233 if (IS_CUSTOM_ELEMENT(old_element))
12234 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
12236 player->index_bit, leave_side);
12238 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
12239 CE_PLAYER_LEAVES_X,
12240 player->index_bit, leave_side);
12242 if (IS_CUSTOM_ELEMENT(new_element))
12243 CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
12244 player->index_bit, enter_side);
12246 CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
12247 CE_PLAYER_ENTERS_X,
12248 player->index_bit, enter_side);
12250 CheckTriggeredElementChangeBySide(jx, jy, player->initial_element,
12251 CE_MOVE_OF_X, move_direction);
12254 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
12256 TestIfPlayerTouchesBadThing(jx, jy);
12257 TestIfPlayerTouchesCustomElement(jx, jy);
12259 /* needed because pushed element has not yet reached its destination,
12260 so it would trigger a change event at its previous field location */
12261 if (!player->is_pushing)
12262 TestIfElementTouchesCustomElement(jx, jy); /* for empty space */
12264 if (!player->active)
12265 RemovePlayer(player);
12268 if (!local_player->LevelSolved && level.use_step_counter)
12278 if (TimeLeft <= 10 && setup.time_limit)
12279 PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
12281 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
12283 DisplayGameControlValues();
12285 if (!TimeLeft && setup.time_limit)
12286 for (i = 0; i < MAX_PLAYERS; i++)
12287 KillPlayer(&stored_player[i]);
12289 else if (game.no_time_limit && !AllPlayersGone) /* level w/o time limit */
12291 game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
12293 DisplayGameControlValues();
12297 if (tape.single_step && tape.recording && !tape.pausing &&
12298 !player->programmed_action)
12299 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
12303 void ScrollScreen(struct PlayerInfo *player, int mode)
12305 static unsigned int screen_frame_counter = 0;
12307 if (mode == SCROLL_INIT)
12309 /* set scrolling step size according to actual player's moving speed */
12310 ScrollStepSize = TILEX / player->move_delay_value;
12312 screen_frame_counter = FrameCounter;
12313 ScreenMovDir = player->MovDir;
12314 ScreenMovPos = player->MovPos;
12315 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
12318 else if (!FrameReached(&screen_frame_counter, 1))
12323 ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
12324 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
12325 redraw_mask |= REDRAW_FIELD;
12328 ScreenMovDir = MV_NONE;
12331 void TestIfPlayerTouchesCustomElement(int x, int y)
12333 static int xy[4][2] =
12340 static int trigger_sides[4][2] =
12342 /* center side border side */
12343 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
12344 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
12345 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
12346 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
12348 static int touch_dir[4] =
12350 MV_LEFT | MV_RIGHT,
12355 int center_element = Feld[x][y]; /* should always be non-moving! */
12358 for (i = 0; i < NUM_DIRECTIONS; i++)
12360 int xx = x + xy[i][0];
12361 int yy = y + xy[i][1];
12362 int center_side = trigger_sides[i][0];
12363 int border_side = trigger_sides[i][1];
12364 int border_element;
12366 if (!IN_LEV_FIELD(xx, yy))
12369 if (IS_PLAYER(x, y)) /* player found at center element */
12371 struct PlayerInfo *player = PLAYERINFO(x, y);
12373 if (game.engine_version < VERSION_IDENT(3,0,7,0))
12374 border_element = Feld[xx][yy]; /* may be moving! */
12375 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
12376 border_element = Feld[xx][yy];
12377 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
12378 border_element = MovingOrBlocked2Element(xx, yy);
12380 continue; /* center and border element do not touch */
12382 CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
12383 player->index_bit, border_side);
12384 CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
12385 CE_PLAYER_TOUCHES_X,
12386 player->index_bit, border_side);
12389 /* use player element that is initially defined in the level playfield,
12390 not the player element that corresponds to the runtime player number
12391 (example: a level that contains EL_PLAYER_3 as the only player would
12392 incorrectly give EL_PLAYER_1 for "player->element_nr") */
12393 int player_element = PLAYERINFO(x, y)->initial_element;
12395 CheckElementChangeBySide(xx, yy, border_element, player_element,
12396 CE_TOUCHING_X, border_side);
12399 else if (IS_PLAYER(xx, yy)) /* player found at border element */
12401 struct PlayerInfo *player = PLAYERINFO(xx, yy);
12403 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
12405 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
12406 continue; /* center and border element do not touch */
12409 CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
12410 player->index_bit, center_side);
12411 CheckTriggeredElementChangeByPlayer(x, y, center_element,
12412 CE_PLAYER_TOUCHES_X,
12413 player->index_bit, center_side);
12416 /* use player element that is initially defined in the level playfield,
12417 not the player element that corresponds to the runtime player number
12418 (example: a level that contains EL_PLAYER_3 as the only player would
12419 incorrectly give EL_PLAYER_1 for "player->element_nr") */
12420 int player_element = PLAYERINFO(xx, yy)->initial_element;
12422 CheckElementChangeBySide(x, y, center_element, player_element,
12423 CE_TOUCHING_X, center_side);
12431 void TestIfElementTouchesCustomElement(int x, int y)
12433 static int xy[4][2] =
12440 static int trigger_sides[4][2] =
12442 /* center side border side */
12443 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
12444 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
12445 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
12446 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
12448 static int touch_dir[4] =
12450 MV_LEFT | MV_RIGHT,
12455 boolean change_center_element = FALSE;
12456 int center_element = Feld[x][y]; /* should always be non-moving! */
12457 int border_element_old[NUM_DIRECTIONS];
12460 for (i = 0; i < NUM_DIRECTIONS; i++)
12462 int xx = x + xy[i][0];
12463 int yy = y + xy[i][1];
12464 int border_element;
12466 border_element_old[i] = -1;
12468 if (!IN_LEV_FIELD(xx, yy))
12471 if (game.engine_version < VERSION_IDENT(3,0,7,0))
12472 border_element = Feld[xx][yy]; /* may be moving! */
12473 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
12474 border_element = Feld[xx][yy];
12475 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
12476 border_element = MovingOrBlocked2Element(xx, yy);
12478 continue; /* center and border element do not touch */
12480 border_element_old[i] = border_element;
12483 for (i = 0; i < NUM_DIRECTIONS; i++)
12485 int xx = x + xy[i][0];
12486 int yy = y + xy[i][1];
12487 int center_side = trigger_sides[i][0];
12488 int border_element = border_element_old[i];
12490 if (border_element == -1)
12493 /* check for change of border element */
12494 CheckElementChangeBySide(xx, yy, border_element, center_element,
12495 CE_TOUCHING_X, center_side);
12497 /* (center element cannot be player, so we dont have to check this here) */
12500 for (i = 0; i < NUM_DIRECTIONS; i++)
12502 int xx = x + xy[i][0];
12503 int yy = y + xy[i][1];
12504 int border_side = trigger_sides[i][1];
12505 int border_element = border_element_old[i];
12507 if (border_element == -1)
12510 /* check for change of center element (but change it only once) */
12511 if (!change_center_element)
12512 change_center_element =
12513 CheckElementChangeBySide(x, y, center_element, border_element,
12514 CE_TOUCHING_X, border_side);
12516 if (IS_PLAYER(xx, yy))
12518 /* use player element that is initially defined in the level playfield,
12519 not the player element that corresponds to the runtime player number
12520 (example: a level that contains EL_PLAYER_3 as the only player would
12521 incorrectly give EL_PLAYER_1 for "player->element_nr") */
12522 int player_element = PLAYERINFO(xx, yy)->initial_element;
12524 CheckElementChangeBySide(x, y, center_element, player_element,
12525 CE_TOUCHING_X, border_side);
12530 void TestIfElementHitsCustomElement(int x, int y, int direction)
12532 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
12533 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
12534 int hitx = x + dx, hity = y + dy;
12535 int hitting_element = Feld[x][y];
12536 int touched_element;
12538 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
12541 touched_element = (IN_LEV_FIELD(hitx, hity) ?
12542 MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
12544 if (IN_LEV_FIELD(hitx, hity))
12546 int opposite_direction = MV_DIR_OPPOSITE(direction);
12547 int hitting_side = direction;
12548 int touched_side = opposite_direction;
12549 boolean object_hit = (!IS_MOVING(hitx, hity) ||
12550 MovDir[hitx][hity] != direction ||
12551 ABS(MovPos[hitx][hity]) <= TILEY / 2);
12557 CheckElementChangeBySide(x, y, hitting_element, touched_element,
12558 CE_HITTING_X, touched_side);
12560 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
12561 CE_HIT_BY_X, hitting_side);
12563 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
12564 CE_HIT_BY_SOMETHING, opposite_direction);
12566 if (IS_PLAYER(hitx, hity))
12568 /* use player element that is initially defined in the level playfield,
12569 not the player element that corresponds to the runtime player number
12570 (example: a level that contains EL_PLAYER_3 as the only player would
12571 incorrectly give EL_PLAYER_1 for "player->element_nr") */
12572 int player_element = PLAYERINFO(hitx, hity)->initial_element;
12574 CheckElementChangeBySide(x, y, hitting_element, player_element,
12575 CE_HITTING_X, touched_side);
12580 /* "hitting something" is also true when hitting the playfield border */
12581 CheckElementChangeBySide(x, y, hitting_element, touched_element,
12582 CE_HITTING_SOMETHING, direction);
12585 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
12587 int i, kill_x = -1, kill_y = -1;
12589 int bad_element = -1;
12590 static int test_xy[4][2] =
12597 static int test_dir[4] =
12605 for (i = 0; i < NUM_DIRECTIONS; i++)
12607 int test_x, test_y, test_move_dir, test_element;
12609 test_x = good_x + test_xy[i][0];
12610 test_y = good_y + test_xy[i][1];
12612 if (!IN_LEV_FIELD(test_x, test_y))
12616 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
12618 test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
12620 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
12621 2nd case: DONT_TOUCH style bad thing does not move away from good thing
12623 if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
12624 (DONT_TOUCH(test_element) && test_move_dir != test_dir[i]))
12628 bad_element = test_element;
12634 if (kill_x != -1 || kill_y != -1)
12636 if (IS_PLAYER(good_x, good_y))
12638 struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
12640 if (player->shield_deadly_time_left > 0 &&
12641 !IS_INDESTRUCTIBLE(bad_element))
12642 Bang(kill_x, kill_y);
12643 else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
12644 KillPlayer(player);
12647 Bang(good_x, good_y);
12651 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
12653 int i, kill_x = -1, kill_y = -1;
12654 int bad_element = Feld[bad_x][bad_y];
12655 static int test_xy[4][2] =
12662 static int touch_dir[4] =
12664 MV_LEFT | MV_RIGHT,
12669 static int test_dir[4] =
12677 if (bad_element == EL_EXPLOSION) /* skip just exploding bad things */
12680 for (i = 0; i < NUM_DIRECTIONS; i++)
12682 int test_x, test_y, test_move_dir, test_element;
12684 test_x = bad_x + test_xy[i][0];
12685 test_y = bad_y + test_xy[i][1];
12687 if (!IN_LEV_FIELD(test_x, test_y))
12691 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
12693 test_element = Feld[test_x][test_y];
12695 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
12696 2nd case: DONT_TOUCH style bad thing does not move away from good thing
12698 if ((DONT_RUN_INTO(bad_element) && bad_move_dir == test_dir[i]) ||
12699 (DONT_TOUCH(bad_element) && test_move_dir != test_dir[i]))
12701 /* good thing is player or penguin that does not move away */
12702 if (IS_PLAYER(test_x, test_y))
12704 struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
12706 if (bad_element == EL_ROBOT && player->is_moving)
12707 continue; /* robot does not kill player if he is moving */
12709 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
12711 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
12712 continue; /* center and border element do not touch */
12720 else if (test_element == EL_PENGUIN)
12730 if (kill_x != -1 || kill_y != -1)
12732 if (IS_PLAYER(kill_x, kill_y))
12734 struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
12736 if (player->shield_deadly_time_left > 0 &&
12737 !IS_INDESTRUCTIBLE(bad_element))
12738 Bang(bad_x, bad_y);
12739 else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
12740 KillPlayer(player);
12743 Bang(kill_x, kill_y);
12747 void TestIfGoodThingGetsHitByBadThing(int bad_x, int bad_y, int bad_move_dir)
12749 int bad_element = Feld[bad_x][bad_y];
12750 int dx = (bad_move_dir == MV_LEFT ? -1 : bad_move_dir == MV_RIGHT ? +1 : 0);
12751 int dy = (bad_move_dir == MV_UP ? -1 : bad_move_dir == MV_DOWN ? +1 : 0);
12752 int test_x = bad_x + dx, test_y = bad_y + dy;
12753 int test_move_dir, test_element;
12754 int kill_x = -1, kill_y = -1;
12756 if (!IN_LEV_FIELD(test_x, test_y))
12760 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
12762 test_element = Feld[test_x][test_y];
12764 if (test_move_dir != bad_move_dir)
12766 /* good thing can be player or penguin that does not move away */
12767 if (IS_PLAYER(test_x, test_y))
12769 struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
12771 /* (note: in comparison to DONT_RUN_TO and DONT_TOUCH, also handle the
12772 player as being hit when he is moving towards the bad thing, because
12773 the "get hit by" condition would be lost after the player stops) */
12774 if (player->MovPos != 0 && player->MovDir == bad_move_dir)
12775 return; /* player moves away from bad thing */
12780 else if (test_element == EL_PENGUIN)
12787 if (kill_x != -1 || kill_y != -1)
12789 if (IS_PLAYER(kill_x, kill_y))
12791 struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
12793 if (player->shield_deadly_time_left > 0 &&
12794 !IS_INDESTRUCTIBLE(bad_element))
12795 Bang(bad_x, bad_y);
12796 else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
12797 KillPlayer(player);
12800 Bang(kill_x, kill_y);
12804 void TestIfPlayerTouchesBadThing(int x, int y)
12806 TestIfGoodThingHitsBadThing(x, y, MV_NONE);
12809 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
12811 TestIfGoodThingHitsBadThing(x, y, move_dir);
12814 void TestIfBadThingTouchesPlayer(int x, int y)
12816 TestIfBadThingHitsGoodThing(x, y, MV_NONE);
12819 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
12821 TestIfBadThingHitsGoodThing(x, y, move_dir);
12824 void TestIfFriendTouchesBadThing(int x, int y)
12826 TestIfGoodThingHitsBadThing(x, y, MV_NONE);
12829 void TestIfBadThingTouchesFriend(int x, int y)
12831 TestIfBadThingHitsGoodThing(x, y, MV_NONE);
12834 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
12836 int i, kill_x = bad_x, kill_y = bad_y;
12837 static int xy[4][2] =
12845 for (i = 0; i < NUM_DIRECTIONS; i++)
12849 x = bad_x + xy[i][0];
12850 y = bad_y + xy[i][1];
12851 if (!IN_LEV_FIELD(x, y))
12854 element = Feld[x][y];
12855 if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
12856 element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
12864 if (kill_x != bad_x || kill_y != bad_y)
12865 Bang(bad_x, bad_y);
12868 void KillPlayer(struct PlayerInfo *player)
12870 int jx = player->jx, jy = player->jy;
12872 if (!player->active)
12876 printf("::: 0: killed == %d, active == %d, reanimated == %d\n",
12877 player->killed, player->active, player->reanimated);
12880 /* the following code was introduced to prevent an infinite loop when calling
12882 -> CheckTriggeredElementChangeExt()
12883 -> ExecuteCustomElementAction()
12885 -> (infinitely repeating the above sequence of function calls)
12886 which occurs when killing the player while having a CE with the setting
12887 "kill player X when explosion of <player X>"; the solution using a new
12888 field "player->killed" was chosen for backwards compatibility, although
12889 clever use of the fields "player->active" etc. would probably also work */
12891 if (player->killed)
12895 player->killed = TRUE;
12897 /* remove accessible field at the player's position */
12898 Feld[jx][jy] = EL_EMPTY;
12900 /* deactivate shield (else Bang()/Explode() would not work right) */
12901 player->shield_normal_time_left = 0;
12902 player->shield_deadly_time_left = 0;
12905 printf("::: 1: killed == %d, active == %d, reanimated == %d\n",
12906 player->killed, player->active, player->reanimated);
12912 printf("::: 2: killed == %d, active == %d, reanimated == %d\n",
12913 player->killed, player->active, player->reanimated);
12916 if (player->reanimated) /* killed player may have been reanimated */
12917 player->killed = player->reanimated = FALSE;
12919 BuryPlayer(player);
12922 static void KillPlayerUnlessEnemyProtected(int x, int y)
12924 if (!PLAYER_ENEMY_PROTECTED(x, y))
12925 KillPlayer(PLAYERINFO(x, y));
12928 static void KillPlayerUnlessExplosionProtected(int x, int y)
12930 if (!PLAYER_EXPLOSION_PROTECTED(x, y))
12931 KillPlayer(PLAYERINFO(x, y));
12934 void BuryPlayer(struct PlayerInfo *player)
12936 int jx = player->jx, jy = player->jy;
12938 if (!player->active)
12941 PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
12942 PlayLevelSound(jx, jy, SND_GAME_LOSING);
12944 player->GameOver = TRUE;
12945 RemovePlayer(player);
12948 void RemovePlayer(struct PlayerInfo *player)
12950 int jx = player->jx, jy = player->jy;
12951 int i, found = FALSE;
12953 player->present = FALSE;
12954 player->active = FALSE;
12956 if (!ExplodeField[jx][jy])
12957 StorePlayer[jx][jy] = 0;
12959 if (player->is_moving)
12960 TEST_DrawLevelField(player->last_jx, player->last_jy);
12962 for (i = 0; i < MAX_PLAYERS; i++)
12963 if (stored_player[i].active)
12967 AllPlayersGone = TRUE;
12973 static void setFieldForSnapping(int x, int y, int element, int direction)
12975 struct ElementInfo *ei = &element_info[element];
12976 int direction_bit = MV_DIR_TO_BIT(direction);
12977 int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
12978 int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
12979 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
12981 Feld[x][y] = EL_ELEMENT_SNAPPING;
12982 MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
12984 ResetGfxAnimation(x, y);
12986 GfxElement[x][y] = element;
12987 GfxAction[x][y] = action;
12988 GfxDir[x][y] = direction;
12989 GfxFrame[x][y] = -1;
12993 =============================================================================
12994 checkDiagonalPushing()
12995 -----------------------------------------------------------------------------
12996 check if diagonal input device direction results in pushing of object
12997 (by checking if the alternative direction is walkable, diggable, ...)
12998 =============================================================================
13001 static boolean checkDiagonalPushing(struct PlayerInfo *player,
13002 int x, int y, int real_dx, int real_dy)
13004 int jx, jy, dx, dy, xx, yy;
13006 if (real_dx == 0 || real_dy == 0) /* no diagonal direction => push */
13009 /* diagonal direction: check alternative direction */
13014 xx = jx + (dx == 0 ? real_dx : 0);
13015 yy = jy + (dy == 0 ? real_dy : 0);
13017 return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
13021 =============================================================================
13023 -----------------------------------------------------------------------------
13024 x, y: field next to player (non-diagonal) to try to dig to
13025 real_dx, real_dy: direction as read from input device (can be diagonal)
13026 =============================================================================
13029 static int DigField(struct PlayerInfo *player,
13030 int oldx, int oldy, int x, int y,
13031 int real_dx, int real_dy, int mode)
13033 boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
13034 boolean player_was_pushing = player->is_pushing;
13035 boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
13036 boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
13037 int jx = oldx, jy = oldy;
13038 int dx = x - jx, dy = y - jy;
13039 int nextx = x + dx, nexty = y + dy;
13040 int move_direction = (dx == -1 ? MV_LEFT :
13041 dx == +1 ? MV_RIGHT :
13043 dy == +1 ? MV_DOWN : MV_NONE);
13044 int opposite_direction = MV_DIR_OPPOSITE(move_direction);
13045 int dig_side = MV_DIR_OPPOSITE(move_direction);
13046 int old_element = Feld[jx][jy];
13047 int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
13050 if (is_player) /* function can also be called by EL_PENGUIN */
13052 if (player->MovPos == 0)
13054 player->is_digging = FALSE;
13055 player->is_collecting = FALSE;
13058 if (player->MovPos == 0) /* last pushing move finished */
13059 player->is_pushing = FALSE;
13061 if (mode == DF_NO_PUSH) /* player just stopped pushing */
13063 player->is_switching = FALSE;
13064 player->push_delay = -1;
13066 return MP_NO_ACTION;
13070 if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
13071 old_element = Back[jx][jy];
13073 /* in case of element dropped at player position, check background */
13074 else if (Back[jx][jy] != EL_EMPTY &&
13075 game.engine_version >= VERSION_IDENT(2,2,0,0))
13076 old_element = Back[jx][jy];
13078 if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
13079 return MP_NO_ACTION; /* field has no opening in this direction */
13081 if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
13082 return MP_NO_ACTION; /* field has no opening in this direction */
13084 if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
13088 Feld[jx][jy] = player->artwork_element;
13089 InitMovingField(jx, jy, MV_DOWN);
13090 Store[jx][jy] = EL_ACID;
13091 ContinueMoving(jx, jy);
13092 BuryPlayer(player);
13094 return MP_DONT_RUN_INTO;
13097 if (player_can_move && DONT_RUN_INTO(element))
13099 TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
13101 return MP_DONT_RUN_INTO;
13104 if (IS_MOVING(x, y) || IS_PLAYER(x, y))
13105 return MP_NO_ACTION;
13107 collect_count = element_info[element].collect_count_initial;
13109 if (!is_player && !IS_COLLECTIBLE(element)) /* penguin cannot collect it */
13110 return MP_NO_ACTION;
13112 if (game.engine_version < VERSION_IDENT(2,2,0,0))
13113 player_can_move = player_can_move_or_snap;
13115 if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
13116 game.engine_version >= VERSION_IDENT(2,2,0,0))
13118 CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
13119 player->index_bit, dig_side);
13120 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13121 player->index_bit, dig_side);
13123 if (element == EL_DC_LANDMINE)
13126 if (Feld[x][y] != element) /* field changed by snapping */
13129 return MP_NO_ACTION;
13132 if (player->gravity && is_player && !player->is_auto_moving &&
13133 canFallDown(player) && move_direction != MV_DOWN &&
13134 !canMoveToValidFieldWithGravity(jx, jy, move_direction))
13135 return MP_NO_ACTION; /* player cannot walk here due to gravity */
13137 if (player_can_move &&
13138 IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
13140 int sound_element = SND_ELEMENT(element);
13141 int sound_action = ACTION_WALKING;
13143 if (IS_RND_GATE(element))
13145 if (!player->key[RND_GATE_NR(element)])
13146 return MP_NO_ACTION;
13148 else if (IS_RND_GATE_GRAY(element))
13150 if (!player->key[RND_GATE_GRAY_NR(element)])
13151 return MP_NO_ACTION;
13153 else if (IS_RND_GATE_GRAY_ACTIVE(element))
13155 if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
13156 return MP_NO_ACTION;
13158 else if (element == EL_EXIT_OPEN ||
13159 element == EL_EM_EXIT_OPEN ||
13160 element == EL_EM_EXIT_OPENING ||
13161 element == EL_STEEL_EXIT_OPEN ||
13162 element == EL_EM_STEEL_EXIT_OPEN ||
13163 element == EL_EM_STEEL_EXIT_OPENING ||
13164 element == EL_SP_EXIT_OPEN ||
13165 element == EL_SP_EXIT_OPENING)
13167 sound_action = ACTION_PASSING; /* player is passing exit */
13169 else if (element == EL_EMPTY)
13171 sound_action = ACTION_MOVING; /* nothing to walk on */
13174 /* play sound from background or player, whatever is available */
13175 if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
13176 PlayLevelSoundElementAction(x, y, sound_element, sound_action);
13178 PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
13180 else if (player_can_move &&
13181 IS_PASSABLE(element) && canPassField(x, y, move_direction))
13183 if (!ACCESS_FROM(element, opposite_direction))
13184 return MP_NO_ACTION; /* field not accessible from this direction */
13186 if (CAN_MOVE(element)) /* only fixed elements can be passed! */
13187 return MP_NO_ACTION;
13189 if (IS_EM_GATE(element))
13191 if (!player->key[EM_GATE_NR(element)])
13192 return MP_NO_ACTION;
13194 else if (IS_EM_GATE_GRAY(element))
13196 if (!player->key[EM_GATE_GRAY_NR(element)])
13197 return MP_NO_ACTION;
13199 else if (IS_EM_GATE_GRAY_ACTIVE(element))
13201 if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
13202 return MP_NO_ACTION;
13204 else if (IS_EMC_GATE(element))
13206 if (!player->key[EMC_GATE_NR(element)])
13207 return MP_NO_ACTION;
13209 else if (IS_EMC_GATE_GRAY(element))
13211 if (!player->key[EMC_GATE_GRAY_NR(element)])
13212 return MP_NO_ACTION;
13214 else if (IS_EMC_GATE_GRAY_ACTIVE(element))
13216 if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
13217 return MP_NO_ACTION;
13219 else if (element == EL_DC_GATE_WHITE ||
13220 element == EL_DC_GATE_WHITE_GRAY ||
13221 element == EL_DC_GATE_WHITE_GRAY_ACTIVE)
13223 if (player->num_white_keys == 0)
13224 return MP_NO_ACTION;
13226 player->num_white_keys--;
13228 else if (IS_SP_PORT(element))
13230 if (element == EL_SP_GRAVITY_PORT_LEFT ||
13231 element == EL_SP_GRAVITY_PORT_RIGHT ||
13232 element == EL_SP_GRAVITY_PORT_UP ||
13233 element == EL_SP_GRAVITY_PORT_DOWN)
13234 player->gravity = !player->gravity;
13235 else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
13236 element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
13237 element == EL_SP_GRAVITY_ON_PORT_UP ||
13238 element == EL_SP_GRAVITY_ON_PORT_DOWN)
13239 player->gravity = TRUE;
13240 else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
13241 element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
13242 element == EL_SP_GRAVITY_OFF_PORT_UP ||
13243 element == EL_SP_GRAVITY_OFF_PORT_DOWN)
13244 player->gravity = FALSE;
13247 /* automatically move to the next field with double speed */
13248 player->programmed_action = move_direction;
13250 if (player->move_delay_reset_counter == 0)
13252 player->move_delay_reset_counter = 2; /* two double speed steps */
13254 DOUBLE_PLAYER_SPEED(player);
13257 PlayLevelSoundAction(x, y, ACTION_PASSING);
13259 else if (player_can_move_or_snap && IS_DIGGABLE(element))
13263 if (mode != DF_SNAP)
13265 GfxElement[x][y] = GFX_ELEMENT(element);
13266 player->is_digging = TRUE;
13269 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
13271 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
13272 player->index_bit, dig_side);
13274 if (mode == DF_SNAP)
13276 if (level.block_snap_field)
13277 setFieldForSnapping(x, y, element, move_direction);
13279 TestIfElementTouchesCustomElement(x, y); /* for empty space */
13281 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13282 player->index_bit, dig_side);
13285 else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
13289 if (is_player && mode != DF_SNAP)
13291 GfxElement[x][y] = element;
13292 player->is_collecting = TRUE;
13295 if (element == EL_SPEED_PILL)
13297 player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
13299 else if (element == EL_EXTRA_TIME && level.time > 0)
13301 TimeLeft += level.extra_time;
13303 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
13305 DisplayGameControlValues();
13307 else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
13309 player->shield_normal_time_left += level.shield_normal_time;
13310 if (element == EL_SHIELD_DEADLY)
13311 player->shield_deadly_time_left += level.shield_deadly_time;
13313 else if (element == EL_DYNAMITE ||
13314 element == EL_EM_DYNAMITE ||
13315 element == EL_SP_DISK_RED)
13317 if (player->inventory_size < MAX_INVENTORY_SIZE)
13318 player->inventory_element[player->inventory_size++] = element;
13320 DrawGameDoorValues();
13322 else if (element == EL_DYNABOMB_INCREASE_NUMBER)
13324 player->dynabomb_count++;
13325 player->dynabombs_left++;
13327 else if (element == EL_DYNABOMB_INCREASE_SIZE)
13329 player->dynabomb_size++;
13331 else if (element == EL_DYNABOMB_INCREASE_POWER)
13333 player->dynabomb_xl = TRUE;
13335 else if (IS_KEY(element))
13337 player->key[KEY_NR(element)] = TRUE;
13339 DrawGameDoorValues();
13341 else if (element == EL_DC_KEY_WHITE)
13343 player->num_white_keys++;
13345 /* display white keys? */
13346 /* DrawGameDoorValues(); */
13348 else if (IS_ENVELOPE(element))
13350 player->show_envelope = element;
13352 else if (element == EL_EMC_LENSES)
13354 game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
13356 RedrawAllInvisibleElementsForLenses();
13358 else if (element == EL_EMC_MAGNIFIER)
13360 game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
13362 RedrawAllInvisibleElementsForMagnifier();
13364 else if (IS_DROPPABLE(element) ||
13365 IS_THROWABLE(element)) /* can be collected and dropped */
13369 if (collect_count == 0)
13370 player->inventory_infinite_element = element;
13372 for (i = 0; i < collect_count; i++)
13373 if (player->inventory_size < MAX_INVENTORY_SIZE)
13374 player->inventory_element[player->inventory_size++] = element;
13376 DrawGameDoorValues();
13378 else if (collect_count > 0)
13380 local_player->gems_still_needed -= collect_count;
13381 if (local_player->gems_still_needed < 0)
13382 local_player->gems_still_needed = 0;
13384 game_panel_controls[GAME_PANEL_GEMS].value = local_player->gems_still_needed;
13386 DisplayGameControlValues();
13389 RaiseScoreElement(element);
13390 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
13393 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
13394 player->index_bit, dig_side);
13396 if (mode == DF_SNAP)
13398 if (level.block_snap_field)
13399 setFieldForSnapping(x, y, element, move_direction);
13401 TestIfElementTouchesCustomElement(x, y); /* for empty space */
13403 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13404 player->index_bit, dig_side);
13407 else if (player_can_move_or_snap && IS_PUSHABLE(element))
13409 if (mode == DF_SNAP && element != EL_BD_ROCK)
13410 return MP_NO_ACTION;
13412 if (CAN_FALL(element) && dy)
13413 return MP_NO_ACTION;
13415 if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
13416 !(element == EL_SPRING && level.use_spring_bug))
13417 return MP_NO_ACTION;
13419 if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
13420 ((move_direction & MV_VERTICAL &&
13421 ((element_info[element].move_pattern & MV_LEFT &&
13422 IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
13423 (element_info[element].move_pattern & MV_RIGHT &&
13424 IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
13425 (move_direction & MV_HORIZONTAL &&
13426 ((element_info[element].move_pattern & MV_UP &&
13427 IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
13428 (element_info[element].move_pattern & MV_DOWN &&
13429 IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
13430 return MP_NO_ACTION;
13432 /* do not push elements already moving away faster than player */
13433 if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
13434 ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
13435 return MP_NO_ACTION;
13437 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
13439 if (player->push_delay_value == -1 || !player_was_pushing)
13440 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13442 else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
13444 if (player->push_delay_value == -1)
13445 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13447 else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
13449 if (!player->is_pushing)
13450 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13453 player->is_pushing = TRUE;
13454 player->is_active = TRUE;
13456 if (!(IN_LEV_FIELD(nextx, nexty) &&
13457 (IS_FREE(nextx, nexty) ||
13458 (IS_SB_ELEMENT(element) &&
13459 Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY) ||
13460 (IS_CUSTOM_ELEMENT(element) &&
13461 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty)))))
13462 return MP_NO_ACTION;
13464 if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
13465 return MP_NO_ACTION;
13467 if (player->push_delay == -1) /* new pushing; restart delay */
13468 player->push_delay = 0;
13470 if (player->push_delay < player->push_delay_value &&
13471 !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
13472 element != EL_SPRING && element != EL_BALLOON)
13474 /* make sure that there is no move delay before next try to push */
13475 if (game.engine_version >= VERSION_IDENT(3,0,7,1))
13476 player->move_delay = 0;
13478 return MP_NO_ACTION;
13481 if (IS_CUSTOM_ELEMENT(element) &&
13482 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty))
13484 if (!DigFieldByCE(nextx, nexty, element))
13485 return MP_NO_ACTION;
13488 if (IS_SB_ELEMENT(element))
13490 if (element == EL_SOKOBAN_FIELD_FULL)
13492 Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
13493 local_player->sokobanfields_still_needed++;
13496 if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
13498 Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
13499 local_player->sokobanfields_still_needed--;
13502 Feld[x][y] = EL_SOKOBAN_OBJECT;
13504 if (Back[x][y] == Back[nextx][nexty])
13505 PlayLevelSoundAction(x, y, ACTION_PUSHING);
13506 else if (Back[x][y] != 0)
13507 PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
13510 PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
13513 if (local_player->sokobanfields_still_needed == 0 &&
13514 (game.emulation == EMU_SOKOBAN || level.auto_exit_sokoban))
13516 PlayerWins(player);
13518 PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
13522 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
13524 InitMovingField(x, y, move_direction);
13525 GfxAction[x][y] = ACTION_PUSHING;
13527 if (mode == DF_SNAP)
13528 ContinueMoving(x, y);
13530 MovPos[x][y] = (dx != 0 ? dx : dy);
13532 Pushed[x][y] = TRUE;
13533 Pushed[nextx][nexty] = TRUE;
13535 if (game.engine_version < VERSION_IDENT(2,2,0,7))
13536 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13538 player->push_delay_value = -1; /* get new value later */
13540 /* check for element change _after_ element has been pushed */
13541 if (game.use_change_when_pushing_bug)
13543 CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
13544 player->index_bit, dig_side);
13545 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
13546 player->index_bit, dig_side);
13549 else if (IS_SWITCHABLE(element))
13551 if (PLAYER_SWITCHING(player, x, y))
13553 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
13554 player->index_bit, dig_side);
13559 player->is_switching = TRUE;
13560 player->switch_x = x;
13561 player->switch_y = y;
13563 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
13565 if (element == EL_ROBOT_WHEEL)
13567 Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
13571 game.robot_wheel_active = TRUE;
13573 TEST_DrawLevelField(x, y);
13575 else if (element == EL_SP_TERMINAL)
13579 SCAN_PLAYFIELD(xx, yy)
13581 if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
13583 else if (Feld[xx][yy] == EL_SP_TERMINAL)
13584 Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
13587 else if (IS_BELT_SWITCH(element))
13589 ToggleBeltSwitch(x, y);
13591 else if (element == EL_SWITCHGATE_SWITCH_UP ||
13592 element == EL_SWITCHGATE_SWITCH_DOWN ||
13593 element == EL_DC_SWITCHGATE_SWITCH_UP ||
13594 element == EL_DC_SWITCHGATE_SWITCH_DOWN)
13596 ToggleSwitchgateSwitch(x, y);
13598 else if (element == EL_LIGHT_SWITCH ||
13599 element == EL_LIGHT_SWITCH_ACTIVE)
13601 ToggleLightSwitch(x, y);
13603 else if (element == EL_TIMEGATE_SWITCH ||
13604 element == EL_DC_TIMEGATE_SWITCH)
13606 ActivateTimegateSwitch(x, y);
13608 else if (element == EL_BALLOON_SWITCH_LEFT ||
13609 element == EL_BALLOON_SWITCH_RIGHT ||
13610 element == EL_BALLOON_SWITCH_UP ||
13611 element == EL_BALLOON_SWITCH_DOWN ||
13612 element == EL_BALLOON_SWITCH_NONE ||
13613 element == EL_BALLOON_SWITCH_ANY)
13615 game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT ? MV_LEFT :
13616 element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
13617 element == EL_BALLOON_SWITCH_UP ? MV_UP :
13618 element == EL_BALLOON_SWITCH_DOWN ? MV_DOWN :
13619 element == EL_BALLOON_SWITCH_NONE ? MV_NONE :
13622 else if (element == EL_LAMP)
13624 Feld[x][y] = EL_LAMP_ACTIVE;
13625 local_player->lights_still_needed--;
13627 ResetGfxAnimation(x, y);
13628 TEST_DrawLevelField(x, y);
13630 else if (element == EL_TIME_ORB_FULL)
13632 Feld[x][y] = EL_TIME_ORB_EMPTY;
13634 if (level.time > 0 || level.use_time_orb_bug)
13636 TimeLeft += level.time_orb_time;
13637 game.no_time_limit = FALSE;
13639 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
13641 DisplayGameControlValues();
13644 ResetGfxAnimation(x, y);
13645 TEST_DrawLevelField(x, y);
13647 else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
13648 element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
13652 game.ball_state = !game.ball_state;
13654 SCAN_PLAYFIELD(xx, yy)
13656 int e = Feld[xx][yy];
13658 if (game.ball_state)
13660 if (e == EL_EMC_MAGIC_BALL)
13661 CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
13662 else if (e == EL_EMC_MAGIC_BALL_SWITCH)
13663 CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
13667 if (e == EL_EMC_MAGIC_BALL_ACTIVE)
13668 CreateField(xx, yy, EL_EMC_MAGIC_BALL);
13669 else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
13670 CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
13675 CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
13676 player->index_bit, dig_side);
13678 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
13679 player->index_bit, dig_side);
13681 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
13682 player->index_bit, dig_side);
13688 if (!PLAYER_SWITCHING(player, x, y))
13690 player->is_switching = TRUE;
13691 player->switch_x = x;
13692 player->switch_y = y;
13694 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
13695 player->index_bit, dig_side);
13696 CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
13697 player->index_bit, dig_side);
13699 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
13700 player->index_bit, dig_side);
13701 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
13702 player->index_bit, dig_side);
13705 CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
13706 player->index_bit, dig_side);
13707 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
13708 player->index_bit, dig_side);
13710 return MP_NO_ACTION;
13713 player->push_delay = -1;
13715 if (is_player) /* function can also be called by EL_PENGUIN */
13717 if (Feld[x][y] != element) /* really digged/collected something */
13719 player->is_collecting = !player->is_digging;
13720 player->is_active = TRUE;
13727 static boolean DigFieldByCE(int x, int y, int digging_element)
13729 int element = Feld[x][y];
13731 if (!IS_FREE(x, y))
13733 int action = (IS_DIGGABLE(element) ? ACTION_DIGGING :
13734 IS_COLLECTIBLE(element) ? ACTION_COLLECTING :
13737 /* no element can dig solid indestructible elements */
13738 if (IS_INDESTRUCTIBLE(element) &&
13739 !IS_DIGGABLE(element) &&
13740 !IS_COLLECTIBLE(element))
13743 if (AmoebaNr[x][y] &&
13744 (element == EL_AMOEBA_FULL ||
13745 element == EL_BD_AMOEBA ||
13746 element == EL_AMOEBA_GROWING))
13748 AmoebaCnt[AmoebaNr[x][y]]--;
13749 AmoebaCnt2[AmoebaNr[x][y]]--;
13752 if (IS_MOVING(x, y))
13753 RemoveMovingField(x, y);
13757 TEST_DrawLevelField(x, y);
13760 /* if digged element was about to explode, prevent the explosion */
13761 ExplodeField[x][y] = EX_TYPE_NONE;
13763 PlayLevelSoundAction(x, y, action);
13766 Store[x][y] = EL_EMPTY;
13768 /* this makes it possible to leave the removed element again */
13769 if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element)))
13770 Store[x][y] = element;
13775 static boolean SnapField(struct PlayerInfo *player, int dx, int dy)
13777 int jx = player->jx, jy = player->jy;
13778 int x = jx + dx, y = jy + dy;
13779 int snap_direction = (dx == -1 ? MV_LEFT :
13780 dx == +1 ? MV_RIGHT :
13782 dy == +1 ? MV_DOWN : MV_NONE);
13783 boolean can_continue_snapping = (level.continuous_snapping &&
13784 WasJustFalling[x][y] < CHECK_DELAY_FALLING);
13786 if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
13789 if (!player->active || !IN_LEV_FIELD(x, y))
13797 if (player->MovPos == 0)
13798 player->is_pushing = FALSE;
13800 player->is_snapping = FALSE;
13802 if (player->MovPos == 0)
13804 player->is_moving = FALSE;
13805 player->is_digging = FALSE;
13806 player->is_collecting = FALSE;
13812 /* prevent snapping with already pressed snap key when not allowed */
13813 if (player->is_snapping && !can_continue_snapping)
13816 player->MovDir = snap_direction;
13818 if (player->MovPos == 0)
13820 player->is_moving = FALSE;
13821 player->is_digging = FALSE;
13822 player->is_collecting = FALSE;
13825 player->is_dropping = FALSE;
13826 player->is_dropping_pressed = FALSE;
13827 player->drop_pressed_delay = 0;
13829 if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
13832 player->is_snapping = TRUE;
13833 player->is_active = TRUE;
13835 if (player->MovPos == 0)
13837 player->is_moving = FALSE;
13838 player->is_digging = FALSE;
13839 player->is_collecting = FALSE;
13842 if (player->MovPos != 0) /* prevent graphic bugs in versions < 2.2.0 */
13843 TEST_DrawLevelField(player->last_jx, player->last_jy);
13845 TEST_DrawLevelField(x, y);
13850 static boolean DropElement(struct PlayerInfo *player)
13852 int old_element, new_element;
13853 int dropx = player->jx, dropy = player->jy;
13854 int drop_direction = player->MovDir;
13855 int drop_side = drop_direction;
13856 int drop_element = get_next_dropped_element(player);
13858 player->is_dropping_pressed = TRUE;
13860 /* do not drop an element on top of another element; when holding drop key
13861 pressed without moving, dropped element must move away before the next
13862 element can be dropped (this is especially important if the next element
13863 is dynamite, which can be placed on background for historical reasons) */
13864 if (PLAYER_DROPPING(player, dropx, dropy) && Feld[dropx][dropy] != EL_EMPTY)
13867 if (IS_THROWABLE(drop_element))
13869 dropx += GET_DX_FROM_DIR(drop_direction);
13870 dropy += GET_DY_FROM_DIR(drop_direction);
13872 if (!IN_LEV_FIELD(dropx, dropy))
13876 old_element = Feld[dropx][dropy]; /* old element at dropping position */
13877 new_element = drop_element; /* default: no change when dropping */
13879 /* check if player is active, not moving and ready to drop */
13880 if (!player->active || player->MovPos || player->drop_delay > 0)
13883 /* check if player has anything that can be dropped */
13884 if (new_element == EL_UNDEFINED)
13887 /* check if drop key was pressed long enough for EM style dynamite */
13888 if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
13891 /* check if anything can be dropped at the current position */
13892 if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
13895 /* collected custom elements can only be dropped on empty fields */
13896 if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
13899 if (old_element != EL_EMPTY)
13900 Back[dropx][dropy] = old_element; /* store old element on this field */
13902 ResetGfxAnimation(dropx, dropy);
13903 ResetRandomAnimationValue(dropx, dropy);
13905 if (player->inventory_size > 0 ||
13906 player->inventory_infinite_element != EL_UNDEFINED)
13908 if (player->inventory_size > 0)
13910 player->inventory_size--;
13912 DrawGameDoorValues();
13914 if (new_element == EL_DYNAMITE)
13915 new_element = EL_DYNAMITE_ACTIVE;
13916 else if (new_element == EL_EM_DYNAMITE)
13917 new_element = EL_EM_DYNAMITE_ACTIVE;
13918 else if (new_element == EL_SP_DISK_RED)
13919 new_element = EL_SP_DISK_RED_ACTIVE;
13922 Feld[dropx][dropy] = new_element;
13924 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
13925 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
13926 el2img(Feld[dropx][dropy]), 0);
13928 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
13930 /* needed if previous element just changed to "empty" in the last frame */
13931 ChangeCount[dropx][dropy] = 0; /* allow at least one more change */
13933 CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
13934 player->index_bit, drop_side);
13935 CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
13937 player->index_bit, drop_side);
13939 TestIfElementTouchesCustomElement(dropx, dropy);
13941 else /* player is dropping a dyna bomb */
13943 player->dynabombs_left--;
13945 Feld[dropx][dropy] = new_element;
13947 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
13948 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
13949 el2img(Feld[dropx][dropy]), 0);
13951 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
13954 if (Feld[dropx][dropy] == new_element) /* uninitialized unless CE change */
13955 InitField_WithBug1(dropx, dropy, FALSE);
13957 new_element = Feld[dropx][dropy]; /* element might have changed */
13959 if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
13960 element_info[new_element].move_pattern == MV_WHEN_DROPPED)
13962 if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
13963 MovDir[dropx][dropy] = drop_direction;
13965 ChangeCount[dropx][dropy] = 0; /* allow at least one more change */
13967 /* do not cause impact style collision by dropping elements that can fall */
13968 CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
13971 player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
13972 player->is_dropping = TRUE;
13974 player->drop_pressed_delay = 0;
13975 player->is_dropping_pressed = FALSE;
13977 player->drop_x = dropx;
13978 player->drop_y = dropy;
13983 /* ------------------------------------------------------------------------- */
13984 /* game sound playing functions */
13985 /* ------------------------------------------------------------------------- */
13987 static int *loop_sound_frame = NULL;
13988 static int *loop_sound_volume = NULL;
13990 void InitPlayLevelSound()
13992 int num_sounds = getSoundListSize();
13994 checked_free(loop_sound_frame);
13995 checked_free(loop_sound_volume);
13997 loop_sound_frame = checked_calloc(num_sounds * sizeof(int));
13998 loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
14001 static void PlayLevelSound(int x, int y, int nr)
14003 int sx = SCREENX(x), sy = SCREENY(y);
14004 int volume, stereo_position;
14005 int max_distance = 8;
14006 int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
14008 if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
14009 (!setup.sound_loops && IS_LOOP_SOUND(nr)))
14012 if (!IN_LEV_FIELD(x, y) ||
14013 sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
14014 sy < -max_distance || sy >= SCR_FIELDY + max_distance)
14017 volume = SOUND_MAX_VOLUME;
14019 if (!IN_SCR_FIELD(sx, sy))
14021 int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
14022 int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
14024 volume -= volume * (dx > dy ? dx : dy) / max_distance;
14027 stereo_position = (SOUND_MAX_LEFT +
14028 (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
14029 (SCR_FIELDX + 2 * max_distance));
14031 if (IS_LOOP_SOUND(nr))
14033 /* This assures that quieter loop sounds do not overwrite louder ones,
14034 while restarting sound volume comparison with each new game frame. */
14036 if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
14039 loop_sound_volume[nr] = volume;
14040 loop_sound_frame[nr] = FrameCounter;
14043 PlaySoundExt(nr, volume, stereo_position, type);
14046 static void PlayLevelSoundNearest(int x, int y, int sound_action)
14048 PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
14049 x > LEVELX(BX2) ? LEVELX(BX2) : x,
14050 y < LEVELY(BY1) ? LEVELY(BY1) :
14051 y > LEVELY(BY2) ? LEVELY(BY2) : y,
14055 static void PlayLevelSoundAction(int x, int y, int action)
14057 PlayLevelSoundElementAction(x, y, Feld[x][y], action);
14060 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
14062 int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
14064 if (sound_effect != SND_UNDEFINED)
14065 PlayLevelSound(x, y, sound_effect);
14068 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
14071 int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
14073 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14074 PlayLevelSound(x, y, sound_effect);
14077 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
14079 int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
14081 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14082 PlayLevelSound(x, y, sound_effect);
14085 static void StopLevelSoundActionIfLoop(int x, int y, int action)
14087 int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
14089 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14090 StopSound(sound_effect);
14093 static void PlayLevelMusic()
14095 if (levelset.music[level_nr] != MUS_UNDEFINED)
14096 PlayMusic(levelset.music[level_nr]); /* from config file */
14098 PlayMusic(MAP_NOCONF_MUSIC(level_nr)); /* from music dir */
14101 void PlayLevelSound_EM(int xx, int yy, int element_em, int sample)
14103 int element = (element_em > -1 ? map_element_EM_to_RND(element_em) : 0);
14104 int offset = (BorderElement == EL_STEELWALL ? 1 : 0);
14105 int x = xx - 1 - offset;
14106 int y = yy - 1 - offset;
14111 PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
14115 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14119 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14123 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14127 PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
14131 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14135 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14138 case SAMPLE_android_clone:
14139 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14142 case SAMPLE_android_move:
14143 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14146 case SAMPLE_spring:
14147 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14151 PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
14155 PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
14158 case SAMPLE_eater_eat:
14159 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14163 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14166 case SAMPLE_collect:
14167 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
14170 case SAMPLE_diamond:
14171 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14174 case SAMPLE_squash:
14175 /* !!! CHECK THIS !!! */
14177 PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
14179 PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
14183 case SAMPLE_wonderfall:
14184 PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
14188 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14192 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14196 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14200 PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
14204 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14208 PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
14211 case SAMPLE_wonder:
14212 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14216 PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
14219 case SAMPLE_exit_open:
14220 PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
14223 case SAMPLE_exit_leave:
14224 PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
14227 case SAMPLE_dynamite:
14228 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14232 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14236 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
14240 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14244 PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
14248 PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
14252 PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
14256 PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
14261 void PlayLevelSound_SP(int xx, int yy, int element_sp, int action_sp)
14263 int element = map_element_SP_to_RND(element_sp);
14264 int action = map_action_SP_to_RND(action_sp);
14265 int offset = (setup.sp_show_border_elements ? 0 : 1);
14266 int x = xx - offset;
14267 int y = yy - offset;
14269 PlayLevelSoundElementAction(x, y, element, action);
14272 void RaiseScore(int value)
14274 local_player->score += value;
14276 game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
14278 DisplayGameControlValues();
14281 void RaiseScoreElement(int element)
14286 case EL_BD_DIAMOND:
14287 case EL_EMERALD_YELLOW:
14288 case EL_EMERALD_RED:
14289 case EL_EMERALD_PURPLE:
14290 case EL_SP_INFOTRON:
14291 RaiseScore(level.score[SC_EMERALD]);
14294 RaiseScore(level.score[SC_DIAMOND]);
14297 RaiseScore(level.score[SC_CRYSTAL]);
14300 RaiseScore(level.score[SC_PEARL]);
14303 case EL_BD_BUTTERFLY:
14304 case EL_SP_ELECTRON:
14305 RaiseScore(level.score[SC_BUG]);
14308 case EL_BD_FIREFLY:
14309 case EL_SP_SNIKSNAK:
14310 RaiseScore(level.score[SC_SPACESHIP]);
14313 case EL_DARK_YAMYAM:
14314 RaiseScore(level.score[SC_YAMYAM]);
14317 RaiseScore(level.score[SC_ROBOT]);
14320 RaiseScore(level.score[SC_PACMAN]);
14323 RaiseScore(level.score[SC_NUT]);
14326 case EL_EM_DYNAMITE:
14327 case EL_SP_DISK_RED:
14328 case EL_DYNABOMB_INCREASE_NUMBER:
14329 case EL_DYNABOMB_INCREASE_SIZE:
14330 case EL_DYNABOMB_INCREASE_POWER:
14331 RaiseScore(level.score[SC_DYNAMITE]);
14333 case EL_SHIELD_NORMAL:
14334 case EL_SHIELD_DEADLY:
14335 RaiseScore(level.score[SC_SHIELD]);
14337 case EL_EXTRA_TIME:
14338 RaiseScore(level.extra_time_score);
14352 case EL_DC_KEY_WHITE:
14353 RaiseScore(level.score[SC_KEY]);
14356 RaiseScore(element_info[element].collect_score);
14361 void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message)
14363 if (skip_request || Request(message, REQ_ASK | REQ_STAY_CLOSED))
14365 /* closing door required in case of envelope style request dialogs */
14367 CloseDoor(DOOR_CLOSE_1);
14369 #if defined(NETWORK_AVALIABLE)
14370 if (options.network)
14371 SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
14377 FadeSkipNextFadeIn();
14379 game_status = GAME_MODE_MAIN;
14381 DrawAndFadeInMainMenu(REDRAW_FIELD);
14385 game_status = GAME_MODE_MAIN;
14387 DrawAndFadeInMainMenu(REDRAW_FIELD);
14391 else /* continue playing the game */
14393 if (tape.playing && tape.deactivate_display)
14394 TapeDeactivateDisplayOff(TRUE);
14396 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
14398 if (tape.playing && tape.deactivate_display)
14399 TapeDeactivateDisplayOn();
14403 void RequestQuitGame(boolean ask_if_really_quit)
14405 boolean quick_quit = (!ask_if_really_quit || level_editor_test_game);
14406 boolean skip_request = AllPlayersGone || quick_quit;
14408 RequestQuitGameExt(skip_request, quick_quit,
14409 "Do you really want to quit the game?");
14413 /* ------------------------------------------------------------------------- */
14414 /* random generator functions */
14415 /* ------------------------------------------------------------------------- */
14417 unsigned int InitEngineRandom_RND(int seed)
14419 game.num_random_calls = 0;
14421 return InitEngineRandom(seed);
14424 unsigned int RND(int max)
14428 game.num_random_calls++;
14430 return GetEngineRandom(max);
14437 /* ------------------------------------------------------------------------- */
14438 /* game engine snapshot handling functions */
14439 /* ------------------------------------------------------------------------- */
14441 struct EngineSnapshotInfo
14443 /* runtime values for custom element collect score */
14444 int collect_score[NUM_CUSTOM_ELEMENTS];
14446 /* runtime values for group element choice position */
14447 int choice_pos[NUM_GROUP_ELEMENTS];
14449 /* runtime values for belt position animations */
14450 int belt_graphic[4][NUM_BELT_PARTS];
14451 int belt_anim_mode[4][NUM_BELT_PARTS];
14454 static struct EngineSnapshotInfo engine_snapshot_rnd;
14455 static char *snapshot_level_identifier = NULL;
14456 static int snapshot_level_nr = -1;
14458 static void SaveEngineSnapshotValues_RND()
14460 static int belt_base_active_element[4] =
14462 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
14463 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
14464 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
14465 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
14469 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
14471 int element = EL_CUSTOM_START + i;
14473 engine_snapshot_rnd.collect_score[i] = element_info[element].collect_score;
14476 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
14478 int element = EL_GROUP_START + i;
14480 engine_snapshot_rnd.choice_pos[i] = element_info[element].group->choice_pos;
14483 for (i = 0; i < 4; i++)
14485 for (j = 0; j < NUM_BELT_PARTS; j++)
14487 int element = belt_base_active_element[i] + j;
14488 int graphic = el2img(element);
14489 int anim_mode = graphic_info[graphic].anim_mode;
14491 engine_snapshot_rnd.belt_graphic[i][j] = graphic;
14492 engine_snapshot_rnd.belt_anim_mode[i][j] = anim_mode;
14497 static void LoadEngineSnapshotValues_RND()
14499 unsigned int num_random_calls = game.num_random_calls;
14502 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
14504 int element = EL_CUSTOM_START + i;
14506 element_info[element].collect_score = engine_snapshot_rnd.collect_score[i];
14509 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
14511 int element = EL_GROUP_START + i;
14513 element_info[element].group->choice_pos = engine_snapshot_rnd.choice_pos[i];
14516 for (i = 0; i < 4; i++)
14518 for (j = 0; j < NUM_BELT_PARTS; j++)
14520 int graphic = engine_snapshot_rnd.belt_graphic[i][j];
14521 int anim_mode = engine_snapshot_rnd.belt_anim_mode[i][j];
14523 graphic_info[graphic].anim_mode = anim_mode;
14527 if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
14529 InitRND(tape.random_seed);
14530 for (i = 0; i < num_random_calls; i++)
14534 if (game.num_random_calls != num_random_calls)
14536 Error(ERR_INFO, "number of random calls out of sync");
14537 Error(ERR_INFO, "number of random calls should be %d", num_random_calls);
14538 Error(ERR_INFO, "number of random calls is %d", game.num_random_calls);
14539 Error(ERR_EXIT, "this should not happen -- please debug");
14543 void FreeEngineSnapshot()
14545 FreeEngineSnapshotBuffers();
14547 setString(&snapshot_level_identifier, NULL);
14548 snapshot_level_nr = -1;
14551 void SaveEngineSnapshot()
14553 /* do not save snapshots from editor */
14554 if (level_editor_test_game)
14557 /* free previous snapshot buffers, if needed */
14558 FreeEngineSnapshotBuffers();
14560 /* copy some special values to a structure better suited for the snapshot */
14562 if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
14563 SaveEngineSnapshotValues_RND();
14564 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
14565 SaveEngineSnapshotValues_EM();
14566 if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
14567 SaveEngineSnapshotValues_SP();
14569 /* save values stored in special snapshot structure */
14571 if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
14572 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
14573 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
14574 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
14575 if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
14576 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_sp));
14578 /* save further RND engine values */
14580 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(stored_player));
14581 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(game));
14582 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(tape));
14584 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ZX));
14585 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ZY));
14586 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExitX));
14587 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExitY));
14589 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(FrameCounter));
14590 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimeFrames));
14591 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimePlayed));
14592 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimeLeft));
14593 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TapeTime));
14595 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenMovDir));
14596 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenMovPos));
14597 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenGfxPos));
14599 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScrollStepSize));
14601 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AllPlayersGone));
14603 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt));
14604 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2));
14606 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Feld));
14607 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovPos));
14608 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovDir));
14609 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovDelay));
14610 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeDelay));
14611 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangePage));
14612 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CustomValue));
14613 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Store));
14614 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Store2));
14615 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(StorePlayer));
14616 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Back));
14617 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaNr));
14618 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(WasJustMoving));
14619 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(WasJustFalling));
14620 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CheckCollision));
14621 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CheckImpact));
14622 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Stop));
14623 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Pushed));
14625 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeCount));
14626 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeEvent));
14628 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodePhase));
14629 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodeDelay));
14630 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodeField));
14632 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(RunnerVisit));
14633 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(PlayerVisit));
14635 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxFrame));
14636 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxRandom));
14637 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxElement));
14638 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxAction));
14639 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxDir));
14641 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(scroll_x));
14642 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(scroll_y));
14644 /* save level identification information */
14646 setString(&snapshot_level_identifier, leveldir_current->identifier);
14647 snapshot_level_nr = level_nr;
14650 ListNode *node = engine_snapshot_list_rnd;
14653 while (node != NULL)
14655 num_bytes += ((struct EngineSnapshotNodeInfo *)node->content)->size;
14660 printf("::: size of engine snapshot: %d bytes\n", num_bytes);
14664 void LoadEngineSnapshot()
14666 /* restore generically stored snapshot buffers */
14668 LoadEngineSnapshotBuffers();
14670 /* restore special values from snapshot structure */
14672 if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
14673 LoadEngineSnapshotValues_RND();
14674 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
14675 LoadEngineSnapshotValues_EM();
14676 if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
14677 LoadEngineSnapshotValues_SP();
14680 boolean CheckEngineSnapshot()
14682 return (strEqual(snapshot_level_identifier, leveldir_current->identifier) &&
14683 snapshot_level_nr == level_nr);
14687 /* ---------- new game button stuff ---------------------------------------- */
14695 } gamebutton_info[NUM_GAME_BUTTONS] =
14698 IMG_GAME_BUTTON_GFX_STOP, &game.button.stop,
14699 GAME_CTRL_ID_STOP, "stop game"
14702 IMG_GAME_BUTTON_GFX_PAUSE, &game.button.pause,
14703 GAME_CTRL_ID_PAUSE, "pause game"
14706 IMG_GAME_BUTTON_GFX_PLAY, &game.button.play,
14707 GAME_CTRL_ID_PLAY, "play game"
14710 IMG_GAME_BUTTON_GFX_SOUND_MUSIC, &game.button.sound_music,
14711 SOUND_CTRL_ID_MUSIC, "background music on/off"
14714 IMG_GAME_BUTTON_GFX_SOUND_LOOPS, &game.button.sound_loops,
14715 SOUND_CTRL_ID_LOOPS, "sound loops on/off"
14718 IMG_GAME_BUTTON_GFX_SOUND_SIMPLE, &game.button.sound_simple,
14719 SOUND_CTRL_ID_SIMPLE, "normal sounds on/off"
14722 IMG_GAME_BUTTON_GFX_SAVE, &game.button.save,
14723 GAME_CTRL_ID_SAVE, "save game"
14726 IMG_GAME_BUTTON_GFX_LOAD, &game.button.load,
14727 GAME_CTRL_ID_LOAD, "load game"
14731 void CreateGameButtons()
14735 for (i = 0; i < NUM_GAME_BUTTONS; i++)
14737 struct GraphicInfo *gfx = &graphic_info[gamebutton_info[i].graphic];
14738 struct Rect *pos = gamebutton_info[i].pos;
14739 struct GadgetInfo *gi;
14742 unsigned int event_mask;
14743 int base_x = (tape.show_game_buttons ? VX : DX);
14744 int base_y = (tape.show_game_buttons ? VY : DY);
14745 int gd_x = gfx->src_x;
14746 int gd_y = gfx->src_y;
14747 int gd_xp = gfx->src_x + gfx->pressed_xoffset;
14748 int gd_yp = gfx->src_y + gfx->pressed_yoffset;
14749 int gd_xa = gfx->src_x + gfx->active_xoffset;
14750 int gd_ya = gfx->src_y + gfx->active_yoffset;
14751 int gd_xap = gfx->src_x + gfx->active_xoffset + gfx->pressed_xoffset;
14752 int gd_yap = gfx->src_y + gfx->active_yoffset + gfx->pressed_yoffset;
14755 if (gfx->bitmap == NULL)
14757 game_gadget[id] = NULL;
14762 if (id == GAME_CTRL_ID_STOP ||
14763 id == GAME_CTRL_ID_PAUSE ||
14764 id == GAME_CTRL_ID_PLAY ||
14765 id == GAME_CTRL_ID_SAVE ||
14766 id == GAME_CTRL_ID_LOAD)
14768 button_type = GD_TYPE_NORMAL_BUTTON;
14770 event_mask = GD_EVENT_RELEASED;
14774 button_type = GD_TYPE_CHECK_BUTTON;
14776 ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
14777 (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
14778 (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
14779 event_mask = GD_EVENT_PRESSED;
14782 gi = CreateGadget(GDI_CUSTOM_ID, id,
14783 GDI_INFO_TEXT, gamebutton_info[i].infotext,
14784 GDI_X, base_x + GDI_ACTIVE_POS(pos->x),
14785 GDI_Y, base_y + GDI_ACTIVE_POS(pos->y),
14786 GDI_WIDTH, gfx->width,
14787 GDI_HEIGHT, gfx->height,
14788 GDI_TYPE, button_type,
14789 GDI_STATE, GD_BUTTON_UNPRESSED,
14790 GDI_CHECKED, checked,
14791 GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
14792 GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
14793 GDI_ALT_DESIGN_UNPRESSED, gfx->bitmap, gd_xa, gd_ya,
14794 GDI_ALT_DESIGN_PRESSED, gfx->bitmap, gd_xap, gd_yap,
14795 GDI_DIRECT_DRAW, FALSE,
14796 GDI_EVENT_MASK, event_mask,
14797 GDI_CALLBACK_ACTION, HandleGameButtons,
14801 Error(ERR_EXIT, "cannot create gadget");
14803 game_gadget[id] = gi;
14807 void FreeGameButtons()
14811 for (i = 0; i < NUM_GAME_BUTTONS; i++)
14812 FreeGadget(game_gadget[i]);
14815 void MapGameButtons()
14819 for (i = 0; i < NUM_GAME_BUTTONS; i++)
14820 MapGadget(game_gadget[i]);
14823 void UnmapGameButtons()
14827 for (i = 0; i < NUM_GAME_BUTTONS; i++)
14828 UnmapGadget(game_gadget[i]);
14831 void RedrawGameButtons()
14835 for (i = 0; i < NUM_GAME_BUTTONS; i++)
14836 RedrawGadget(game_gadget[i]);
14838 // RedrawGadget() may have set REDRAW_ALL if buttons are defined off-area
14839 redraw_mask &= ~REDRAW_ALL;
14842 static void HandleGameButtonsExt(int id)
14844 boolean handle_game_buttons =
14845 (game_status == GAME_MODE_PLAYING ||
14846 (game_status == GAME_MODE_MAIN && tape.show_game_buttons));
14848 if (!handle_game_buttons)
14853 case GAME_CTRL_ID_STOP:
14854 if (game_status == GAME_MODE_MAIN)
14860 RequestQuitGame(TRUE);
14864 case GAME_CTRL_ID_PAUSE:
14865 if (options.network && game_status == GAME_MODE_PLAYING)
14867 #if defined(NETWORK_AVALIABLE)
14869 SendToServer_ContinuePlaying();
14871 SendToServer_PausePlaying();
14875 TapeTogglePause(TAPE_TOGGLE_MANUAL);
14878 case GAME_CTRL_ID_PLAY:
14879 if (game_status == GAME_MODE_MAIN)
14881 StartGameActions(options.network, setup.autorecord, level.random_seed);
14883 else if (tape.pausing)
14885 #if defined(NETWORK_AVALIABLE)
14886 if (options.network)
14887 SendToServer_ContinuePlaying();
14891 tape.pausing = FALSE;
14892 DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF, 0);
14897 case SOUND_CTRL_ID_MUSIC:
14898 if (setup.sound_music)
14900 setup.sound_music = FALSE;
14904 else if (audio.music_available)
14906 setup.sound = setup.sound_music = TRUE;
14908 SetAudioMode(setup.sound);
14914 case SOUND_CTRL_ID_LOOPS:
14915 if (setup.sound_loops)
14916 setup.sound_loops = FALSE;
14917 else if (audio.loops_available)
14919 setup.sound = setup.sound_loops = TRUE;
14921 SetAudioMode(setup.sound);
14925 case SOUND_CTRL_ID_SIMPLE:
14926 if (setup.sound_simple)
14927 setup.sound_simple = FALSE;
14928 else if (audio.sound_available)
14930 setup.sound = setup.sound_simple = TRUE;
14932 SetAudioMode(setup.sound);
14936 case GAME_CTRL_ID_SAVE:
14940 case GAME_CTRL_ID_LOAD:
14949 static void HandleGameButtons(struct GadgetInfo *gi)
14951 HandleGameButtonsExt(gi->custom_id);
14954 void HandleSoundButtonKeys(Key key)
14957 if (key == setup.shortcut.sound_simple)
14958 ClickOnGadget(game_gadget[SOUND_CTRL_ID_SIMPLE], MB_LEFTBUTTON);
14959 else if (key == setup.shortcut.sound_loops)
14960 ClickOnGadget(game_gadget[SOUND_CTRL_ID_LOOPS], MB_LEFTBUTTON);
14961 else if (key == setup.shortcut.sound_music)
14962 ClickOnGadget(game_gadget[SOUND_CTRL_ID_MUSIC], MB_LEFTBUTTON);