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"
26 #define DEBUG_INIT_PLAYER 1
27 #define DEBUG_PLAYER_ACTIONS 0
29 /* EXPERIMENTAL STUFF */
30 #define USE_NEW_AMOEBA_CODE FALSE
32 /* EXPERIMENTAL STUFF */
33 #define USE_QUICKSAND_BD_ROCK_BUGFIX 0
34 #define USE_QUICKSAND_IMPACT_BUGFIX 0
35 #define USE_DELAYED_GFX_REDRAW 0
36 #define USE_NEW_PLAYER_ASSIGNMENTS 1
38 #if USE_DELAYED_GFX_REDRAW
39 #define TEST_DrawLevelField(x, y) \
40 GfxRedraw[x][y] |= GFX_REDRAW_TILE
41 #define TEST_DrawLevelFieldCrumbled(x, y) \
42 GfxRedraw[x][y] |= GFX_REDRAW_TILE_CRUMBLED
43 #define TEST_DrawLevelFieldCrumbledNeighbours(x, y) \
44 GfxRedraw[x][y] |= GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS
45 #define TEST_DrawTwinkleOnField(x, y) \
46 GfxRedraw[x][y] |= GFX_REDRAW_TILE_TWINKLED
48 #define TEST_DrawLevelField(x, y) \
50 #define TEST_DrawLevelFieldCrumbled(x, y) \
51 DrawLevelFieldCrumbled(x, y)
52 #define TEST_DrawLevelFieldCrumbledNeighbours(x, y) \
53 DrawLevelFieldCrumbledNeighbours(x, y)
54 #define TEST_DrawTwinkleOnField(x, y) \
55 DrawTwinkleOnField(x, y)
64 /* for MovePlayer() */
65 #define MP_NO_ACTION 0
68 #define MP_DONT_RUN_INTO (MP_MOVING | MP_ACTION)
70 /* for ScrollPlayer() */
72 #define SCROLL_GO_ON 1
74 /* for Bang()/Explode() */
75 #define EX_PHASE_START 0
76 #define EX_TYPE_NONE 0
77 #define EX_TYPE_NORMAL (1 << 0)
78 #define EX_TYPE_CENTER (1 << 1)
79 #define EX_TYPE_BORDER (1 << 2)
80 #define EX_TYPE_CROSS (1 << 3)
81 #define EX_TYPE_DYNA (1 << 4)
82 #define EX_TYPE_SINGLE_TILE (EX_TYPE_CENTER | EX_TYPE_BORDER)
84 #define PANEL_OFF() (local_player->LevelSolved_PanelOff)
85 #define PANEL_DEACTIVATED(p) ((p)->x < 0 || (p)->y < 0 || PANEL_OFF())
86 #define PANEL_XPOS(p) (DX + ALIGNED_TEXT_XPOS(p))
87 #define PANEL_YPOS(p) (DY + ALIGNED_TEXT_YPOS(p))
89 /* game panel display and control definitions */
90 #define GAME_PANEL_LEVEL_NUMBER 0
91 #define GAME_PANEL_GEMS 1
92 #define GAME_PANEL_INVENTORY_COUNT 2
93 #define GAME_PANEL_INVENTORY_FIRST_1 3
94 #define GAME_PANEL_INVENTORY_FIRST_2 4
95 #define GAME_PANEL_INVENTORY_FIRST_3 5
96 #define GAME_PANEL_INVENTORY_FIRST_4 6
97 #define GAME_PANEL_INVENTORY_FIRST_5 7
98 #define GAME_PANEL_INVENTORY_FIRST_6 8
99 #define GAME_PANEL_INVENTORY_FIRST_7 9
100 #define GAME_PANEL_INVENTORY_FIRST_8 10
101 #define GAME_PANEL_INVENTORY_LAST_1 11
102 #define GAME_PANEL_INVENTORY_LAST_2 12
103 #define GAME_PANEL_INVENTORY_LAST_3 13
104 #define GAME_PANEL_INVENTORY_LAST_4 14
105 #define GAME_PANEL_INVENTORY_LAST_5 15
106 #define GAME_PANEL_INVENTORY_LAST_6 16
107 #define GAME_PANEL_INVENTORY_LAST_7 17
108 #define GAME_PANEL_INVENTORY_LAST_8 18
109 #define GAME_PANEL_KEY_1 19
110 #define GAME_PANEL_KEY_2 20
111 #define GAME_PANEL_KEY_3 21
112 #define GAME_PANEL_KEY_4 22
113 #define GAME_PANEL_KEY_5 23
114 #define GAME_PANEL_KEY_6 24
115 #define GAME_PANEL_KEY_7 25
116 #define GAME_PANEL_KEY_8 26
117 #define GAME_PANEL_KEY_WHITE 27
118 #define GAME_PANEL_KEY_WHITE_COUNT 28
119 #define GAME_PANEL_SCORE 29
120 #define GAME_PANEL_HIGHSCORE 30
121 #define GAME_PANEL_TIME 31
122 #define GAME_PANEL_TIME_HH 32
123 #define GAME_PANEL_TIME_MM 33
124 #define GAME_PANEL_TIME_SS 34
125 #define GAME_PANEL_FRAME 35
126 #define GAME_PANEL_SHIELD_NORMAL 36
127 #define GAME_PANEL_SHIELD_NORMAL_TIME 37
128 #define GAME_PANEL_SHIELD_DEADLY 38
129 #define GAME_PANEL_SHIELD_DEADLY_TIME 39
130 #define GAME_PANEL_EXIT 40
131 #define GAME_PANEL_EMC_MAGIC_BALL 41
132 #define GAME_PANEL_EMC_MAGIC_BALL_SWITCH 42
133 #define GAME_PANEL_LIGHT_SWITCH 43
134 #define GAME_PANEL_LIGHT_SWITCH_TIME 44
135 #define GAME_PANEL_TIMEGATE_SWITCH 45
136 #define GAME_PANEL_TIMEGATE_SWITCH_TIME 46
137 #define GAME_PANEL_SWITCHGATE_SWITCH 47
138 #define GAME_PANEL_EMC_LENSES 48
139 #define GAME_PANEL_EMC_LENSES_TIME 49
140 #define GAME_PANEL_EMC_MAGNIFIER 50
141 #define GAME_PANEL_EMC_MAGNIFIER_TIME 51
142 #define GAME_PANEL_BALLOON_SWITCH 52
143 #define GAME_PANEL_DYNABOMB_NUMBER 53
144 #define GAME_PANEL_DYNABOMB_SIZE 54
145 #define GAME_PANEL_DYNABOMB_POWER 55
146 #define GAME_PANEL_PENGUINS 56
147 #define GAME_PANEL_SOKOBAN_OBJECTS 57
148 #define GAME_PANEL_SOKOBAN_FIELDS 58
149 #define GAME_PANEL_ROBOT_WHEEL 59
150 #define GAME_PANEL_CONVEYOR_BELT_1 60
151 #define GAME_PANEL_CONVEYOR_BELT_2 61
152 #define GAME_PANEL_CONVEYOR_BELT_3 62
153 #define GAME_PANEL_CONVEYOR_BELT_4 63
154 #define GAME_PANEL_CONVEYOR_BELT_1_SWITCH 64
155 #define GAME_PANEL_CONVEYOR_BELT_2_SWITCH 65
156 #define GAME_PANEL_CONVEYOR_BELT_3_SWITCH 66
157 #define GAME_PANEL_CONVEYOR_BELT_4_SWITCH 67
158 #define GAME_PANEL_MAGIC_WALL 68
159 #define GAME_PANEL_MAGIC_WALL_TIME 69
160 #define GAME_PANEL_GRAVITY_STATE 70
161 #define GAME_PANEL_GRAPHIC_1 71
162 #define GAME_PANEL_GRAPHIC_2 72
163 #define GAME_PANEL_GRAPHIC_3 73
164 #define GAME_PANEL_GRAPHIC_4 74
165 #define GAME_PANEL_GRAPHIC_5 75
166 #define GAME_PANEL_GRAPHIC_6 76
167 #define GAME_PANEL_GRAPHIC_7 77
168 #define GAME_PANEL_GRAPHIC_8 78
169 #define GAME_PANEL_ELEMENT_1 79
170 #define GAME_PANEL_ELEMENT_2 80
171 #define GAME_PANEL_ELEMENT_3 81
172 #define GAME_PANEL_ELEMENT_4 82
173 #define GAME_PANEL_ELEMENT_5 83
174 #define GAME_PANEL_ELEMENT_6 84
175 #define GAME_PANEL_ELEMENT_7 85
176 #define GAME_PANEL_ELEMENT_8 86
177 #define GAME_PANEL_ELEMENT_COUNT_1 87
178 #define GAME_PANEL_ELEMENT_COUNT_2 88
179 #define GAME_PANEL_ELEMENT_COUNT_3 89
180 #define GAME_PANEL_ELEMENT_COUNT_4 90
181 #define GAME_PANEL_ELEMENT_COUNT_5 91
182 #define GAME_PANEL_ELEMENT_COUNT_6 92
183 #define GAME_PANEL_ELEMENT_COUNT_7 93
184 #define GAME_PANEL_ELEMENT_COUNT_8 94
185 #define GAME_PANEL_CE_SCORE_1 95
186 #define GAME_PANEL_CE_SCORE_2 96
187 #define GAME_PANEL_CE_SCORE_3 97
188 #define GAME_PANEL_CE_SCORE_4 98
189 #define GAME_PANEL_CE_SCORE_5 99
190 #define GAME_PANEL_CE_SCORE_6 100
191 #define GAME_PANEL_CE_SCORE_7 101
192 #define GAME_PANEL_CE_SCORE_8 102
193 #define GAME_PANEL_CE_SCORE_1_ELEMENT 103
194 #define GAME_PANEL_CE_SCORE_2_ELEMENT 104
195 #define GAME_PANEL_CE_SCORE_3_ELEMENT 105
196 #define GAME_PANEL_CE_SCORE_4_ELEMENT 106
197 #define GAME_PANEL_CE_SCORE_5_ELEMENT 107
198 #define GAME_PANEL_CE_SCORE_6_ELEMENT 108
199 #define GAME_PANEL_CE_SCORE_7_ELEMENT 109
200 #define GAME_PANEL_CE_SCORE_8_ELEMENT 110
201 #define GAME_PANEL_PLAYER_NAME 111
202 #define GAME_PANEL_LEVEL_NAME 112
203 #define GAME_PANEL_LEVEL_AUTHOR 113
205 #define NUM_GAME_PANEL_CONTROLS 114
207 struct GamePanelOrderInfo
213 static struct GamePanelOrderInfo game_panel_order[NUM_GAME_PANEL_CONTROLS];
215 struct GamePanelControlInfo
219 struct TextPosInfo *pos;
222 int value, last_value;
223 int frame, last_frame;
228 static struct GamePanelControlInfo game_panel_controls[] =
231 GAME_PANEL_LEVEL_NUMBER,
232 &game.panel.level_number,
241 GAME_PANEL_INVENTORY_COUNT,
242 &game.panel.inventory_count,
246 GAME_PANEL_INVENTORY_FIRST_1,
247 &game.panel.inventory_first[0],
251 GAME_PANEL_INVENTORY_FIRST_2,
252 &game.panel.inventory_first[1],
256 GAME_PANEL_INVENTORY_FIRST_3,
257 &game.panel.inventory_first[2],
261 GAME_PANEL_INVENTORY_FIRST_4,
262 &game.panel.inventory_first[3],
266 GAME_PANEL_INVENTORY_FIRST_5,
267 &game.panel.inventory_first[4],
271 GAME_PANEL_INVENTORY_FIRST_6,
272 &game.panel.inventory_first[5],
276 GAME_PANEL_INVENTORY_FIRST_7,
277 &game.panel.inventory_first[6],
281 GAME_PANEL_INVENTORY_FIRST_8,
282 &game.panel.inventory_first[7],
286 GAME_PANEL_INVENTORY_LAST_1,
287 &game.panel.inventory_last[0],
291 GAME_PANEL_INVENTORY_LAST_2,
292 &game.panel.inventory_last[1],
296 GAME_PANEL_INVENTORY_LAST_3,
297 &game.panel.inventory_last[2],
301 GAME_PANEL_INVENTORY_LAST_4,
302 &game.panel.inventory_last[3],
306 GAME_PANEL_INVENTORY_LAST_5,
307 &game.panel.inventory_last[4],
311 GAME_PANEL_INVENTORY_LAST_6,
312 &game.panel.inventory_last[5],
316 GAME_PANEL_INVENTORY_LAST_7,
317 &game.panel.inventory_last[6],
321 GAME_PANEL_INVENTORY_LAST_8,
322 &game.panel.inventory_last[7],
366 GAME_PANEL_KEY_WHITE,
367 &game.panel.key_white,
371 GAME_PANEL_KEY_WHITE_COUNT,
372 &game.panel.key_white_count,
381 GAME_PANEL_HIGHSCORE,
382 &game.panel.highscore,
411 GAME_PANEL_SHIELD_NORMAL,
412 &game.panel.shield_normal,
416 GAME_PANEL_SHIELD_NORMAL_TIME,
417 &game.panel.shield_normal_time,
421 GAME_PANEL_SHIELD_DEADLY,
422 &game.panel.shield_deadly,
426 GAME_PANEL_SHIELD_DEADLY_TIME,
427 &game.panel.shield_deadly_time,
436 GAME_PANEL_EMC_MAGIC_BALL,
437 &game.panel.emc_magic_ball,
441 GAME_PANEL_EMC_MAGIC_BALL_SWITCH,
442 &game.panel.emc_magic_ball_switch,
446 GAME_PANEL_LIGHT_SWITCH,
447 &game.panel.light_switch,
451 GAME_PANEL_LIGHT_SWITCH_TIME,
452 &game.panel.light_switch_time,
456 GAME_PANEL_TIMEGATE_SWITCH,
457 &game.panel.timegate_switch,
461 GAME_PANEL_TIMEGATE_SWITCH_TIME,
462 &game.panel.timegate_switch_time,
466 GAME_PANEL_SWITCHGATE_SWITCH,
467 &game.panel.switchgate_switch,
471 GAME_PANEL_EMC_LENSES,
472 &game.panel.emc_lenses,
476 GAME_PANEL_EMC_LENSES_TIME,
477 &game.panel.emc_lenses_time,
481 GAME_PANEL_EMC_MAGNIFIER,
482 &game.panel.emc_magnifier,
486 GAME_PANEL_EMC_MAGNIFIER_TIME,
487 &game.panel.emc_magnifier_time,
491 GAME_PANEL_BALLOON_SWITCH,
492 &game.panel.balloon_switch,
496 GAME_PANEL_DYNABOMB_NUMBER,
497 &game.panel.dynabomb_number,
501 GAME_PANEL_DYNABOMB_SIZE,
502 &game.panel.dynabomb_size,
506 GAME_PANEL_DYNABOMB_POWER,
507 &game.panel.dynabomb_power,
512 &game.panel.penguins,
516 GAME_PANEL_SOKOBAN_OBJECTS,
517 &game.panel.sokoban_objects,
521 GAME_PANEL_SOKOBAN_FIELDS,
522 &game.panel.sokoban_fields,
526 GAME_PANEL_ROBOT_WHEEL,
527 &game.panel.robot_wheel,
531 GAME_PANEL_CONVEYOR_BELT_1,
532 &game.panel.conveyor_belt[0],
536 GAME_PANEL_CONVEYOR_BELT_2,
537 &game.panel.conveyor_belt[1],
541 GAME_PANEL_CONVEYOR_BELT_3,
542 &game.panel.conveyor_belt[2],
546 GAME_PANEL_CONVEYOR_BELT_4,
547 &game.panel.conveyor_belt[3],
551 GAME_PANEL_CONVEYOR_BELT_1_SWITCH,
552 &game.panel.conveyor_belt_switch[0],
556 GAME_PANEL_CONVEYOR_BELT_2_SWITCH,
557 &game.panel.conveyor_belt_switch[1],
561 GAME_PANEL_CONVEYOR_BELT_3_SWITCH,
562 &game.panel.conveyor_belt_switch[2],
566 GAME_PANEL_CONVEYOR_BELT_4_SWITCH,
567 &game.panel.conveyor_belt_switch[3],
571 GAME_PANEL_MAGIC_WALL,
572 &game.panel.magic_wall,
576 GAME_PANEL_MAGIC_WALL_TIME,
577 &game.panel.magic_wall_time,
581 GAME_PANEL_GRAVITY_STATE,
582 &game.panel.gravity_state,
586 GAME_PANEL_GRAPHIC_1,
587 &game.panel.graphic[0],
591 GAME_PANEL_GRAPHIC_2,
592 &game.panel.graphic[1],
596 GAME_PANEL_GRAPHIC_3,
597 &game.panel.graphic[2],
601 GAME_PANEL_GRAPHIC_4,
602 &game.panel.graphic[3],
606 GAME_PANEL_GRAPHIC_5,
607 &game.panel.graphic[4],
611 GAME_PANEL_GRAPHIC_6,
612 &game.panel.graphic[5],
616 GAME_PANEL_GRAPHIC_7,
617 &game.panel.graphic[6],
621 GAME_PANEL_GRAPHIC_8,
622 &game.panel.graphic[7],
626 GAME_PANEL_ELEMENT_1,
627 &game.panel.element[0],
631 GAME_PANEL_ELEMENT_2,
632 &game.panel.element[1],
636 GAME_PANEL_ELEMENT_3,
637 &game.panel.element[2],
641 GAME_PANEL_ELEMENT_4,
642 &game.panel.element[3],
646 GAME_PANEL_ELEMENT_5,
647 &game.panel.element[4],
651 GAME_PANEL_ELEMENT_6,
652 &game.panel.element[5],
656 GAME_PANEL_ELEMENT_7,
657 &game.panel.element[6],
661 GAME_PANEL_ELEMENT_8,
662 &game.panel.element[7],
666 GAME_PANEL_ELEMENT_COUNT_1,
667 &game.panel.element_count[0],
671 GAME_PANEL_ELEMENT_COUNT_2,
672 &game.panel.element_count[1],
676 GAME_PANEL_ELEMENT_COUNT_3,
677 &game.panel.element_count[2],
681 GAME_PANEL_ELEMENT_COUNT_4,
682 &game.panel.element_count[3],
686 GAME_PANEL_ELEMENT_COUNT_5,
687 &game.panel.element_count[4],
691 GAME_PANEL_ELEMENT_COUNT_6,
692 &game.panel.element_count[5],
696 GAME_PANEL_ELEMENT_COUNT_7,
697 &game.panel.element_count[6],
701 GAME_PANEL_ELEMENT_COUNT_8,
702 &game.panel.element_count[7],
706 GAME_PANEL_CE_SCORE_1,
707 &game.panel.ce_score[0],
711 GAME_PANEL_CE_SCORE_2,
712 &game.panel.ce_score[1],
716 GAME_PANEL_CE_SCORE_3,
717 &game.panel.ce_score[2],
721 GAME_PANEL_CE_SCORE_4,
722 &game.panel.ce_score[3],
726 GAME_PANEL_CE_SCORE_5,
727 &game.panel.ce_score[4],
731 GAME_PANEL_CE_SCORE_6,
732 &game.panel.ce_score[5],
736 GAME_PANEL_CE_SCORE_7,
737 &game.panel.ce_score[6],
741 GAME_PANEL_CE_SCORE_8,
742 &game.panel.ce_score[7],
746 GAME_PANEL_CE_SCORE_1_ELEMENT,
747 &game.panel.ce_score_element[0],
751 GAME_PANEL_CE_SCORE_2_ELEMENT,
752 &game.panel.ce_score_element[1],
756 GAME_PANEL_CE_SCORE_3_ELEMENT,
757 &game.panel.ce_score_element[2],
761 GAME_PANEL_CE_SCORE_4_ELEMENT,
762 &game.panel.ce_score_element[3],
766 GAME_PANEL_CE_SCORE_5_ELEMENT,
767 &game.panel.ce_score_element[4],
771 GAME_PANEL_CE_SCORE_6_ELEMENT,
772 &game.panel.ce_score_element[5],
776 GAME_PANEL_CE_SCORE_7_ELEMENT,
777 &game.panel.ce_score_element[6],
781 GAME_PANEL_CE_SCORE_8_ELEMENT,
782 &game.panel.ce_score_element[7],
786 GAME_PANEL_PLAYER_NAME,
787 &game.panel.player_name,
791 GAME_PANEL_LEVEL_NAME,
792 &game.panel.level_name,
796 GAME_PANEL_LEVEL_AUTHOR,
797 &game.panel.level_author,
808 /* values for delayed check of falling and moving elements and for collision */
809 #define CHECK_DELAY_MOVING 3
810 #define CHECK_DELAY_FALLING CHECK_DELAY_MOVING
811 #define CHECK_DELAY_COLLISION 2
812 #define CHECK_DELAY_IMPACT CHECK_DELAY_COLLISION
814 /* values for initial player move delay (initial delay counter value) */
815 #define INITIAL_MOVE_DELAY_OFF -1
816 #define INITIAL_MOVE_DELAY_ON 0
818 /* values for player movement speed (which is in fact a delay value) */
819 #define MOVE_DELAY_MIN_SPEED 32
820 #define MOVE_DELAY_NORMAL_SPEED 8
821 #define MOVE_DELAY_HIGH_SPEED 4
822 #define MOVE_DELAY_MAX_SPEED 1
824 #define DOUBLE_MOVE_DELAY(x) (x = (x < MOVE_DELAY_MIN_SPEED ? x * 2 : x))
825 #define HALVE_MOVE_DELAY(x) (x = (x > MOVE_DELAY_MAX_SPEED ? x / 2 : x))
827 #define DOUBLE_PLAYER_SPEED(p) (HALVE_MOVE_DELAY( (p)->move_delay_value))
828 #define HALVE_PLAYER_SPEED(p) (DOUBLE_MOVE_DELAY((p)->move_delay_value))
830 /* values for other actions */
831 #define MOVE_STEPSIZE_NORMAL (TILEX / MOVE_DELAY_NORMAL_SPEED)
832 #define MOVE_STEPSIZE_MIN (1)
833 #define MOVE_STEPSIZE_MAX (TILEX)
835 #define GET_DX_FROM_DIR(d) ((d) == MV_LEFT ? -1 : (d) == MV_RIGHT ? 1 : 0)
836 #define GET_DY_FROM_DIR(d) ((d) == MV_UP ? -1 : (d) == MV_DOWN ? 1 : 0)
838 #define INIT_GFX_RANDOM() (GetSimpleRandom(1000000))
840 #define GET_NEW_PUSH_DELAY(e) ( (element_info[e].push_delay_fixed) + \
841 RND(element_info[e].push_delay_random))
842 #define GET_NEW_DROP_DELAY(e) ( (element_info[e].drop_delay_fixed) + \
843 RND(element_info[e].drop_delay_random))
844 #define GET_NEW_MOVE_DELAY(e) ( (element_info[e].move_delay_fixed) + \
845 RND(element_info[e].move_delay_random))
846 #define GET_MAX_MOVE_DELAY(e) ( (element_info[e].move_delay_fixed) + \
847 (element_info[e].move_delay_random))
848 #define GET_NEW_CE_VALUE(e) ( (element_info[e].ce_value_fixed_initial) +\
849 RND(element_info[e].ce_value_random_initial))
850 #define GET_CE_SCORE(e) ( (element_info[e].collect_score))
851 #define GET_CHANGE_DELAY(c) ( ((c)->delay_fixed * (c)->delay_frames) + \
852 RND((c)->delay_random * (c)->delay_frames))
853 #define GET_CE_DELAY_VALUE(c) ( ((c)->delay_fixed) + \
854 RND((c)->delay_random))
857 #define GET_VALID_RUNTIME_ELEMENT(e) \
858 ((e) >= NUM_RUNTIME_ELEMENTS ? EL_UNKNOWN : (e))
860 #define RESOLVED_REFERENCE_ELEMENT(be, e) \
861 ((be) + (e) - EL_SELF < EL_CUSTOM_START ? EL_CUSTOM_START : \
862 (be) + (e) - EL_SELF > EL_CUSTOM_END ? EL_CUSTOM_END : \
863 (be) + (e) - EL_SELF)
865 #define GET_PLAYER_FROM_BITS(p) \
866 (EL_PLAYER_1 + ((p) != PLAYER_BITS_ANY ? log_2(p) : 0))
868 #define GET_TARGET_ELEMENT(be, e, ch, cv, cs) \
869 ((e) == EL_TRIGGER_PLAYER ? (ch)->actual_trigger_player : \
870 (e) == EL_TRIGGER_ELEMENT ? (ch)->actual_trigger_element : \
871 (e) == EL_TRIGGER_CE_VALUE ? (ch)->actual_trigger_ce_value : \
872 (e) == EL_TRIGGER_CE_SCORE ? (ch)->actual_trigger_ce_score : \
873 (e) == EL_CURRENT_CE_VALUE ? (cv) : \
874 (e) == EL_CURRENT_CE_SCORE ? (cs) : \
875 (e) >= EL_PREV_CE_8 && (e) <= EL_NEXT_CE_8 ? \
876 RESOLVED_REFERENCE_ELEMENT(be, e) : \
879 #define CAN_GROW_INTO(e) \
880 ((e) == EL_SAND || (IS_DIGGABLE(e) && level.grow_into_diggable))
882 #define ELEMENT_CAN_ENTER_FIELD_BASE_X(x, y, condition) \
883 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
886 #define ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, condition) \
887 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
888 (CAN_MOVE_INTO_ACID(e) && \
889 Feld[x][y] == EL_ACID) || \
892 #define ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, condition) \
893 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) || \
894 (CAN_MOVE_INTO_ACID(e) && \
895 Feld[x][y] == EL_ACID) || \
898 #define ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, condition) \
899 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
901 (CAN_MOVE_INTO_ACID(e) && \
902 Feld[x][y] == EL_ACID) || \
903 (DONT_COLLIDE_WITH(e) && \
905 !PLAYER_ENEMY_PROTECTED(x, y))))
907 #define ELEMENT_CAN_ENTER_FIELD(e, x, y) \
908 ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, 0)
910 #define SATELLITE_CAN_ENTER_FIELD(x, y) \
911 ELEMENT_CAN_ENTER_FIELD_BASE_2(EL_SATELLITE, x, y, 0)
913 #define ANDROID_CAN_ENTER_FIELD(e, x, y) \
914 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, Feld[x][y] == EL_EMC_PLANT)
916 #define ANDROID_CAN_CLONE_FIELD(x, y) \
917 (IN_LEV_FIELD(x, y) && (CAN_BE_CLONED_BY_ANDROID(Feld[x][y]) || \
918 CAN_BE_CLONED_BY_ANDROID(EL_TRIGGER_ELEMENT)))
920 #define ENEMY_CAN_ENTER_FIELD(e, x, y) \
921 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
923 #define YAMYAM_CAN_ENTER_FIELD(e, x, y) \
924 ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, Feld[x][y] == EL_DIAMOND)
926 #define DARK_YAMYAM_CAN_ENTER_FIELD(e, x, y) \
927 ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x,y, IS_FOOD_DARK_YAMYAM(Feld[x][y]))
929 #define PACMAN_CAN_ENTER_FIELD(e, x, y) \
930 ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, IS_AMOEBOID(Feld[x][y]))
932 #define PIG_CAN_ENTER_FIELD(e, x, y) \
933 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, IS_FOOD_PIG(Feld[x][y]))
935 #define PENGUIN_CAN_ENTER_FIELD(e, x, y) \
936 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (Feld[x][y] == EL_EXIT_OPEN || \
937 Feld[x][y] == EL_EM_EXIT_OPEN || \
938 Feld[x][y] == EL_STEEL_EXIT_OPEN || \
939 Feld[x][y] == EL_EM_STEEL_EXIT_OPEN || \
940 IS_FOOD_PENGUIN(Feld[x][y])))
941 #define DRAGON_CAN_ENTER_FIELD(e, x, y) \
942 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
944 #define MOLE_CAN_ENTER_FIELD(e, x, y, condition) \
945 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (condition))
947 #define SPRING_CAN_ENTER_FIELD(e, x, y) \
948 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
950 #define SPRING_CAN_BUMP_FROM_FIELD(x, y) \
951 (IN_LEV_FIELD(x, y) && (Feld[x][y] == EL_EMC_SPRING_BUMPER || \
952 Feld[x][y] == EL_EMC_SPRING_BUMPER_ACTIVE))
954 #define MOVE_ENTER_EL(e) (element_info[e].move_enter_element)
956 #define CE_ENTER_FIELD_COND(e, x, y) \
957 (!IS_PLAYER(x, y) && \
958 IS_EQUAL_OR_IN_GROUP(Feld[x][y], MOVE_ENTER_EL(e)))
960 #define CUSTOM_ELEMENT_CAN_ENTER_FIELD(e, x, y) \
961 ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, CE_ENTER_FIELD_COND(e, x, y))
963 #define IN_LEV_FIELD_AND_IS_FREE(x, y) (IN_LEV_FIELD(x, y) && IS_FREE(x, y))
964 #define IN_LEV_FIELD_AND_NOT_FREE(x, y) (IN_LEV_FIELD(x, y) && !IS_FREE(x, y))
966 #define ACCESS_FROM(e, d) (element_info[e].access_direction &(d))
967 #define IS_WALKABLE_FROM(e, d) (IS_WALKABLE(e) && ACCESS_FROM(e, d))
968 #define IS_PASSABLE_FROM(e, d) (IS_PASSABLE(e) && ACCESS_FROM(e, d))
969 #define IS_ACCESSIBLE_FROM(e, d) (IS_ACCESSIBLE(e) && ACCESS_FROM(e, d))
971 /* game button identifiers */
972 #define GAME_CTRL_ID_STOP 0
973 #define GAME_CTRL_ID_PAUSE 1
974 #define GAME_CTRL_ID_PLAY 2
975 #define GAME_CTRL_ID_UNDO 3
976 #define GAME_CTRL_ID_REDO 4
977 #define GAME_CTRL_ID_SAVE 5
978 #define GAME_CTRL_ID_PAUSE2 6
979 #define GAME_CTRL_ID_LOAD 7
980 #define SOUND_CTRL_ID_MUSIC 8
981 #define SOUND_CTRL_ID_LOOPS 9
982 #define SOUND_CTRL_ID_SIMPLE 10
984 #define NUM_GAME_BUTTONS 11
987 /* forward declaration for internal use */
989 static void CreateField(int, int, int);
991 static void ResetGfxAnimation(int, int);
993 static void SetPlayerWaiting(struct PlayerInfo *, boolean);
994 static void AdvanceFrameAndPlayerCounters(int);
996 static boolean MovePlayerOneStep(struct PlayerInfo *, int, int, int, int);
997 static boolean MovePlayer(struct PlayerInfo *, int, int);
998 static void ScrollPlayer(struct PlayerInfo *, int);
999 static void ScrollScreen(struct PlayerInfo *, int);
1001 static int DigField(struct PlayerInfo *, int, int, int, int, int, int, int);
1002 static boolean DigFieldByCE(int, int, int);
1003 static boolean SnapField(struct PlayerInfo *, int, int);
1004 static boolean DropElement(struct PlayerInfo *);
1006 static void InitBeltMovement(void);
1007 static void CloseAllOpenTimegates(void);
1008 static void CheckGravityMovement(struct PlayerInfo *);
1009 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *);
1010 static void KillPlayerUnlessEnemyProtected(int, int);
1011 static void KillPlayerUnlessExplosionProtected(int, int);
1013 static void TestIfPlayerTouchesCustomElement(int, int);
1014 static void TestIfElementTouchesCustomElement(int, int);
1015 static void TestIfElementHitsCustomElement(int, int, int);
1017 static void HandleElementChange(int, int, int);
1018 static void ExecuteCustomElementAction(int, int, int, int);
1019 static boolean ChangeElement(int, int, int, int);
1021 static boolean CheckTriggeredElementChangeExt(int, int, int, int, int,int,int);
1022 #define CheckTriggeredElementChange(x, y, e, ev) \
1023 CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY,CH_SIDE_ANY, -1)
1024 #define CheckTriggeredElementChangeByPlayer(x, y, e, ev, p, s) \
1025 CheckTriggeredElementChangeExt(x, y, e, ev, p, s, -1)
1026 #define CheckTriggeredElementChangeBySide(x, y, e, ev, s) \
1027 CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, s, -1)
1028 #define CheckTriggeredElementChangeByPage(x, y, e, ev, p) \
1029 CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY, CH_SIDE_ANY, p)
1031 static boolean CheckElementChangeExt(int, int, int, int, int, int, int);
1032 #define CheckElementChange(x, y, e, te, ev) \
1033 CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, CH_SIDE_ANY)
1034 #define CheckElementChangeByPlayer(x, y, e, ev, p, s) \
1035 CheckElementChangeExt(x, y, e, EL_EMPTY, ev, p, s)
1036 #define CheckElementChangeBySide(x, y, e, te, ev, s) \
1037 CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, s)
1039 static void PlayLevelSound(int, int, int);
1040 static void PlayLevelSoundNearest(int, int, int);
1041 static void PlayLevelSoundAction(int, int, int);
1042 static void PlayLevelSoundElementAction(int, int, int, int);
1043 static void PlayLevelSoundElementActionIfLoop(int, int, int, int);
1044 static void PlayLevelSoundActionIfLoop(int, int, int);
1045 static void StopLevelSoundActionIfLoop(int, int, int);
1046 static void PlayLevelMusic();
1048 static void HandleGameButtons(struct GadgetInfo *);
1050 int AmoebeNachbarNr(int, int);
1051 void AmoebeUmwandeln(int, int);
1052 void ContinueMoving(int, int);
1053 void Bang(int, int);
1054 void InitMovDir(int, int);
1055 void InitAmoebaNr(int, int);
1056 int NewHiScore(void);
1058 void TestIfGoodThingHitsBadThing(int, int, int);
1059 void TestIfBadThingHitsGoodThing(int, int, int);
1060 void TestIfPlayerTouchesBadThing(int, int);
1061 void TestIfPlayerRunsIntoBadThing(int, int, int);
1062 void TestIfBadThingTouchesPlayer(int, int);
1063 void TestIfBadThingRunsIntoPlayer(int, int, int);
1064 void TestIfFriendTouchesBadThing(int, int);
1065 void TestIfBadThingTouchesFriend(int, int);
1066 void TestIfBadThingTouchesOtherBadThing(int, int);
1067 void TestIfGoodThingGetsHitByBadThing(int, int, int);
1069 void KillPlayer(struct PlayerInfo *);
1070 void BuryPlayer(struct PlayerInfo *);
1071 void RemovePlayer(struct PlayerInfo *);
1073 static int getInvisibleActiveFromInvisibleElement(int);
1074 static int getInvisibleFromInvisibleActiveElement(int);
1076 static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
1078 /* for detection of endless loops, caused by custom element programming */
1079 /* (using maximal playfield width x 10 is just a rough approximation) */
1080 #define MAX_ELEMENT_CHANGE_RECURSION_DEPTH (MAX_PLAYFIELD_WIDTH * 10)
1082 #define RECURSION_LOOP_DETECTION_START(e, rc) \
1084 if (recursion_loop_detected) \
1087 if (recursion_loop_depth > MAX_ELEMENT_CHANGE_RECURSION_DEPTH) \
1089 recursion_loop_detected = TRUE; \
1090 recursion_loop_element = (e); \
1093 recursion_loop_depth++; \
1096 #define RECURSION_LOOP_DETECTION_END() \
1098 recursion_loop_depth--; \
1101 static int recursion_loop_depth;
1102 static boolean recursion_loop_detected;
1103 static boolean recursion_loop_element;
1105 static int map_player_action[MAX_PLAYERS];
1108 /* ------------------------------------------------------------------------- */
1109 /* definition of elements that automatically change to other elements after */
1110 /* a specified time, eventually calling a function when changing */
1111 /* ------------------------------------------------------------------------- */
1113 /* forward declaration for changer functions */
1114 static void InitBuggyBase(int, int);
1115 static void WarnBuggyBase(int, int);
1117 static void InitTrap(int, int);
1118 static void ActivateTrap(int, int);
1119 static void ChangeActiveTrap(int, int);
1121 static void InitRobotWheel(int, int);
1122 static void RunRobotWheel(int, int);
1123 static void StopRobotWheel(int, int);
1125 static void InitTimegateWheel(int, int);
1126 static void RunTimegateWheel(int, int);
1128 static void InitMagicBallDelay(int, int);
1129 static void ActivateMagicBall(int, int);
1131 struct ChangingElementInfo
1136 void (*pre_change_function)(int x, int y);
1137 void (*change_function)(int x, int y);
1138 void (*post_change_function)(int x, int y);
1141 static struct ChangingElementInfo change_delay_list[] =
1176 EL_STEEL_EXIT_OPENING,
1184 EL_STEEL_EXIT_CLOSING,
1185 EL_STEEL_EXIT_CLOSED,
1208 EL_EM_STEEL_EXIT_OPENING,
1209 EL_EM_STEEL_EXIT_OPEN,
1216 EL_EM_STEEL_EXIT_CLOSING,
1240 EL_SWITCHGATE_OPENING,
1248 EL_SWITCHGATE_CLOSING,
1249 EL_SWITCHGATE_CLOSED,
1256 EL_TIMEGATE_OPENING,
1264 EL_TIMEGATE_CLOSING,
1273 EL_ACID_SPLASH_LEFT,
1281 EL_ACID_SPLASH_RIGHT,
1290 EL_SP_BUGGY_BASE_ACTIVATING,
1297 EL_SP_BUGGY_BASE_ACTIVATING,
1298 EL_SP_BUGGY_BASE_ACTIVE,
1305 EL_SP_BUGGY_BASE_ACTIVE,
1329 EL_ROBOT_WHEEL_ACTIVE,
1337 EL_TIMEGATE_SWITCH_ACTIVE,
1345 EL_DC_TIMEGATE_SWITCH_ACTIVE,
1346 EL_DC_TIMEGATE_SWITCH,
1353 EL_EMC_MAGIC_BALL_ACTIVE,
1354 EL_EMC_MAGIC_BALL_ACTIVE,
1361 EL_EMC_SPRING_BUMPER_ACTIVE,
1362 EL_EMC_SPRING_BUMPER,
1369 EL_DIAGONAL_SHRINKING,
1377 EL_DIAGONAL_GROWING,
1398 int push_delay_fixed, push_delay_random;
1402 { EL_SPRING, 0, 0 },
1403 { EL_BALLOON, 0, 0 },
1405 { EL_SOKOBAN_OBJECT, 2, 0 },
1406 { EL_SOKOBAN_FIELD_FULL, 2, 0 },
1407 { EL_SATELLITE, 2, 0 },
1408 { EL_SP_DISK_YELLOW, 2, 0 },
1410 { EL_UNDEFINED, 0, 0 },
1418 move_stepsize_list[] =
1420 { EL_AMOEBA_DROP, 2 },
1421 { EL_AMOEBA_DROPPING, 2 },
1422 { EL_QUICKSAND_FILLING, 1 },
1423 { EL_QUICKSAND_EMPTYING, 1 },
1424 { EL_QUICKSAND_FAST_FILLING, 2 },
1425 { EL_QUICKSAND_FAST_EMPTYING, 2 },
1426 { EL_MAGIC_WALL_FILLING, 2 },
1427 { EL_MAGIC_WALL_EMPTYING, 2 },
1428 { EL_BD_MAGIC_WALL_FILLING, 2 },
1429 { EL_BD_MAGIC_WALL_EMPTYING, 2 },
1430 { EL_DC_MAGIC_WALL_FILLING, 2 },
1431 { EL_DC_MAGIC_WALL_EMPTYING, 2 },
1433 { EL_UNDEFINED, 0 },
1441 collect_count_list[] =
1444 { EL_BD_DIAMOND, 1 },
1445 { EL_EMERALD_YELLOW, 1 },
1446 { EL_EMERALD_RED, 1 },
1447 { EL_EMERALD_PURPLE, 1 },
1449 { EL_SP_INFOTRON, 1 },
1453 { EL_UNDEFINED, 0 },
1461 access_direction_list[] =
1463 { EL_TUBE_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1464 { EL_TUBE_VERTICAL, MV_UP | MV_DOWN },
1465 { EL_TUBE_HORIZONTAL, MV_LEFT | MV_RIGHT },
1466 { EL_TUBE_VERTICAL_LEFT, MV_LEFT | MV_UP | MV_DOWN },
1467 { EL_TUBE_VERTICAL_RIGHT, MV_RIGHT | MV_UP | MV_DOWN },
1468 { EL_TUBE_HORIZONTAL_UP, MV_LEFT | MV_RIGHT | MV_UP },
1469 { EL_TUBE_HORIZONTAL_DOWN, MV_LEFT | MV_RIGHT | MV_DOWN },
1470 { EL_TUBE_LEFT_UP, MV_LEFT | MV_UP },
1471 { EL_TUBE_LEFT_DOWN, MV_LEFT | MV_DOWN },
1472 { EL_TUBE_RIGHT_UP, MV_RIGHT | MV_UP },
1473 { EL_TUBE_RIGHT_DOWN, MV_RIGHT | MV_DOWN },
1475 { EL_SP_PORT_LEFT, MV_RIGHT },
1476 { EL_SP_PORT_RIGHT, MV_LEFT },
1477 { EL_SP_PORT_UP, MV_DOWN },
1478 { EL_SP_PORT_DOWN, MV_UP },
1479 { EL_SP_PORT_HORIZONTAL, MV_LEFT | MV_RIGHT },
1480 { EL_SP_PORT_VERTICAL, MV_UP | MV_DOWN },
1481 { EL_SP_PORT_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1482 { EL_SP_GRAVITY_PORT_LEFT, MV_RIGHT },
1483 { EL_SP_GRAVITY_PORT_RIGHT, MV_LEFT },
1484 { EL_SP_GRAVITY_PORT_UP, MV_DOWN },
1485 { EL_SP_GRAVITY_PORT_DOWN, MV_UP },
1486 { EL_SP_GRAVITY_ON_PORT_LEFT, MV_RIGHT },
1487 { EL_SP_GRAVITY_ON_PORT_RIGHT, MV_LEFT },
1488 { EL_SP_GRAVITY_ON_PORT_UP, MV_DOWN },
1489 { EL_SP_GRAVITY_ON_PORT_DOWN, MV_UP },
1490 { EL_SP_GRAVITY_OFF_PORT_LEFT, MV_RIGHT },
1491 { EL_SP_GRAVITY_OFF_PORT_RIGHT, MV_LEFT },
1492 { EL_SP_GRAVITY_OFF_PORT_UP, MV_DOWN },
1493 { EL_SP_GRAVITY_OFF_PORT_DOWN, MV_UP },
1495 { EL_UNDEFINED, MV_NONE }
1498 static boolean trigger_events[MAX_NUM_ELEMENTS][NUM_CHANGE_EVENTS];
1500 #define IS_AUTO_CHANGING(e) (element_info[e].has_change_event[CE_DELAY])
1501 #define IS_JUST_CHANGING(x, y) (ChangeDelay[x][y] != 0)
1502 #define IS_CHANGING(x, y) (IS_AUTO_CHANGING(Feld[x][y]) || \
1503 IS_JUST_CHANGING(x, y))
1505 #define CE_PAGE(e, ce) (element_info[e].event_page[ce])
1507 /* static variables for playfield scan mode (scanning forward or backward) */
1508 static int playfield_scan_start_x = 0;
1509 static int playfield_scan_start_y = 0;
1510 static int playfield_scan_delta_x = 1;
1511 static int playfield_scan_delta_y = 1;
1513 #define SCAN_PLAYFIELD(x, y) for ((y) = playfield_scan_start_y; \
1514 (y) >= 0 && (y) <= lev_fieldy - 1; \
1515 (y) += playfield_scan_delta_y) \
1516 for ((x) = playfield_scan_start_x; \
1517 (x) >= 0 && (x) <= lev_fieldx - 1; \
1518 (x) += playfield_scan_delta_x)
1521 void DEBUG_SetMaximumDynamite()
1525 for (i = 0; i < MAX_INVENTORY_SIZE; i++)
1526 if (local_player->inventory_size < MAX_INVENTORY_SIZE)
1527 local_player->inventory_element[local_player->inventory_size++] =
1532 static void InitPlayfieldScanModeVars()
1534 if (game.use_reverse_scan_direction)
1536 playfield_scan_start_x = lev_fieldx - 1;
1537 playfield_scan_start_y = lev_fieldy - 1;
1539 playfield_scan_delta_x = -1;
1540 playfield_scan_delta_y = -1;
1544 playfield_scan_start_x = 0;
1545 playfield_scan_start_y = 0;
1547 playfield_scan_delta_x = 1;
1548 playfield_scan_delta_y = 1;
1552 static void InitPlayfieldScanMode(int mode)
1554 game.use_reverse_scan_direction =
1555 (mode == CA_ARG_SCAN_MODE_REVERSE ? TRUE : FALSE);
1557 InitPlayfieldScanModeVars();
1560 static int get_move_delay_from_stepsize(int move_stepsize)
1563 MIN(MAX(MOVE_STEPSIZE_MIN, move_stepsize), MOVE_STEPSIZE_MAX);
1565 /* make sure that stepsize value is always a power of 2 */
1566 move_stepsize = (1 << log_2(move_stepsize));
1568 return TILEX / move_stepsize;
1571 static void SetPlayerMoveSpeed(struct PlayerInfo *player, int move_stepsize,
1574 int player_nr = player->index_nr;
1575 int move_delay = get_move_delay_from_stepsize(move_stepsize);
1576 boolean cannot_move = (move_stepsize == STEPSIZE_NOT_MOVING ? TRUE : FALSE);
1578 /* do no immediately change move delay -- the player might just be moving */
1579 player->move_delay_value_next = move_delay;
1581 /* information if player can move must be set separately */
1582 player->cannot_move = cannot_move;
1586 player->move_delay = game.initial_move_delay[player_nr];
1587 player->move_delay_value = game.initial_move_delay_value[player_nr];
1589 player->move_delay_value_next = -1;
1591 player->move_delay_reset_counter = 0;
1595 void GetPlayerConfig()
1597 GameFrameDelay = setup.game_frame_delay;
1599 if (!audio.sound_available)
1600 setup.sound_simple = FALSE;
1602 if (!audio.loops_available)
1603 setup.sound_loops = FALSE;
1605 if (!audio.music_available)
1606 setup.sound_music = FALSE;
1608 if (!video.fullscreen_available)
1609 setup.fullscreen = FALSE;
1611 setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
1613 SetAudioMode(setup.sound);
1617 int GetElementFromGroupElement(int element)
1619 if (IS_GROUP_ELEMENT(element))
1621 struct ElementGroupInfo *group = element_info[element].group;
1622 int last_anim_random_frame = gfx.anim_random_frame;
1625 if (group->choice_mode == ANIM_RANDOM)
1626 gfx.anim_random_frame = RND(group->num_elements_resolved);
1628 element_pos = getAnimationFrame(group->num_elements_resolved, 1,
1629 group->choice_mode, 0,
1632 if (group->choice_mode == ANIM_RANDOM)
1633 gfx.anim_random_frame = last_anim_random_frame;
1635 group->choice_pos++;
1637 element = group->element_resolved[element_pos];
1643 static void InitPlayerField(int x, int y, int element, boolean init_game)
1645 if (element == EL_SP_MURPHY)
1649 if (stored_player[0].present)
1651 Feld[x][y] = EL_SP_MURPHY_CLONE;
1657 stored_player[0].initial_element = element;
1658 stored_player[0].use_murphy = TRUE;
1660 if (!level.use_artwork_element[0])
1661 stored_player[0].artwork_element = EL_SP_MURPHY;
1664 Feld[x][y] = EL_PLAYER_1;
1670 struct PlayerInfo *player = &stored_player[Feld[x][y] - EL_PLAYER_1];
1671 int jx = player->jx, jy = player->jy;
1673 player->present = TRUE;
1675 player->block_last_field = (element == EL_SP_MURPHY ?
1676 level.sp_block_last_field :
1677 level.block_last_field);
1679 /* ---------- initialize player's last field block delay --------------- */
1681 /* always start with reliable default value (no adjustment needed) */
1682 player->block_delay_adjustment = 0;
1684 /* special case 1: in Supaplex, Murphy blocks last field one more frame */
1685 if (player->block_last_field && element == EL_SP_MURPHY)
1686 player->block_delay_adjustment = 1;
1688 /* special case 2: in game engines before 3.1.1, blocking was different */
1689 if (game.use_block_last_field_bug)
1690 player->block_delay_adjustment = (player->block_last_field ? -1 : 1);
1692 if (!options.network || player->connected)
1694 player->active = TRUE;
1696 /* remove potentially duplicate players */
1697 if (StorePlayer[jx][jy] == Feld[x][y])
1698 StorePlayer[jx][jy] = 0;
1700 StorePlayer[x][y] = Feld[x][y];
1702 #if DEBUG_INIT_PLAYER
1705 printf("- player element %d activated", player->element_nr);
1706 printf(" (local player is %d and currently %s)\n",
1707 local_player->element_nr,
1708 local_player->active ? "active" : "not active");
1713 Feld[x][y] = EL_EMPTY;
1715 player->jx = player->last_jx = x;
1716 player->jy = player->last_jy = y;
1721 int player_nr = GET_PLAYER_NR(element);
1722 struct PlayerInfo *player = &stored_player[player_nr];
1724 if (player->active && player->killed)
1725 player->reanimated = TRUE; /* if player was just killed, reanimate him */
1729 static void InitField(int x, int y, boolean init_game)
1731 int element = Feld[x][y];
1740 InitPlayerField(x, y, element, init_game);
1743 case EL_SOKOBAN_FIELD_PLAYER:
1744 element = Feld[x][y] = EL_PLAYER_1;
1745 InitField(x, y, init_game);
1747 element = Feld[x][y] = EL_SOKOBAN_FIELD_EMPTY;
1748 InitField(x, y, init_game);
1751 case EL_SOKOBAN_FIELD_EMPTY:
1752 local_player->sokobanfields_still_needed++;
1756 if (x < lev_fieldx-1 && Feld[x+1][y] == EL_ACID)
1757 Feld[x][y] = EL_ACID_POOL_TOPLEFT;
1758 else if (x > 0 && Feld[x-1][y] == EL_ACID)
1759 Feld[x][y] = EL_ACID_POOL_TOPRIGHT;
1760 else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPLEFT)
1761 Feld[x][y] = EL_ACID_POOL_BOTTOMLEFT;
1762 else if (y > 0 && Feld[x][y-1] == EL_ACID)
1763 Feld[x][y] = EL_ACID_POOL_BOTTOM;
1764 else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPRIGHT)
1765 Feld[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
1774 case EL_SPACESHIP_RIGHT:
1775 case EL_SPACESHIP_UP:
1776 case EL_SPACESHIP_LEFT:
1777 case EL_SPACESHIP_DOWN:
1778 case EL_BD_BUTTERFLY:
1779 case EL_BD_BUTTERFLY_RIGHT:
1780 case EL_BD_BUTTERFLY_UP:
1781 case EL_BD_BUTTERFLY_LEFT:
1782 case EL_BD_BUTTERFLY_DOWN:
1784 case EL_BD_FIREFLY_RIGHT:
1785 case EL_BD_FIREFLY_UP:
1786 case EL_BD_FIREFLY_LEFT:
1787 case EL_BD_FIREFLY_DOWN:
1788 case EL_PACMAN_RIGHT:
1790 case EL_PACMAN_LEFT:
1791 case EL_PACMAN_DOWN:
1793 case EL_YAMYAM_LEFT:
1794 case EL_YAMYAM_RIGHT:
1796 case EL_YAMYAM_DOWN:
1797 case EL_DARK_YAMYAM:
1800 case EL_SP_SNIKSNAK:
1801 case EL_SP_ELECTRON:
1810 case EL_AMOEBA_FULL:
1815 case EL_AMOEBA_DROP:
1816 if (y == lev_fieldy - 1)
1818 Feld[x][y] = EL_AMOEBA_GROWING;
1819 Store[x][y] = EL_AMOEBA_WET;
1823 case EL_DYNAMITE_ACTIVE:
1824 case EL_SP_DISK_RED_ACTIVE:
1825 case EL_DYNABOMB_PLAYER_1_ACTIVE:
1826 case EL_DYNABOMB_PLAYER_2_ACTIVE:
1827 case EL_DYNABOMB_PLAYER_3_ACTIVE:
1828 case EL_DYNABOMB_PLAYER_4_ACTIVE:
1829 MovDelay[x][y] = 96;
1832 case EL_EM_DYNAMITE_ACTIVE:
1833 MovDelay[x][y] = 32;
1837 local_player->lights_still_needed++;
1841 local_player->friends_still_needed++;
1846 GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
1849 case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
1850 case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
1851 case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
1852 case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
1853 case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
1854 case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
1855 case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
1856 case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
1857 case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
1858 case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
1859 case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
1860 case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
1863 int belt_nr = getBeltNrFromBeltSwitchElement(Feld[x][y]);
1864 int belt_dir = getBeltDirFromBeltSwitchElement(Feld[x][y]);
1865 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Feld[x][y]);
1867 if (game.belt_dir_nr[belt_nr] == 3) /* initial value */
1869 game.belt_dir[belt_nr] = belt_dir;
1870 game.belt_dir_nr[belt_nr] = belt_dir_nr;
1872 else /* more than one switch -- set it like the first switch */
1874 Feld[x][y] = Feld[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
1879 case EL_LIGHT_SWITCH_ACTIVE:
1881 game.light_time_left = level.time_light * FRAMES_PER_SECOND;
1884 case EL_INVISIBLE_STEELWALL:
1885 case EL_INVISIBLE_WALL:
1886 case EL_INVISIBLE_SAND:
1887 if (game.light_time_left > 0 ||
1888 game.lenses_time_left > 0)
1889 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
1892 case EL_EMC_MAGIC_BALL:
1893 if (game.ball_state)
1894 Feld[x][y] = EL_EMC_MAGIC_BALL_ACTIVE;
1897 case EL_EMC_MAGIC_BALL_SWITCH:
1898 if (game.ball_state)
1899 Feld[x][y] = EL_EMC_MAGIC_BALL_SWITCH_ACTIVE;
1902 case EL_TRIGGER_PLAYER:
1903 case EL_TRIGGER_ELEMENT:
1904 case EL_TRIGGER_CE_VALUE:
1905 case EL_TRIGGER_CE_SCORE:
1907 case EL_ANY_ELEMENT:
1908 case EL_CURRENT_CE_VALUE:
1909 case EL_CURRENT_CE_SCORE:
1926 /* reference elements should not be used on the playfield */
1927 Feld[x][y] = EL_EMPTY;
1931 if (IS_CUSTOM_ELEMENT(element))
1933 if (CAN_MOVE(element))
1936 if (!element_info[element].use_last_ce_value || init_game)
1937 CustomValue[x][y] = GET_NEW_CE_VALUE(Feld[x][y]);
1939 else if (IS_GROUP_ELEMENT(element))
1941 Feld[x][y] = GetElementFromGroupElement(element);
1943 InitField(x, y, init_game);
1950 CheckTriggeredElementChange(x, y, element, CE_CREATION_OF_X);
1953 inline static void InitField_WithBug1(int x, int y, boolean init_game)
1955 InitField(x, y, init_game);
1957 /* not needed to call InitMovDir() -- already done by InitField()! */
1958 if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1959 CAN_MOVE(Feld[x][y]))
1963 inline static void InitField_WithBug2(int x, int y, boolean init_game)
1965 int old_element = Feld[x][y];
1967 InitField(x, y, init_game);
1969 /* not needed to call InitMovDir() -- already done by InitField()! */
1970 if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1971 CAN_MOVE(old_element) &&
1972 (old_element < EL_MOLE_LEFT || old_element > EL_MOLE_DOWN))
1975 /* this case is in fact a combination of not less than three bugs:
1976 first, it calls InitMovDir() for elements that can move, although this is
1977 already done by InitField(); then, it checks the element that was at this
1978 field _before_ the call to InitField() (which can change it); lastly, it
1979 was not called for "mole with direction" elements, which were treated as
1980 "cannot move" due to (fixed) wrong element initialization in "src/init.c"
1984 static int get_key_element_from_nr(int key_nr)
1986 int key_base_element = (key_nr >= STD_NUM_KEYS ? EL_EMC_KEY_5 - STD_NUM_KEYS :
1987 level.game_engine_type == GAME_ENGINE_TYPE_EM ?
1988 EL_EM_KEY_1 : EL_KEY_1);
1990 return key_base_element + key_nr;
1993 static int get_next_dropped_element(struct PlayerInfo *player)
1995 return (player->inventory_size > 0 ?
1996 player->inventory_element[player->inventory_size - 1] :
1997 player->inventory_infinite_element != EL_UNDEFINED ?
1998 player->inventory_infinite_element :
1999 player->dynabombs_left > 0 ?
2000 EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
2004 static int get_inventory_element_from_pos(struct PlayerInfo *player, int pos)
2006 /* pos >= 0: get element from bottom of the stack;
2007 pos < 0: get element from top of the stack */
2011 int min_inventory_size = -pos;
2012 int inventory_pos = player->inventory_size - min_inventory_size;
2013 int min_dynabombs_left = min_inventory_size - player->inventory_size;
2015 return (player->inventory_size >= min_inventory_size ?
2016 player->inventory_element[inventory_pos] :
2017 player->inventory_infinite_element != EL_UNDEFINED ?
2018 player->inventory_infinite_element :
2019 player->dynabombs_left >= min_dynabombs_left ?
2020 EL_DYNABOMB_PLAYER_1 + player->index_nr :
2025 int min_dynabombs_left = pos + 1;
2026 int min_inventory_size = pos + 1 - player->dynabombs_left;
2027 int inventory_pos = pos - player->dynabombs_left;
2029 return (player->inventory_infinite_element != EL_UNDEFINED ?
2030 player->inventory_infinite_element :
2031 player->dynabombs_left >= min_dynabombs_left ?
2032 EL_DYNABOMB_PLAYER_1 + player->index_nr :
2033 player->inventory_size >= min_inventory_size ?
2034 player->inventory_element[inventory_pos] :
2039 static int compareGamePanelOrderInfo(const void *object1, const void *object2)
2041 const struct GamePanelOrderInfo *gpo1 = (struct GamePanelOrderInfo *)object1;
2042 const struct GamePanelOrderInfo *gpo2 = (struct GamePanelOrderInfo *)object2;
2045 if (gpo1->sort_priority != gpo2->sort_priority)
2046 compare_result = gpo1->sort_priority - gpo2->sort_priority;
2048 compare_result = gpo1->nr - gpo2->nr;
2050 return compare_result;
2053 void InitGameControlValues()
2057 for (i = 0; game_panel_controls[i].nr != -1; i++)
2059 struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2060 struct GamePanelOrderInfo *gpo = &game_panel_order[i];
2061 struct TextPosInfo *pos = gpc->pos;
2063 int type = gpc->type;
2067 Error(ERR_INFO, "'game_panel_controls' structure corrupted at %d", i);
2068 Error(ERR_EXIT, "this should not happen -- please debug");
2071 /* force update of game controls after initialization */
2072 gpc->value = gpc->last_value = -1;
2073 gpc->frame = gpc->last_frame = -1;
2074 gpc->gfx_frame = -1;
2076 /* determine panel value width for later calculation of alignment */
2077 if (type == TYPE_INTEGER || type == TYPE_STRING)
2079 pos->width = pos->size * getFontWidth(pos->font);
2080 pos->height = getFontHeight(pos->font);
2082 else if (type == TYPE_ELEMENT)
2084 pos->width = pos->size;
2085 pos->height = pos->size;
2088 /* fill structure for game panel draw order */
2090 gpo->sort_priority = pos->sort_priority;
2093 /* sort game panel controls according to sort_priority and control number */
2094 qsort(game_panel_order, NUM_GAME_PANEL_CONTROLS,
2095 sizeof(struct GamePanelOrderInfo), compareGamePanelOrderInfo);
2098 void UpdatePlayfieldElementCount()
2100 boolean use_element_count = FALSE;
2103 /* first check if it is needed at all to calculate playfield element count */
2104 for (i = GAME_PANEL_ELEMENT_COUNT_1; i <= GAME_PANEL_ELEMENT_COUNT_8; i++)
2105 if (!PANEL_DEACTIVATED(game_panel_controls[i].pos))
2106 use_element_count = TRUE;
2108 if (!use_element_count)
2111 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2112 element_info[i].element_count = 0;
2114 SCAN_PLAYFIELD(x, y)
2116 element_info[Feld[x][y]].element_count++;
2119 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
2120 for (j = 0; j < MAX_NUM_ELEMENTS; j++)
2121 if (IS_IN_GROUP(j, i))
2122 element_info[EL_GROUP_START + i].element_count +=
2123 element_info[j].element_count;
2126 void UpdateGameControlValues()
2129 int time = (local_player->LevelSolved ?
2130 local_player->LevelSolved_CountingTime :
2131 level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2132 level.native_em_level->lev->time :
2133 level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2134 level.native_sp_level->game_sp->time_played :
2135 game.no_time_limit ? TimePlayed : TimeLeft);
2136 int score = (local_player->LevelSolved ?
2137 local_player->LevelSolved_CountingScore :
2138 level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2139 level.native_em_level->lev->score :
2140 level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2141 level.native_sp_level->game_sp->score :
2142 local_player->score);
2143 int gems = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2144 level.native_em_level->lev->required :
2145 level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2146 level.native_sp_level->game_sp->infotrons_still_needed :
2147 local_player->gems_still_needed);
2148 int exit_closed = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2149 level.native_em_level->lev->required > 0 :
2150 level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2151 level.native_sp_level->game_sp->infotrons_still_needed > 0 :
2152 local_player->gems_still_needed > 0 ||
2153 local_player->sokobanfields_still_needed > 0 ||
2154 local_player->lights_still_needed > 0);
2156 UpdatePlayfieldElementCount();
2158 /* update game panel control values */
2160 game_panel_controls[GAME_PANEL_LEVEL_NUMBER].value = level_nr;
2161 game_panel_controls[GAME_PANEL_GEMS].value = gems;
2163 game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value = 0;
2164 for (i = 0; i < MAX_NUM_KEYS; i++)
2165 game_panel_controls[GAME_PANEL_KEY_1 + i].value = EL_EMPTY;
2166 game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_EMPTY;
2167 game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value = 0;
2169 if (game.centered_player_nr == -1)
2171 for (i = 0; i < MAX_PLAYERS; i++)
2173 /* only one player in Supaplex game engine */
2174 if (level.game_engine_type == GAME_ENGINE_TYPE_SP && i > 0)
2177 for (k = 0; k < MAX_NUM_KEYS; k++)
2179 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2181 if (level.native_em_level->ply[i]->keys & (1 << k))
2182 game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2183 get_key_element_from_nr(k);
2185 else if (stored_player[i].key[k])
2186 game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2187 get_key_element_from_nr(k);
2190 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2191 game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2192 level.native_em_level->ply[i]->dynamite;
2193 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
2194 game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2195 level.native_sp_level->game_sp->red_disk_count;
2197 game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2198 stored_player[i].inventory_size;
2200 if (stored_player[i].num_white_keys > 0)
2201 game_panel_controls[GAME_PANEL_KEY_WHITE].value =
2204 game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2205 stored_player[i].num_white_keys;
2210 int player_nr = game.centered_player_nr;
2212 for (k = 0; k < MAX_NUM_KEYS; k++)
2214 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2216 if (level.native_em_level->ply[player_nr]->keys & (1 << k))
2217 game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2218 get_key_element_from_nr(k);
2220 else if (stored_player[player_nr].key[k])
2221 game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2222 get_key_element_from_nr(k);
2225 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2226 game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2227 level.native_em_level->ply[player_nr]->dynamite;
2228 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
2229 game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2230 level.native_sp_level->game_sp->red_disk_count;
2232 game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2233 stored_player[player_nr].inventory_size;
2235 if (stored_player[player_nr].num_white_keys > 0)
2236 game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_DC_KEY_WHITE;
2238 game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2239 stored_player[player_nr].num_white_keys;
2242 for (i = 0; i < NUM_PANEL_INVENTORY; i++)
2244 game_panel_controls[GAME_PANEL_INVENTORY_FIRST_1 + i].value =
2245 get_inventory_element_from_pos(local_player, i);
2246 game_panel_controls[GAME_PANEL_INVENTORY_LAST_1 + i].value =
2247 get_inventory_element_from_pos(local_player, -i - 1);
2250 game_panel_controls[GAME_PANEL_SCORE].value = score;
2251 game_panel_controls[GAME_PANEL_HIGHSCORE].value = highscore[0].Score;
2253 game_panel_controls[GAME_PANEL_TIME].value = time;
2255 game_panel_controls[GAME_PANEL_TIME_HH].value = time / 3600;
2256 game_panel_controls[GAME_PANEL_TIME_MM].value = (time / 60) % 60;
2257 game_panel_controls[GAME_PANEL_TIME_SS].value = time % 60;
2259 game_panel_controls[GAME_PANEL_FRAME].value = FrameCounter;
2261 game_panel_controls[GAME_PANEL_SHIELD_NORMAL].value =
2262 (local_player->shield_normal_time_left > 0 ? EL_SHIELD_NORMAL_ACTIVE :
2264 game_panel_controls[GAME_PANEL_SHIELD_NORMAL_TIME].value =
2265 local_player->shield_normal_time_left;
2266 game_panel_controls[GAME_PANEL_SHIELD_DEADLY].value =
2267 (local_player->shield_deadly_time_left > 0 ? EL_SHIELD_DEADLY_ACTIVE :
2269 game_panel_controls[GAME_PANEL_SHIELD_DEADLY_TIME].value =
2270 local_player->shield_deadly_time_left;
2272 game_panel_controls[GAME_PANEL_EXIT].value =
2273 (exit_closed ? EL_EXIT_CLOSED : EL_EXIT_OPEN);
2275 game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL].value =
2276 (game.ball_state ? EL_EMC_MAGIC_BALL_ACTIVE : EL_EMC_MAGIC_BALL);
2277 game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL_SWITCH].value =
2278 (game.ball_state ? EL_EMC_MAGIC_BALL_SWITCH_ACTIVE :
2279 EL_EMC_MAGIC_BALL_SWITCH);
2281 game_panel_controls[GAME_PANEL_LIGHT_SWITCH].value =
2282 (game.light_time_left > 0 ? EL_LIGHT_SWITCH_ACTIVE : EL_LIGHT_SWITCH);
2283 game_panel_controls[GAME_PANEL_LIGHT_SWITCH_TIME].value =
2284 game.light_time_left;
2286 game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH].value =
2287 (game.timegate_time_left > 0 ? EL_TIMEGATE_OPEN : EL_TIMEGATE_CLOSED);
2288 game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH_TIME].value =
2289 game.timegate_time_left;
2291 game_panel_controls[GAME_PANEL_SWITCHGATE_SWITCH].value =
2292 EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
2294 game_panel_controls[GAME_PANEL_EMC_LENSES].value =
2295 (game.lenses_time_left > 0 ? EL_EMC_LENSES : EL_EMPTY);
2296 game_panel_controls[GAME_PANEL_EMC_LENSES_TIME].value =
2297 game.lenses_time_left;
2299 game_panel_controls[GAME_PANEL_EMC_MAGNIFIER].value =
2300 (game.magnify_time_left > 0 ? EL_EMC_MAGNIFIER : EL_EMPTY);
2301 game_panel_controls[GAME_PANEL_EMC_MAGNIFIER_TIME].value =
2302 game.magnify_time_left;
2304 game_panel_controls[GAME_PANEL_BALLOON_SWITCH].value =
2305 (game.wind_direction == MV_LEFT ? EL_BALLOON_SWITCH_LEFT :
2306 game.wind_direction == MV_RIGHT ? EL_BALLOON_SWITCH_RIGHT :
2307 game.wind_direction == MV_UP ? EL_BALLOON_SWITCH_UP :
2308 game.wind_direction == MV_DOWN ? EL_BALLOON_SWITCH_DOWN :
2309 EL_BALLOON_SWITCH_NONE);
2311 game_panel_controls[GAME_PANEL_DYNABOMB_NUMBER].value =
2312 local_player->dynabomb_count;
2313 game_panel_controls[GAME_PANEL_DYNABOMB_SIZE].value =
2314 local_player->dynabomb_size;
2315 game_panel_controls[GAME_PANEL_DYNABOMB_POWER].value =
2316 (local_player->dynabomb_xl ? EL_DYNABOMB_INCREASE_POWER : EL_EMPTY);
2318 game_panel_controls[GAME_PANEL_PENGUINS].value =
2319 local_player->friends_still_needed;
2321 game_panel_controls[GAME_PANEL_SOKOBAN_OBJECTS].value =
2322 local_player->sokobanfields_still_needed;
2323 game_panel_controls[GAME_PANEL_SOKOBAN_FIELDS].value =
2324 local_player->sokobanfields_still_needed;
2326 game_panel_controls[GAME_PANEL_ROBOT_WHEEL].value =
2327 (game.robot_wheel_active ? EL_ROBOT_WHEEL_ACTIVE : EL_ROBOT_WHEEL);
2329 for (i = 0; i < NUM_BELTS; i++)
2331 game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1 + i].value =
2332 (game.belt_dir[i] != MV_NONE ? EL_CONVEYOR_BELT_1_MIDDLE_ACTIVE :
2333 EL_CONVEYOR_BELT_1_MIDDLE) + i;
2334 game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1_SWITCH + i].value =
2335 getBeltSwitchElementFromBeltNrAndBeltDir(i, game.belt_dir[i]);
2338 game_panel_controls[GAME_PANEL_MAGIC_WALL].value =
2339 (game.magic_wall_active ? EL_MAGIC_WALL_ACTIVE : EL_MAGIC_WALL);
2340 game_panel_controls[GAME_PANEL_MAGIC_WALL_TIME].value =
2341 game.magic_wall_time_left;
2343 game_panel_controls[GAME_PANEL_GRAVITY_STATE].value =
2344 local_player->gravity;
2346 for (i = 0; i < NUM_PANEL_GRAPHICS; i++)
2347 game_panel_controls[GAME_PANEL_GRAPHIC_1 + i].value = EL_GRAPHIC_1 + i;
2349 for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2350 game_panel_controls[GAME_PANEL_ELEMENT_1 + i].value =
2351 (IS_DRAWABLE_ELEMENT(game.panel.element[i].id) ?
2352 game.panel.element[i].id : EL_UNDEFINED);
2354 for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2355 game_panel_controls[GAME_PANEL_ELEMENT_COUNT_1 + i].value =
2356 (IS_VALID_ELEMENT(game.panel.element_count[i].id) ?
2357 element_info[game.panel.element_count[i].id].element_count : 0);
2359 for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2360 game_panel_controls[GAME_PANEL_CE_SCORE_1 + i].value =
2361 (IS_CUSTOM_ELEMENT(game.panel.ce_score[i].id) ?
2362 element_info[game.panel.ce_score[i].id].collect_score : 0);
2364 for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2365 game_panel_controls[GAME_PANEL_CE_SCORE_1_ELEMENT + i].value =
2366 (IS_CUSTOM_ELEMENT(game.panel.ce_score_element[i].id) ?
2367 element_info[game.panel.ce_score_element[i].id].collect_score :
2370 game_panel_controls[GAME_PANEL_PLAYER_NAME].value = 0;
2371 game_panel_controls[GAME_PANEL_LEVEL_NAME].value = 0;
2372 game_panel_controls[GAME_PANEL_LEVEL_AUTHOR].value = 0;
2374 /* update game panel control frames */
2376 for (i = 0; game_panel_controls[i].nr != -1; i++)
2378 struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2380 if (gpc->type == TYPE_ELEMENT)
2382 if (gpc->value != EL_UNDEFINED && gpc->value != EL_EMPTY)
2384 int last_anim_random_frame = gfx.anim_random_frame;
2385 int element = gpc->value;
2386 int graphic = el2panelimg(element);
2388 if (gpc->value != gpc->last_value)
2391 gpc->gfx_random = INIT_GFX_RANDOM();
2397 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2398 IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2399 gpc->gfx_random = INIT_GFX_RANDOM();
2402 if (ANIM_MODE(graphic) == ANIM_RANDOM)
2403 gfx.anim_random_frame = gpc->gfx_random;
2405 if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
2406 gpc->gfx_frame = element_info[element].collect_score;
2408 gpc->frame = getGraphicAnimationFrame(el2panelimg(gpc->value),
2411 if (ANIM_MODE(graphic) == ANIM_RANDOM)
2412 gfx.anim_random_frame = last_anim_random_frame;
2418 void DisplayGameControlValues()
2420 boolean redraw_panel = FALSE;
2423 for (i = 0; game_panel_controls[i].nr != -1; i++)
2425 struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2427 if (PANEL_DEACTIVATED(gpc->pos))
2430 if (gpc->value == gpc->last_value &&
2431 gpc->frame == gpc->last_frame)
2434 redraw_panel = TRUE;
2440 /* copy default game door content to main double buffer */
2442 /* !!! CHECK AGAIN !!! */
2443 SetPanelBackground();
2444 // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
2445 DrawBackground(DX, DY, DXSIZE, DYSIZE);
2447 /* redraw game control buttons */
2448 RedrawGameButtons();
2450 SetGameStatus(GAME_MODE_PSEUDO_PANEL);
2452 for (i = 0; i < NUM_GAME_PANEL_CONTROLS; i++)
2454 int nr = game_panel_order[i].nr;
2455 struct GamePanelControlInfo *gpc = &game_panel_controls[nr];
2456 struct TextPosInfo *pos = gpc->pos;
2457 int type = gpc->type;
2458 int value = gpc->value;
2459 int frame = gpc->frame;
2460 int size = pos->size;
2461 int font = pos->font;
2462 boolean draw_masked = pos->draw_masked;
2463 int mask_mode = (draw_masked ? BLIT_MASKED : BLIT_OPAQUE);
2465 if (PANEL_DEACTIVATED(pos))
2468 gpc->last_value = value;
2469 gpc->last_frame = frame;
2471 if (type == TYPE_INTEGER)
2473 if (nr == GAME_PANEL_LEVEL_NUMBER ||
2474 nr == GAME_PANEL_TIME)
2476 boolean use_dynamic_size = (size == -1 ? TRUE : FALSE);
2478 if (use_dynamic_size) /* use dynamic number of digits */
2480 int value_change = (nr == GAME_PANEL_LEVEL_NUMBER ? 100 : 1000);
2481 int size1 = (nr == GAME_PANEL_LEVEL_NUMBER ? 2 : 3);
2482 int size2 = size1 + 1;
2483 int font1 = pos->font;
2484 int font2 = pos->font_alt;
2486 size = (value < value_change ? size1 : size2);
2487 font = (value < value_change ? font1 : font2);
2491 /* correct text size if "digits" is zero or less */
2493 size = strlen(int2str(value, size));
2495 /* dynamically correct text alignment */
2496 pos->width = size * getFontWidth(font);
2498 DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2499 int2str(value, size), font, mask_mode);
2501 else if (type == TYPE_ELEMENT)
2503 int element, graphic;
2507 int dst_x = PANEL_XPOS(pos);
2508 int dst_y = PANEL_YPOS(pos);
2510 if (value != EL_UNDEFINED && value != EL_EMPTY)
2513 graphic = el2panelimg(value);
2515 // printf("::: %d, '%s' [%d]\n", element, EL_NAME(element), size);
2517 if (element >= EL_GRAPHIC_1 && element <= EL_GRAPHIC_8 && size == 0)
2520 getSizedGraphicSource(graphic, frame, size, &src_bitmap,
2523 width = graphic_info[graphic].width * size / TILESIZE;
2524 height = graphic_info[graphic].height * size / TILESIZE;
2527 BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2530 BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2534 else if (type == TYPE_STRING)
2536 boolean active = (value != 0);
2537 char *state_normal = "off";
2538 char *state_active = "on";
2539 char *state = (active ? state_active : state_normal);
2540 char *s = (nr == GAME_PANEL_GRAVITY_STATE ? state :
2541 nr == GAME_PANEL_PLAYER_NAME ? setup.player_name :
2542 nr == GAME_PANEL_LEVEL_NAME ? level.name :
2543 nr == GAME_PANEL_LEVEL_AUTHOR ? level.author : NULL);
2545 if (nr == GAME_PANEL_GRAVITY_STATE)
2547 int font1 = pos->font; /* (used for normal state) */
2548 int font2 = pos->font_alt; /* (used for active state) */
2550 font = (active ? font2 : font1);
2559 /* don't truncate output if "chars" is zero or less */
2562 /* dynamically correct text alignment */
2563 pos->width = size * getFontWidth(font);
2566 s_cut = getStringCopyN(s, size);
2568 DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2569 s_cut, font, mask_mode);
2575 redraw_mask |= REDRAW_DOOR_1;
2578 SetGameStatus(GAME_MODE_PLAYING);
2581 void UpdateAndDisplayGameControlValues()
2583 if (tape.deactivate_display)
2586 UpdateGameControlValues();
2587 DisplayGameControlValues();
2590 void UpdateGameDoorValues()
2592 UpdateGameControlValues();
2595 void DrawGameDoorValues()
2597 DisplayGameControlValues();
2602 =============================================================================
2604 -----------------------------------------------------------------------------
2605 initialize game engine due to level / tape version number
2606 =============================================================================
2609 static void InitGameEngine()
2611 int i, j, k, l, x, y;
2613 /* set game engine from tape file when re-playing, else from level file */
2614 game.engine_version = (tape.playing ? tape.engine_version :
2615 level.game_version);
2617 /* set single or multi-player game mode (needed for re-playing tapes) */
2618 game.team_mode = setup.team_mode;
2622 int num_players = 0;
2624 for (i = 0; i < MAX_PLAYERS; i++)
2625 if (tape.player_participates[i])
2628 /* multi-player tapes contain input data for more than one player */
2629 game.team_mode = (num_players > 1);
2632 /* ---------------------------------------------------------------------- */
2633 /* set flags for bugs and changes according to active game engine version */
2634 /* ---------------------------------------------------------------------- */
2637 Summary of bugfix/change:
2638 Fixed handling for custom elements that change when pushed by the player.
2640 Fixed/changed in version:
2644 Before 3.1.0, custom elements that "change when pushing" changed directly
2645 after the player started pushing them (until then handled in "DigField()").
2646 Since 3.1.0, these custom elements are not changed until the "pushing"
2647 move of the element is finished (now handled in "ContinueMoving()").
2649 Affected levels/tapes:
2650 The first condition is generally needed for all levels/tapes before version
2651 3.1.0, which might use the old behaviour before it was changed; known tapes
2652 that are affected are some tapes from the level set "Walpurgis Gardens" by
2654 The second condition is an exception from the above case and is needed for
2655 the special case of tapes recorded with game (not engine!) version 3.1.0 or
2656 above (including some development versions of 3.1.0), but before it was
2657 known that this change would break tapes like the above and was fixed in
2658 3.1.1, so that the changed behaviour was active although the engine version
2659 while recording maybe was before 3.1.0. There is at least one tape that is
2660 affected by this exception, which is the tape for the one-level set "Bug
2661 Machine" by Juergen Bonhagen.
2664 game.use_change_when_pushing_bug =
2665 (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2667 tape.game_version >= VERSION_IDENT(3,1,0,0) &&
2668 tape.game_version < VERSION_IDENT(3,1,1,0)));
2671 Summary of bugfix/change:
2672 Fixed handling for blocking the field the player leaves when moving.
2674 Fixed/changed in version:
2678 Before 3.1.1, when "block last field when moving" was enabled, the field
2679 the player is leaving when moving was blocked for the time of the move,
2680 and was directly unblocked afterwards. This resulted in the last field
2681 being blocked for exactly one less than the number of frames of one player
2682 move. Additionally, even when blocking was disabled, the last field was
2683 blocked for exactly one frame.
2684 Since 3.1.1, due to changes in player movement handling, the last field
2685 is not blocked at all when blocking is disabled. When blocking is enabled,
2686 the last field is blocked for exactly the number of frames of one player
2687 move. Additionally, if the player is Murphy, the hero of Supaplex, the
2688 last field is blocked for exactly one more than the number of frames of
2691 Affected levels/tapes:
2692 (!!! yet to be determined -- probably many !!!)
2695 game.use_block_last_field_bug =
2696 (game.engine_version < VERSION_IDENT(3,1,1,0));
2698 /* ---------------------------------------------------------------------- */
2700 /* set maximal allowed number of custom element changes per game frame */
2701 game.max_num_changes_per_frame = 1;
2703 /* default scan direction: scan playfield from top/left to bottom/right */
2704 InitPlayfieldScanMode(CA_ARG_SCAN_MODE_NORMAL);
2706 /* dynamically adjust element properties according to game engine version */
2707 InitElementPropertiesEngine(game.engine_version);
2710 printf("level %d: level version == %06d\n", level_nr, level.game_version);
2711 printf(" tape version == %06d [%s] [file: %06d]\n",
2712 tape.engine_version, (tape.playing ? "PLAYING" : "RECORDING"),
2714 printf(" => game.engine_version == %06d\n", game.engine_version);
2717 /* ---------- initialize player's initial move delay --------------------- */
2719 /* dynamically adjust player properties according to level information */
2720 for (i = 0; i < MAX_PLAYERS; i++)
2721 game.initial_move_delay_value[i] =
2722 get_move_delay_from_stepsize(level.initial_player_stepsize[i]);
2724 /* dynamically adjust player properties according to game engine version */
2725 for (i = 0; i < MAX_PLAYERS; i++)
2726 game.initial_move_delay[i] =
2727 (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
2728 game.initial_move_delay_value[i] : 0);
2730 /* ---------- initialize player's initial push delay --------------------- */
2732 /* dynamically adjust player properties according to game engine version */
2733 game.initial_push_delay_value =
2734 (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
2736 /* ---------- initialize changing elements ------------------------------- */
2738 /* initialize changing elements information */
2739 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2741 struct ElementInfo *ei = &element_info[i];
2743 /* this pointer might have been changed in the level editor */
2744 ei->change = &ei->change_page[0];
2746 if (!IS_CUSTOM_ELEMENT(i))
2748 ei->change->target_element = EL_EMPTY_SPACE;
2749 ei->change->delay_fixed = 0;
2750 ei->change->delay_random = 0;
2751 ei->change->delay_frames = 1;
2754 for (j = 0; j < NUM_CHANGE_EVENTS; j++)
2756 ei->has_change_event[j] = FALSE;
2758 ei->event_page_nr[j] = 0;
2759 ei->event_page[j] = &ei->change_page[0];
2763 /* add changing elements from pre-defined list */
2764 for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
2766 struct ChangingElementInfo *ch_delay = &change_delay_list[i];
2767 struct ElementInfo *ei = &element_info[ch_delay->element];
2769 ei->change->target_element = ch_delay->target_element;
2770 ei->change->delay_fixed = ch_delay->change_delay;
2772 ei->change->pre_change_function = ch_delay->pre_change_function;
2773 ei->change->change_function = ch_delay->change_function;
2774 ei->change->post_change_function = ch_delay->post_change_function;
2776 ei->change->can_change = TRUE;
2777 ei->change->can_change_or_has_action = TRUE;
2779 ei->has_change_event[CE_DELAY] = TRUE;
2781 SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
2782 SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE_OR_HAS_ACTION, TRUE);
2785 /* ---------- initialize internal run-time variables --------------------- */
2787 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2789 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
2791 for (j = 0; j < ei->num_change_pages; j++)
2793 ei->change_page[j].can_change_or_has_action =
2794 (ei->change_page[j].can_change |
2795 ei->change_page[j].has_action);
2799 /* add change events from custom element configuration */
2800 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2802 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
2804 for (j = 0; j < ei->num_change_pages; j++)
2806 if (!ei->change_page[j].can_change_or_has_action)
2809 for (k = 0; k < NUM_CHANGE_EVENTS; k++)
2811 /* only add event page for the first page found with this event */
2812 if (ei->change_page[j].has_event[k] && !(ei->has_change_event[k]))
2814 ei->has_change_event[k] = TRUE;
2816 ei->event_page_nr[k] = j;
2817 ei->event_page[k] = &ei->change_page[j];
2823 /* ---------- initialize reference elements in change conditions --------- */
2825 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2827 int element = EL_CUSTOM_START + i;
2828 struct ElementInfo *ei = &element_info[element];
2830 for (j = 0; j < ei->num_change_pages; j++)
2832 int trigger_element = ei->change_page[j].initial_trigger_element;
2834 if (trigger_element >= EL_PREV_CE_8 &&
2835 trigger_element <= EL_NEXT_CE_8)
2836 trigger_element = RESOLVED_REFERENCE_ELEMENT(element, trigger_element);
2838 ei->change_page[j].trigger_element = trigger_element;
2842 /* ---------- initialize run-time trigger player and element ------------- */
2844 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2846 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
2848 for (j = 0; j < ei->num_change_pages; j++)
2850 ei->change_page[j].actual_trigger_element = EL_EMPTY;
2851 ei->change_page[j].actual_trigger_player = EL_EMPTY;
2852 ei->change_page[j].actual_trigger_player_bits = CH_PLAYER_NONE;
2853 ei->change_page[j].actual_trigger_side = CH_SIDE_NONE;
2854 ei->change_page[j].actual_trigger_ce_value = 0;
2855 ei->change_page[j].actual_trigger_ce_score = 0;
2859 /* ---------- initialize trigger events ---------------------------------- */
2861 /* initialize trigger events information */
2862 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2863 for (j = 0; j < NUM_CHANGE_EVENTS; j++)
2864 trigger_events[i][j] = FALSE;
2866 /* add trigger events from element change event properties */
2867 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2869 struct ElementInfo *ei = &element_info[i];
2871 for (j = 0; j < ei->num_change_pages; j++)
2873 if (!ei->change_page[j].can_change_or_has_action)
2876 if (ei->change_page[j].has_event[CE_BY_OTHER_ACTION])
2878 int trigger_element = ei->change_page[j].trigger_element;
2880 for (k = 0; k < NUM_CHANGE_EVENTS; k++)
2882 if (ei->change_page[j].has_event[k])
2884 if (IS_GROUP_ELEMENT(trigger_element))
2886 struct ElementGroupInfo *group =
2887 element_info[trigger_element].group;
2889 for (l = 0; l < group->num_elements_resolved; l++)
2890 trigger_events[group->element_resolved[l]][k] = TRUE;
2892 else if (trigger_element == EL_ANY_ELEMENT)
2893 for (l = 0; l < MAX_NUM_ELEMENTS; l++)
2894 trigger_events[l][k] = TRUE;
2896 trigger_events[trigger_element][k] = TRUE;
2903 /* ---------- initialize push delay -------------------------------------- */
2905 /* initialize push delay values to default */
2906 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2908 if (!IS_CUSTOM_ELEMENT(i))
2910 /* set default push delay values (corrected since version 3.0.7-1) */
2911 if (game.engine_version < VERSION_IDENT(3,0,7,1))
2913 element_info[i].push_delay_fixed = 2;
2914 element_info[i].push_delay_random = 8;
2918 element_info[i].push_delay_fixed = 8;
2919 element_info[i].push_delay_random = 8;
2924 /* set push delay value for certain elements from pre-defined list */
2925 for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
2927 int e = push_delay_list[i].element;
2929 element_info[e].push_delay_fixed = push_delay_list[i].push_delay_fixed;
2930 element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
2933 /* set push delay value for Supaplex elements for newer engine versions */
2934 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
2936 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2938 if (IS_SP_ELEMENT(i))
2940 /* set SP push delay to just enough to push under a falling zonk */
2941 int delay = (game.engine_version >= VERSION_IDENT(3,1,1,0) ? 8 : 6);
2943 element_info[i].push_delay_fixed = delay;
2944 element_info[i].push_delay_random = 0;
2949 /* ---------- initialize move stepsize ----------------------------------- */
2951 /* initialize move stepsize values to default */
2952 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2953 if (!IS_CUSTOM_ELEMENT(i))
2954 element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
2956 /* set move stepsize value for certain elements from pre-defined list */
2957 for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
2959 int e = move_stepsize_list[i].element;
2961 element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
2964 /* ---------- initialize collect score ----------------------------------- */
2966 /* initialize collect score values for custom elements from initial value */
2967 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2968 if (IS_CUSTOM_ELEMENT(i))
2969 element_info[i].collect_score = element_info[i].collect_score_initial;
2971 /* ---------- initialize collect count ----------------------------------- */
2973 /* initialize collect count values for non-custom elements */
2974 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2975 if (!IS_CUSTOM_ELEMENT(i))
2976 element_info[i].collect_count_initial = 0;
2978 /* add collect count values for all elements from pre-defined list */
2979 for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
2980 element_info[collect_count_list[i].element].collect_count_initial =
2981 collect_count_list[i].count;
2983 /* ---------- initialize access direction -------------------------------- */
2985 /* initialize access direction values to default (access from every side) */
2986 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2987 if (!IS_CUSTOM_ELEMENT(i))
2988 element_info[i].access_direction = MV_ALL_DIRECTIONS;
2990 /* set access direction value for certain elements from pre-defined list */
2991 for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
2992 element_info[access_direction_list[i].element].access_direction =
2993 access_direction_list[i].direction;
2995 /* ---------- initialize explosion content ------------------------------- */
2996 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2998 if (IS_CUSTOM_ELEMENT(i))
3001 for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
3003 /* (content for EL_YAMYAM set at run-time with game.yamyam_content_nr) */
3005 element_info[i].content.e[x][y] =
3006 (i == EL_PLAYER_1 ? EL_EMERALD_YELLOW :
3007 i == EL_PLAYER_2 ? EL_EMERALD_RED :
3008 i == EL_PLAYER_3 ? EL_EMERALD :
3009 i == EL_PLAYER_4 ? EL_EMERALD_PURPLE :
3010 i == EL_MOLE ? EL_EMERALD_RED :
3011 i == EL_PENGUIN ? EL_EMERALD_PURPLE :
3012 i == EL_BUG ? (x == 1 && y == 1 ? EL_DIAMOND : EL_EMERALD) :
3013 i == EL_BD_BUTTERFLY ? EL_BD_DIAMOND :
3014 i == EL_SP_ELECTRON ? EL_SP_INFOTRON :
3015 i == EL_AMOEBA_TO_DIAMOND ? level.amoeba_content :
3016 i == EL_WALL_EMERALD ? EL_EMERALD :
3017 i == EL_WALL_DIAMOND ? EL_DIAMOND :
3018 i == EL_WALL_BD_DIAMOND ? EL_BD_DIAMOND :
3019 i == EL_WALL_EMERALD_YELLOW ? EL_EMERALD_YELLOW :
3020 i == EL_WALL_EMERALD_RED ? EL_EMERALD_RED :
3021 i == EL_WALL_EMERALD_PURPLE ? EL_EMERALD_PURPLE :
3022 i == EL_WALL_PEARL ? EL_PEARL :
3023 i == EL_WALL_CRYSTAL ? EL_CRYSTAL :
3028 /* ---------- initialize recursion detection ------------------------------ */
3029 recursion_loop_depth = 0;
3030 recursion_loop_detected = FALSE;
3031 recursion_loop_element = EL_UNDEFINED;
3033 /* ---------- initialize graphics engine ---------------------------------- */
3034 game.scroll_delay_value =
3035 (game.forced_scroll_delay_value != -1 ? game.forced_scroll_delay_value :
3036 setup.scroll_delay ? setup.scroll_delay_value : 0);
3037 game.scroll_delay_value =
3038 MIN(MAX(MIN_SCROLL_DELAY, game.scroll_delay_value), MAX_SCROLL_DELAY);
3040 /* ---------- initialize game engine snapshots ---------------------------- */
3041 for (i = 0; i < MAX_PLAYERS; i++)
3042 game.snapshot.last_action[i] = 0;
3043 game.snapshot.changed_action = FALSE;
3044 game.snapshot.collected_item = FALSE;
3045 game.snapshot.mode =
3046 (strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_STEP) ?
3047 SNAPSHOT_MODE_EVERY_STEP :
3048 strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_MOVE) ?
3049 SNAPSHOT_MODE_EVERY_MOVE :
3050 strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_COLLECT) ?
3051 SNAPSHOT_MODE_EVERY_COLLECT : SNAPSHOT_MODE_OFF);
3052 game.snapshot.save_snapshot = FALSE;
3055 int get_num_special_action(int element, int action_first, int action_last)
3057 int num_special_action = 0;
3060 for (i = action_first; i <= action_last; i++)
3062 boolean found = FALSE;
3064 for (j = 0; j < NUM_DIRECTIONS; j++)
3065 if (el_act_dir2img(element, i, j) !=
3066 el_act_dir2img(element, ACTION_DEFAULT, j))
3070 num_special_action++;
3075 return num_special_action;
3080 =============================================================================
3082 -----------------------------------------------------------------------------
3083 initialize and start new game
3084 =============================================================================
3089 int full_lev_fieldx = lev_fieldx + (BorderElement != EL_EMPTY ? 2 : 0);
3090 int full_lev_fieldy = lev_fieldy + (BorderElement != EL_EMPTY ? 2 : 0);
3091 int fade_mask = REDRAW_FIELD;
3093 boolean emulate_bd = TRUE; /* unless non-BOULDERDASH elements found */
3094 boolean emulate_sb = TRUE; /* unless non-SOKOBAN elements found */
3095 boolean emulate_sp = TRUE; /* unless non-SUPAPLEX elements found */
3096 int initial_move_dir = MV_DOWN;
3099 // required here to update video display before fading (FIX THIS)
3100 DrawMaskedBorder(REDRAW_DOOR_2);
3102 if (!game.restart_level)
3103 CloseDoor(DOOR_CLOSE_1);
3105 SetGameStatus(GAME_MODE_PLAYING);
3107 if (level_editor_test_game)
3108 FadeSkipNextFadeIn();
3110 FadeSetEnterScreen();
3112 if (CheckIfGlobalBorderHasChanged())
3113 fade_mask = REDRAW_ALL;
3115 FadeSoundsAndMusic();
3117 ExpireSoundLoops(TRUE);
3121 /* needed if different viewport properties defined for playing */
3122 ChangeViewportPropertiesIfNeeded();
3126 OpenDoor(GetDoorState() | DOOR_NO_DELAY | DOOR_FORCE_REDRAW);
3128 DrawCompleteVideoDisplay();
3131 InitGameControlValues();
3133 /* don't play tapes over network */
3134 network_playing = (options.network && !tape.playing);
3136 for (i = 0; i < MAX_PLAYERS; i++)
3138 struct PlayerInfo *player = &stored_player[i];
3140 player->index_nr = i;
3141 player->index_bit = (1 << i);
3142 player->element_nr = EL_PLAYER_1 + i;
3144 player->present = FALSE;
3145 player->active = FALSE;
3146 player->mapped = FALSE;
3148 player->killed = FALSE;
3149 player->reanimated = FALSE;
3152 player->effective_action = 0;
3153 player->programmed_action = 0;
3156 player->score_final = 0;
3158 player->gems_still_needed = level.gems_needed;
3159 player->sokobanfields_still_needed = 0;
3160 player->lights_still_needed = 0;
3161 player->friends_still_needed = 0;
3163 for (j = 0; j < MAX_NUM_KEYS; j++)
3164 player->key[j] = FALSE;
3166 player->num_white_keys = 0;
3168 player->dynabomb_count = 0;
3169 player->dynabomb_size = 1;
3170 player->dynabombs_left = 0;
3171 player->dynabomb_xl = FALSE;
3173 player->MovDir = initial_move_dir;
3176 player->GfxDir = initial_move_dir;
3177 player->GfxAction = ACTION_DEFAULT;
3179 player->StepFrame = 0;
3181 player->initial_element = player->element_nr;
3182 player->artwork_element =
3183 (level.use_artwork_element[i] ? level.artwork_element[i] :
3184 player->element_nr);
3185 player->use_murphy = FALSE;
3187 player->block_last_field = FALSE; /* initialized in InitPlayerField() */
3188 player->block_delay_adjustment = 0; /* initialized in InitPlayerField() */
3190 player->gravity = level.initial_player_gravity[i];
3192 player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
3194 player->actual_frame_counter = 0;
3196 player->step_counter = 0;
3198 player->last_move_dir = initial_move_dir;
3200 player->is_active = FALSE;
3202 player->is_waiting = FALSE;
3203 player->is_moving = FALSE;
3204 player->is_auto_moving = FALSE;
3205 player->is_digging = FALSE;
3206 player->is_snapping = FALSE;
3207 player->is_collecting = FALSE;
3208 player->is_pushing = FALSE;
3209 player->is_switching = FALSE;
3210 player->is_dropping = FALSE;
3211 player->is_dropping_pressed = FALSE;
3213 player->is_bored = FALSE;
3214 player->is_sleeping = FALSE;
3216 player->was_waiting = TRUE;
3217 player->was_moving = FALSE;
3218 player->was_snapping = FALSE;
3219 player->was_dropping = FALSE;
3221 player->frame_counter_bored = -1;
3222 player->frame_counter_sleeping = -1;
3224 player->anim_delay_counter = 0;
3225 player->post_delay_counter = 0;
3227 player->dir_waiting = initial_move_dir;
3228 player->action_waiting = ACTION_DEFAULT;
3229 player->last_action_waiting = ACTION_DEFAULT;
3230 player->special_action_bored = ACTION_DEFAULT;
3231 player->special_action_sleeping = ACTION_DEFAULT;
3233 player->switch_x = -1;
3234 player->switch_y = -1;
3236 player->drop_x = -1;
3237 player->drop_y = -1;
3239 player->show_envelope = 0;
3241 SetPlayerMoveSpeed(player, level.initial_player_stepsize[i], TRUE);
3243 player->push_delay = -1; /* initialized when pushing starts */
3244 player->push_delay_value = game.initial_push_delay_value;
3246 player->drop_delay = 0;
3247 player->drop_pressed_delay = 0;
3249 player->last_jx = -1;
3250 player->last_jy = -1;
3254 player->shield_normal_time_left = 0;
3255 player->shield_deadly_time_left = 0;
3257 player->inventory_infinite_element = EL_UNDEFINED;
3258 player->inventory_size = 0;
3260 if (level.use_initial_inventory[i])
3262 for (j = 0; j < level.initial_inventory_size[i]; j++)
3264 int element = level.initial_inventory_content[i][j];
3265 int collect_count = element_info[element].collect_count_initial;
3268 if (!IS_CUSTOM_ELEMENT(element))
3271 if (collect_count == 0)
3272 player->inventory_infinite_element = element;
3274 for (k = 0; k < collect_count; k++)
3275 if (player->inventory_size < MAX_INVENTORY_SIZE)
3276 player->inventory_element[player->inventory_size++] = element;
3280 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
3281 SnapField(player, 0, 0);
3283 player->LevelSolved = FALSE;
3284 player->GameOver = FALSE;
3286 player->LevelSolved_GameWon = FALSE;
3287 player->LevelSolved_GameEnd = FALSE;
3288 player->LevelSolved_PanelOff = FALSE;
3289 player->LevelSolved_SaveTape = FALSE;
3290 player->LevelSolved_SaveScore = FALSE;
3291 player->LevelSolved_CountingTime = 0;
3292 player->LevelSolved_CountingScore = 0;
3294 map_player_action[i] = i;
3297 network_player_action_received = FALSE;
3299 #if defined(NETWORK_AVALIABLE)
3300 /* initial null action */
3301 if (network_playing)
3302 SendToServer_MovePlayer(MV_NONE);
3311 TimeLeft = level.time;
3314 ScreenMovDir = MV_NONE;
3318 ScrollStepSize = 0; /* will be correctly initialized by ScrollScreen() */
3320 AllPlayersGone = FALSE;
3322 game.no_time_limit = (level.time == 0);
3324 game.yamyam_content_nr = 0;
3325 game.robot_wheel_active = FALSE;
3326 game.magic_wall_active = FALSE;
3327 game.magic_wall_time_left = 0;
3328 game.light_time_left = 0;
3329 game.timegate_time_left = 0;
3330 game.switchgate_pos = 0;
3331 game.wind_direction = level.wind_direction_initial;
3333 game.lenses_time_left = 0;
3334 game.magnify_time_left = 0;
3336 game.ball_state = level.ball_state_initial;
3337 game.ball_content_nr = 0;
3339 game.envelope_active = FALSE;
3341 /* set focus to local player for network games, else to all players */
3342 game.centered_player_nr = (network_playing ? local_player->index_nr : -1);
3343 game.centered_player_nr_next = game.centered_player_nr;
3344 game.set_centered_player = FALSE;
3346 if (network_playing && tape.recording)
3348 /* store client dependent player focus when recording network games */
3349 tape.centered_player_nr_next = game.centered_player_nr_next;
3350 tape.set_centered_player = TRUE;
3353 for (i = 0; i < NUM_BELTS; i++)
3355 game.belt_dir[i] = MV_NONE;
3356 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
3359 for (i = 0; i < MAX_NUM_AMOEBA; i++)
3360 AmoebaCnt[i] = AmoebaCnt2[i] = 0;
3362 #if DEBUG_INIT_PLAYER
3365 printf("Player status at level initialization:\n");
3369 SCAN_PLAYFIELD(x, y)
3371 Feld[x][y] = level.field[x][y];
3372 MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
3373 ChangeDelay[x][y] = 0;
3374 ChangePage[x][y] = -1;
3375 CustomValue[x][y] = 0; /* initialized in InitField() */
3376 Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
3378 WasJustMoving[x][y] = 0;
3379 WasJustFalling[x][y] = 0;
3380 CheckCollision[x][y] = 0;
3381 CheckImpact[x][y] = 0;
3383 Pushed[x][y] = FALSE;
3385 ChangeCount[x][y] = 0;
3386 ChangeEvent[x][y] = -1;
3388 ExplodePhase[x][y] = 0;
3389 ExplodeDelay[x][y] = 0;
3390 ExplodeField[x][y] = EX_TYPE_NONE;
3392 RunnerVisit[x][y] = 0;
3393 PlayerVisit[x][y] = 0;
3396 GfxRandom[x][y] = INIT_GFX_RANDOM();
3397 GfxElement[x][y] = EL_UNDEFINED;
3398 GfxAction[x][y] = ACTION_DEFAULT;
3399 GfxDir[x][y] = MV_NONE;
3400 GfxRedraw[x][y] = GFX_REDRAW_NONE;
3403 SCAN_PLAYFIELD(x, y)
3405 if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
3407 if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
3409 if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
3412 InitField(x, y, TRUE);
3414 ResetGfxAnimation(x, y);
3419 for (i = 0; i < MAX_PLAYERS; i++)
3421 struct PlayerInfo *player = &stored_player[i];
3423 /* set number of special actions for bored and sleeping animation */
3424 player->num_special_action_bored =
3425 get_num_special_action(player->artwork_element,
3426 ACTION_BORING_1, ACTION_BORING_LAST);
3427 player->num_special_action_sleeping =
3428 get_num_special_action(player->artwork_element,
3429 ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
3432 game.emulation = (emulate_bd ? EMU_BOULDERDASH :
3433 emulate_sb ? EMU_SOKOBAN :
3434 emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
3436 /* initialize type of slippery elements */
3437 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3439 if (!IS_CUSTOM_ELEMENT(i))
3441 /* default: elements slip down either to the left or right randomly */
3442 element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
3444 /* SP style elements prefer to slip down on the left side */
3445 if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
3446 element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3448 /* BD style elements prefer to slip down on the left side */
3449 if (game.emulation == EMU_BOULDERDASH)
3450 element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3454 /* initialize explosion and ignition delay */
3455 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3457 if (!IS_CUSTOM_ELEMENT(i))
3460 int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
3461 game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
3462 game.emulation == EMU_SUPAPLEX ? 3 : 2);
3463 int last_phase = (num_phase + 1) * delay;
3464 int half_phase = (num_phase / 2) * delay;
3466 element_info[i].explosion_delay = last_phase - 1;
3467 element_info[i].ignition_delay = half_phase;
3469 if (i == EL_BLACK_ORB)
3470 element_info[i].ignition_delay = 1;
3474 /* correct non-moving belts to start moving left */
3475 for (i = 0; i < NUM_BELTS; i++)
3476 if (game.belt_dir[i] == MV_NONE)
3477 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
3479 #if USE_NEW_PLAYER_ASSIGNMENTS
3480 /* !!! SAME AS init.c:InitPlayerInfo() -- FIX THIS !!! */
3481 /* choose default local player */
3482 local_player = &stored_player[0];
3484 for (i = 0; i < MAX_PLAYERS; i++)
3485 stored_player[i].connected = FALSE;
3487 local_player->connected = TRUE;
3488 /* !!! SAME AS init.c:InitPlayerInfo() -- FIX THIS !!! */
3492 for (i = 0; i < MAX_PLAYERS; i++)
3493 stored_player[i].connected = tape.player_participates[i];
3495 else if (game.team_mode && !options.network)
3497 /* try to guess locally connected team mode players (needed for correct
3498 assignment of player figures from level to locally playing players) */
3500 for (i = 0; i < MAX_PLAYERS; i++)
3501 if (setup.input[i].use_joystick ||
3502 setup.input[i].key.left != KSYM_UNDEFINED)
3503 stored_player[i].connected = TRUE;
3506 #if DEBUG_INIT_PLAYER
3509 printf("Player status after level initialization:\n");
3511 for (i = 0; i < MAX_PLAYERS; i++)
3513 struct PlayerInfo *player = &stored_player[i];
3515 printf("- player %d: present == %d, connected == %d, active == %d",
3521 if (local_player == player)
3522 printf(" (local player)");
3529 #if DEBUG_INIT_PLAYER
3531 printf("Reassigning players ...\n");
3534 /* check if any connected player was not found in playfield */
3535 for (i = 0; i < MAX_PLAYERS; i++)
3537 struct PlayerInfo *player = &stored_player[i];
3539 if (player->connected && !player->present)
3541 struct PlayerInfo *field_player = NULL;
3543 #if DEBUG_INIT_PLAYER
3545 printf("- looking for field player for player %d ...\n", i + 1);
3548 /* assign first free player found that is present in the playfield */
3550 /* first try: look for unmapped playfield player that is not connected */
3551 for (j = 0; j < MAX_PLAYERS; j++)
3552 if (field_player == NULL &&
3553 stored_player[j].present &&
3554 !stored_player[j].mapped &&
3555 !stored_player[j].connected)
3556 field_player = &stored_player[j];
3558 /* second try: look for *any* unmapped playfield player */
3559 for (j = 0; j < MAX_PLAYERS; j++)
3560 if (field_player == NULL &&
3561 stored_player[j].present &&
3562 !stored_player[j].mapped)
3563 field_player = &stored_player[j];
3565 if (field_player != NULL)
3567 int jx = field_player->jx, jy = field_player->jy;
3569 #if DEBUG_INIT_PLAYER
3571 printf("- found player %d\n", field_player->index_nr + 1);
3574 player->present = FALSE;
3575 player->active = FALSE;
3577 field_player->present = TRUE;
3578 field_player->active = TRUE;
3581 player->initial_element = field_player->initial_element;
3582 player->artwork_element = field_player->artwork_element;
3584 player->block_last_field = field_player->block_last_field;
3585 player->block_delay_adjustment = field_player->block_delay_adjustment;
3588 StorePlayer[jx][jy] = field_player->element_nr;
3590 field_player->jx = field_player->last_jx = jx;
3591 field_player->jy = field_player->last_jy = jy;
3593 if (local_player == player)
3594 local_player = field_player;
3596 map_player_action[field_player->index_nr] = i;
3598 field_player->mapped = TRUE;
3600 #if DEBUG_INIT_PLAYER
3602 printf("- map_player_action[%d] == %d\n",
3603 field_player->index_nr + 1, i + 1);
3608 if (player->connected && player->present)
3609 player->mapped = TRUE;
3612 #if DEBUG_INIT_PLAYER
3615 printf("Player status after player assignment (first stage):\n");
3617 for (i = 0; i < MAX_PLAYERS; i++)
3619 struct PlayerInfo *player = &stored_player[i];
3621 printf("- player %d: present == %d, connected == %d, active == %d",
3627 if (local_player == player)
3628 printf(" (local player)");
3637 /* check if any connected player was not found in playfield */
3638 for (i = 0; i < MAX_PLAYERS; i++)
3640 struct PlayerInfo *player = &stored_player[i];
3642 if (player->connected && !player->present)
3644 for (j = 0; j < MAX_PLAYERS; j++)
3646 struct PlayerInfo *field_player = &stored_player[j];
3647 int jx = field_player->jx, jy = field_player->jy;
3649 /* assign first free player found that is present in the playfield */
3650 if (field_player->present && !field_player->connected)
3652 player->present = TRUE;
3653 player->active = TRUE;
3655 field_player->present = FALSE;
3656 field_player->active = FALSE;
3658 player->initial_element = field_player->initial_element;
3659 player->artwork_element = field_player->artwork_element;
3661 player->block_last_field = field_player->block_last_field;
3662 player->block_delay_adjustment = field_player->block_delay_adjustment;
3664 StorePlayer[jx][jy] = player->element_nr;
3666 player->jx = player->last_jx = jx;
3667 player->jy = player->last_jy = jy;
3677 printf("::: local_player->present == %d\n", local_player->present);
3682 /* when playing a tape, eliminate all players who do not participate */
3684 #if USE_NEW_PLAYER_ASSIGNMENTS
3686 if (!game.team_mode)
3688 for (i = 0; i < MAX_PLAYERS; i++)
3690 if (stored_player[i].active &&
3691 !tape.player_participates[map_player_action[i]])
3693 struct PlayerInfo *player = &stored_player[i];
3694 int jx = player->jx, jy = player->jy;
3696 #if DEBUG_INIT_PLAYER
3698 printf("Removing player %d at (%d, %d)\n", i + 1, jx, jy);
3701 player->active = FALSE;
3702 StorePlayer[jx][jy] = 0;
3703 Feld[jx][jy] = EL_EMPTY;
3710 for (i = 0; i < MAX_PLAYERS; i++)
3712 if (stored_player[i].active &&
3713 !tape.player_participates[i])
3715 struct PlayerInfo *player = &stored_player[i];
3716 int jx = player->jx, jy = player->jy;
3718 player->active = FALSE;
3719 StorePlayer[jx][jy] = 0;
3720 Feld[jx][jy] = EL_EMPTY;
3725 else if (!options.network && !game.team_mode) /* && !tape.playing */
3727 /* when in single player mode, eliminate all but the first active player */
3729 for (i = 0; i < MAX_PLAYERS; i++)
3731 if (stored_player[i].active)
3733 for (j = i + 1; j < MAX_PLAYERS; j++)
3735 if (stored_player[j].active)
3737 struct PlayerInfo *player = &stored_player[j];
3738 int jx = player->jx, jy = player->jy;
3740 player->active = FALSE;
3741 player->present = FALSE;
3743 StorePlayer[jx][jy] = 0;
3744 Feld[jx][jy] = EL_EMPTY;
3751 /* when recording the game, store which players take part in the game */
3754 #if USE_NEW_PLAYER_ASSIGNMENTS
3755 for (i = 0; i < MAX_PLAYERS; i++)
3756 if (stored_player[i].connected)
3757 tape.player_participates[i] = TRUE;
3759 for (i = 0; i < MAX_PLAYERS; i++)
3760 if (stored_player[i].active)
3761 tape.player_participates[i] = TRUE;
3765 #if DEBUG_INIT_PLAYER
3768 printf("Player status after player assignment (final stage):\n");
3770 for (i = 0; i < MAX_PLAYERS; i++)
3772 struct PlayerInfo *player = &stored_player[i];
3774 printf("- player %d: present == %d, connected == %d, active == %d",
3780 if (local_player == player)
3781 printf(" (local player)");
3788 if (BorderElement == EL_EMPTY)
3791 SBX_Right = lev_fieldx - SCR_FIELDX;
3793 SBY_Lower = lev_fieldy - SCR_FIELDY;
3798 SBX_Right = lev_fieldx - SCR_FIELDX + 1;
3800 SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
3803 if (full_lev_fieldx <= SCR_FIELDX)
3804 SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
3805 if (full_lev_fieldy <= SCR_FIELDY)
3806 SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
3808 if (EVEN(SCR_FIELDX) && full_lev_fieldx > SCR_FIELDX)
3810 if (EVEN(SCR_FIELDY) && full_lev_fieldy > SCR_FIELDY)
3813 /* if local player not found, look for custom element that might create
3814 the player (make some assumptions about the right custom element) */
3815 if (!local_player->present)
3817 int start_x = 0, start_y = 0;
3818 int found_rating = 0;
3819 int found_element = EL_UNDEFINED;
3820 int player_nr = local_player->index_nr;
3822 SCAN_PLAYFIELD(x, y)
3824 int element = Feld[x][y];
3829 if (level.use_start_element[player_nr] &&
3830 level.start_element[player_nr] == element &&
3837 found_element = element;
3840 if (!IS_CUSTOM_ELEMENT(element))
3843 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 single target */
3848 content = element_info[element].change_page[i].target_element;
3849 is_player = ELEM_IS_PLAYER(content);
3851 if (is_player && (found_rating < 3 ||
3852 (found_rating == 3 && element < found_element)))
3858 found_element = element;
3863 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
3865 /* check for player created from custom element as explosion content */
3866 content = element_info[element].content.e[xx][yy];
3867 is_player = ELEM_IS_PLAYER(content);
3869 if (is_player && (found_rating < 2 ||
3870 (found_rating == 2 && element < found_element)))
3872 start_x = x + xx - 1;
3873 start_y = y + yy - 1;
3876 found_element = element;
3879 if (!CAN_CHANGE(element))
3882 for (i = 0; i < element_info[element].num_change_pages; i++)
3884 /* check for player created from custom element as extended target */
3886 element_info[element].change_page[i].target_content.e[xx][yy];
3888 is_player = ELEM_IS_PLAYER(content);
3890 if (is_player && (found_rating < 1 ||
3891 (found_rating == 1 && element < found_element)))
3893 start_x = x + xx - 1;
3894 start_y = y + yy - 1;
3897 found_element = element;
3903 scroll_x = (start_x < SBX_Left + MIDPOSX ? SBX_Left :
3904 start_x > SBX_Right + MIDPOSX ? SBX_Right :
3907 scroll_y = (start_y < SBY_Upper + MIDPOSY ? SBY_Upper :
3908 start_y > SBY_Lower + MIDPOSY ? SBY_Lower :
3913 scroll_x = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
3914 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
3915 local_player->jx - MIDPOSX);
3917 scroll_y = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
3918 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
3919 local_player->jy - MIDPOSY);
3922 /* !!! FIX THIS (START) !!! */
3923 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
3925 InitGameEngine_EM();
3927 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
3929 InitGameEngine_SP();
3933 DrawLevel(REDRAW_FIELD);
3936 /* after drawing the level, correct some elements */
3937 if (game.timegate_time_left == 0)
3938 CloseAllOpenTimegates();
3941 /* blit playfield from scroll buffer to normal back buffer for fading in */
3942 BlitScreenToBitmap(backbuffer);
3943 /* !!! FIX THIS (END) !!! */
3945 DrawMaskedBorder(fade_mask);
3950 // full screen redraw is required at this point in the following cases:
3951 // - special editor door undrawn when game was started from level editor
3952 // - drawing area (playfield) was changed and has to be removed completely
3953 redraw_mask = REDRAW_ALL;
3957 if (!game.restart_level)
3959 /* copy default game door content to main double buffer */
3961 /* !!! CHECK AGAIN !!! */
3962 SetPanelBackground();
3963 // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
3964 DrawBackground(DX, DY, DXSIZE, DYSIZE);
3967 SetPanelBackground();
3968 SetDrawBackgroundMask(REDRAW_DOOR_1);
3970 UpdateAndDisplayGameControlValues();
3972 if (!game.restart_level)
3978 CreateGameButtons();
3980 game_gadget[SOUND_CTRL_ID_MUSIC]->checked = setup.sound_music;
3981 game_gadget[SOUND_CTRL_ID_LOOPS]->checked = setup.sound_loops;
3982 game_gadget[SOUND_CTRL_ID_SIMPLE]->checked = setup.sound_simple;
3987 /* copy actual game door content to door double buffer for OpenDoor() */
3988 BlitBitmap(drawto, bitmap_db_door_1, DX, DY, DXSIZE, DYSIZE, 0, 0);
3990 OpenDoor(DOOR_OPEN_ALL);
3992 PlaySound(SND_GAME_STARTING);
3994 if (setup.sound_music)
3997 KeyboardAutoRepeatOffUnlessAutoplay();
3999 #if DEBUG_INIT_PLAYER
4002 printf("Player status (final):\n");
4004 for (i = 0; i < MAX_PLAYERS; i++)
4006 struct PlayerInfo *player = &stored_player[i];
4008 printf("- player %d: present == %d, connected == %d, active == %d",
4014 if (local_player == player)
4015 printf(" (local player)");
4028 if (!game.restart_level && !tape.playing)
4030 LevelStats_incPlayed(level_nr);
4032 SaveLevelSetup_SeriesInfo();
4035 game.restart_level = FALSE;
4037 SaveEngineSnapshotToListInitial();
4040 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y)
4042 /* this is used for non-R'n'D game engines to update certain engine values */
4044 /* needed to determine if sounds are played within the visible screen area */
4045 scroll_x = actual_scroll_x;
4046 scroll_y = actual_scroll_y;
4049 void InitMovDir(int x, int y)
4051 int i, element = Feld[x][y];
4052 static int xy[4][2] =
4059 static int direction[3][4] =
4061 { MV_RIGHT, MV_UP, MV_LEFT, MV_DOWN },
4062 { MV_LEFT, MV_DOWN, MV_RIGHT, MV_UP },
4063 { MV_LEFT, MV_RIGHT, MV_UP, MV_DOWN }
4072 Feld[x][y] = EL_BUG;
4073 MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
4076 case EL_SPACESHIP_RIGHT:
4077 case EL_SPACESHIP_UP:
4078 case EL_SPACESHIP_LEFT:
4079 case EL_SPACESHIP_DOWN:
4080 Feld[x][y] = EL_SPACESHIP;
4081 MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
4084 case EL_BD_BUTTERFLY_RIGHT:
4085 case EL_BD_BUTTERFLY_UP:
4086 case EL_BD_BUTTERFLY_LEFT:
4087 case EL_BD_BUTTERFLY_DOWN:
4088 Feld[x][y] = EL_BD_BUTTERFLY;
4089 MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
4092 case EL_BD_FIREFLY_RIGHT:
4093 case EL_BD_FIREFLY_UP:
4094 case EL_BD_FIREFLY_LEFT:
4095 case EL_BD_FIREFLY_DOWN:
4096 Feld[x][y] = EL_BD_FIREFLY;
4097 MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
4100 case EL_PACMAN_RIGHT:
4102 case EL_PACMAN_LEFT:
4103 case EL_PACMAN_DOWN:
4104 Feld[x][y] = EL_PACMAN;
4105 MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
4108 case EL_YAMYAM_LEFT:
4109 case EL_YAMYAM_RIGHT:
4111 case EL_YAMYAM_DOWN:
4112 Feld[x][y] = EL_YAMYAM;
4113 MovDir[x][y] = direction[2][element - EL_YAMYAM_LEFT];
4116 case EL_SP_SNIKSNAK:
4117 MovDir[x][y] = MV_UP;
4120 case EL_SP_ELECTRON:
4121 MovDir[x][y] = MV_LEFT;
4128 Feld[x][y] = EL_MOLE;
4129 MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
4133 if (IS_CUSTOM_ELEMENT(element))
4135 struct ElementInfo *ei = &element_info[element];
4136 int move_direction_initial = ei->move_direction_initial;
4137 int move_pattern = ei->move_pattern;
4139 if (move_direction_initial == MV_START_PREVIOUS)
4141 if (MovDir[x][y] != MV_NONE)
4144 move_direction_initial = MV_START_AUTOMATIC;
4147 if (move_direction_initial == MV_START_RANDOM)
4148 MovDir[x][y] = 1 << RND(4);
4149 else if (move_direction_initial & MV_ANY_DIRECTION)
4150 MovDir[x][y] = move_direction_initial;
4151 else if (move_pattern == MV_ALL_DIRECTIONS ||
4152 move_pattern == MV_TURNING_LEFT ||
4153 move_pattern == MV_TURNING_RIGHT ||
4154 move_pattern == MV_TURNING_LEFT_RIGHT ||
4155 move_pattern == MV_TURNING_RIGHT_LEFT ||
4156 move_pattern == MV_TURNING_RANDOM)
4157 MovDir[x][y] = 1 << RND(4);
4158 else if (move_pattern == MV_HORIZONTAL)
4159 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
4160 else if (move_pattern == MV_VERTICAL)
4161 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
4162 else if (move_pattern & MV_ANY_DIRECTION)
4163 MovDir[x][y] = element_info[element].move_pattern;
4164 else if (move_pattern == MV_ALONG_LEFT_SIDE ||
4165 move_pattern == MV_ALONG_RIGHT_SIDE)
4167 /* use random direction as default start direction */
4168 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
4169 MovDir[x][y] = 1 << RND(4);
4171 for (i = 0; i < NUM_DIRECTIONS; i++)
4173 int x1 = x + xy[i][0];
4174 int y1 = y + xy[i][1];
4176 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4178 if (move_pattern == MV_ALONG_RIGHT_SIDE)
4179 MovDir[x][y] = direction[0][i];
4181 MovDir[x][y] = direction[1][i];
4190 MovDir[x][y] = 1 << RND(4);
4192 if (element != EL_BUG &&
4193 element != EL_SPACESHIP &&
4194 element != EL_BD_BUTTERFLY &&
4195 element != EL_BD_FIREFLY)
4198 for (i = 0; i < NUM_DIRECTIONS; i++)
4200 int x1 = x + xy[i][0];
4201 int y1 = y + xy[i][1];
4203 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4205 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
4207 MovDir[x][y] = direction[0][i];
4210 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
4211 element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4213 MovDir[x][y] = direction[1][i];
4222 GfxDir[x][y] = MovDir[x][y];
4225 void InitAmoebaNr(int x, int y)
4228 int group_nr = AmoebeNachbarNr(x, y);
4232 for (i = 1; i < MAX_NUM_AMOEBA; i++)
4234 if (AmoebaCnt[i] == 0)
4242 AmoebaNr[x][y] = group_nr;
4243 AmoebaCnt[group_nr]++;
4244 AmoebaCnt2[group_nr]++;
4247 static void PlayerWins(struct PlayerInfo *player)
4249 player->LevelSolved = TRUE;
4250 player->GameOver = TRUE;
4252 player->score_final = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
4253 level.native_em_level->lev->score : player->score);
4255 player->LevelSolved_CountingTime = (game.no_time_limit ? TimePlayed :
4257 player->LevelSolved_CountingScore = player->score_final;
4262 static int time, time_final;
4263 static int score, score_final;
4264 static int game_over_delay_1 = 0;
4265 static int game_over_delay_2 = 0;
4266 int game_over_delay_value_1 = 50;
4267 int game_over_delay_value_2 = 50;
4269 if (!local_player->LevelSolved_GameWon)
4273 /* do not start end game actions before the player stops moving (to exit) */
4274 if (local_player->MovPos)
4277 local_player->LevelSolved_GameWon = TRUE;
4278 local_player->LevelSolved_SaveTape = tape.recording;
4279 local_player->LevelSolved_SaveScore = !tape.playing;
4283 LevelStats_incSolved(level_nr);
4285 SaveLevelSetup_SeriesInfo();
4288 if (tape.auto_play) /* tape might already be stopped here */
4289 tape.auto_play_level_solved = TRUE;
4293 game_over_delay_1 = game_over_delay_value_1;
4294 game_over_delay_2 = game_over_delay_value_2;
4296 time = time_final = (game.no_time_limit ? TimePlayed : TimeLeft);
4297 score = score_final = local_player->score_final;
4302 score_final += TimeLeft * level.score[SC_TIME_BONUS];
4304 else if (game.no_time_limit && TimePlayed < 999)
4307 score_final += (999 - TimePlayed) * level.score[SC_TIME_BONUS];
4310 local_player->score_final = score_final;
4312 if (level_editor_test_game)
4315 score = score_final;
4317 local_player->LevelSolved_CountingTime = time;
4318 local_player->LevelSolved_CountingScore = score;
4320 game_panel_controls[GAME_PANEL_TIME].value = time;
4321 game_panel_controls[GAME_PANEL_SCORE].value = score;
4323 DisplayGameControlValues();
4326 if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
4328 if (ExitX >= 0 && ExitY >= 0) /* local player has left the level */
4330 /* close exit door after last player */
4331 if ((AllPlayersGone &&
4332 (Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
4333 Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN ||
4334 Feld[ExitX][ExitY] == EL_STEEL_EXIT_OPEN)) ||
4335 Feld[ExitX][ExitY] == EL_EM_EXIT_OPEN ||
4336 Feld[ExitX][ExitY] == EL_EM_STEEL_EXIT_OPEN)
4338 int element = Feld[ExitX][ExitY];
4340 Feld[ExitX][ExitY] =
4341 (element == EL_EXIT_OPEN ? EL_EXIT_CLOSING :
4342 element == EL_EM_EXIT_OPEN ? EL_EM_EXIT_CLOSING :
4343 element == EL_SP_EXIT_OPEN ? EL_SP_EXIT_CLOSING:
4344 element == EL_STEEL_EXIT_OPEN ? EL_STEEL_EXIT_CLOSING:
4345 EL_EM_STEEL_EXIT_CLOSING);
4347 PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING);
4350 /* player disappears */
4351 DrawLevelField(ExitX, ExitY);
4354 for (i = 0; i < MAX_PLAYERS; i++)
4356 struct PlayerInfo *player = &stored_player[i];
4358 if (player->present)
4360 RemovePlayer(player);
4362 /* player disappears */
4363 DrawLevelField(player->jx, player->jy);
4368 PlaySound(SND_GAME_WINNING);
4371 if (game_over_delay_1 > 0)
4373 game_over_delay_1--;
4378 if (time != time_final)
4380 int time_to_go = ABS(time_final - time);
4381 int time_count_dir = (time < time_final ? +1 : -1);
4382 int time_count_steps = (time_to_go > 100 && time_to_go % 10 == 0 ? 10 : 1);
4384 time += time_count_steps * time_count_dir;
4385 score += time_count_steps * level.score[SC_TIME_BONUS];
4387 local_player->LevelSolved_CountingTime = time;
4388 local_player->LevelSolved_CountingScore = score;
4390 game_panel_controls[GAME_PANEL_TIME].value = time;
4391 game_panel_controls[GAME_PANEL_SCORE].value = score;
4393 DisplayGameControlValues();
4395 if (time == time_final)
4396 StopSound(SND_GAME_LEVELTIME_BONUS);
4397 else if (setup.sound_loops)
4398 PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4400 PlaySound(SND_GAME_LEVELTIME_BONUS);
4405 local_player->LevelSolved_PanelOff = TRUE;
4407 if (game_over_delay_2 > 0)
4409 game_over_delay_2--;
4420 boolean raise_level = FALSE;
4422 local_player->LevelSolved_GameEnd = TRUE;
4424 if (!global.use_envelope_request)
4425 CloseDoor(DOOR_CLOSE_1);
4427 if (local_player->LevelSolved_SaveTape)
4429 SaveTapeChecked(tape.level_nr); /* ask to save tape */
4432 CloseDoor(DOOR_CLOSE_ALL);
4434 if (level_editor_test_game)
4436 SetGameStatus(GAME_MODE_MAIN);
4443 if (!local_player->LevelSolved_SaveScore)
4445 SetGameStatus(GAME_MODE_MAIN);
4452 if (level_nr == leveldir_current->handicap_level)
4454 leveldir_current->handicap_level++;
4456 SaveLevelSetup_SeriesInfo();
4459 if (level_nr < leveldir_current->last_level)
4460 raise_level = TRUE; /* advance to next level */
4462 if ((hi_pos = NewHiScore()) >= 0)
4464 SetGameStatus(GAME_MODE_SCORES);
4466 DrawHallOfFame(hi_pos);
4476 SetGameStatus(GAME_MODE_MAIN);
4493 LoadScore(level_nr);
4495 if (strEqual(setup.player_name, EMPTY_PLAYER_NAME) ||
4496 local_player->score_final < highscore[MAX_SCORE_ENTRIES - 1].Score)
4499 for (k = 0; k < MAX_SCORE_ENTRIES; k++)
4501 if (local_player->score_final > highscore[k].Score)
4503 /* player has made it to the hall of fame */
4505 if (k < MAX_SCORE_ENTRIES - 1)
4507 int m = MAX_SCORE_ENTRIES - 1;
4510 for (l = k; l < MAX_SCORE_ENTRIES; l++)
4511 if (strEqual(setup.player_name, highscore[l].Name))
4513 if (m == k) /* player's new highscore overwrites his old one */
4517 for (l = m; l > k; l--)
4519 strcpy(highscore[l].Name, highscore[l - 1].Name);
4520 highscore[l].Score = highscore[l - 1].Score;
4527 strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
4528 highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
4529 highscore[k].Score = local_player->score_final;
4535 else if (!strncmp(setup.player_name, highscore[k].Name,
4536 MAX_PLAYER_NAME_LEN))
4537 break; /* player already there with a higher score */
4543 SaveScore(level_nr);
4548 inline static int getElementMoveStepsizeExt(int x, int y, int direction)
4550 int element = Feld[x][y];
4551 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
4552 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
4553 int horiz_move = (dx != 0);
4554 int sign = (horiz_move ? dx : dy);
4555 int step = sign * element_info[element].move_stepsize;
4557 /* special values for move stepsize for spring and things on conveyor belt */
4560 if (CAN_FALL(element) &&
4561 y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
4562 step = sign * MOVE_STEPSIZE_NORMAL / 2;
4563 else if (element == EL_SPRING)
4564 step = sign * MOVE_STEPSIZE_NORMAL * 2;
4570 inline static int getElementMoveStepsize(int x, int y)
4572 return getElementMoveStepsizeExt(x, y, MovDir[x][y]);
4575 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
4577 if (player->GfxAction != action || player->GfxDir != dir)
4579 player->GfxAction = action;
4580 player->GfxDir = dir;
4582 player->StepFrame = 0;
4586 static void ResetGfxFrame(int x, int y, boolean redraw)
4588 int element = Feld[x][y];
4589 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
4590 int last_gfx_frame = GfxFrame[x][y];
4592 if (graphic_info[graphic].anim_global_sync)
4593 GfxFrame[x][y] = FrameCounter;
4594 else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
4595 GfxFrame[x][y] = CustomValue[x][y];
4596 else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
4597 GfxFrame[x][y] = element_info[element].collect_score;
4598 else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
4599 GfxFrame[x][y] = ChangeDelay[x][y];
4601 if (redraw && GfxFrame[x][y] != last_gfx_frame)
4602 DrawLevelGraphicAnimation(x, y, graphic);
4605 static void ResetGfxAnimation(int x, int y)
4607 GfxAction[x][y] = ACTION_DEFAULT;
4608 GfxDir[x][y] = MovDir[x][y];
4611 ResetGfxFrame(x, y, FALSE);
4614 static void ResetRandomAnimationValue(int x, int y)
4616 GfxRandom[x][y] = INIT_GFX_RANDOM();
4619 void InitMovingField(int x, int y, int direction)
4621 int element = Feld[x][y];
4622 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
4623 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
4626 boolean is_moving_before, is_moving_after;
4628 /* check if element was/is moving or being moved before/after mode change */
4629 is_moving_before = (WasJustMoving[x][y] != 0);
4630 is_moving_after = (getElementMoveStepsizeExt(x, y, direction) != 0);
4632 /* reset animation only for moving elements which change direction of moving
4633 or which just started or stopped moving
4634 (else CEs with property "can move" / "not moving" are reset each frame) */
4635 if (is_moving_before != is_moving_after ||
4636 direction != MovDir[x][y])
4637 ResetGfxAnimation(x, y);
4639 MovDir[x][y] = direction;
4640 GfxDir[x][y] = direction;
4642 GfxAction[x][y] = (!is_moving_after ? ACTION_WAITING :
4643 direction == MV_DOWN && CAN_FALL(element) ?
4644 ACTION_FALLING : ACTION_MOVING);
4646 /* this is needed for CEs with property "can move" / "not moving" */
4648 if (is_moving_after)
4650 if (Feld[newx][newy] == EL_EMPTY)
4651 Feld[newx][newy] = EL_BLOCKED;
4653 MovDir[newx][newy] = MovDir[x][y];
4655 CustomValue[newx][newy] = CustomValue[x][y];
4657 GfxFrame[newx][newy] = GfxFrame[x][y];
4658 GfxRandom[newx][newy] = GfxRandom[x][y];
4659 GfxAction[newx][newy] = GfxAction[x][y];
4660 GfxDir[newx][newy] = GfxDir[x][y];
4664 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
4666 int direction = MovDir[x][y];
4667 int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0);
4668 int newy = y + (direction & MV_UP ? -1 : direction & MV_DOWN ? +1 : 0);
4674 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
4676 int oldx = x, oldy = y;
4677 int direction = MovDir[x][y];
4679 if (direction == MV_LEFT)
4681 else if (direction == MV_RIGHT)
4683 else if (direction == MV_UP)
4685 else if (direction == MV_DOWN)
4688 *comes_from_x = oldx;
4689 *comes_from_y = oldy;
4692 int MovingOrBlocked2Element(int x, int y)
4694 int element = Feld[x][y];
4696 if (element == EL_BLOCKED)
4700 Blocked2Moving(x, y, &oldx, &oldy);
4701 return Feld[oldx][oldy];
4707 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
4709 /* like MovingOrBlocked2Element(), but if element is moving
4710 and (x,y) is the field the moving element is just leaving,
4711 return EL_BLOCKED instead of the element value */
4712 int element = Feld[x][y];
4714 if (IS_MOVING(x, y))
4716 if (element == EL_BLOCKED)
4720 Blocked2Moving(x, y, &oldx, &oldy);
4721 return Feld[oldx][oldy];
4730 static void RemoveField(int x, int y)
4732 Feld[x][y] = EL_EMPTY;
4738 CustomValue[x][y] = 0;
4741 ChangeDelay[x][y] = 0;
4742 ChangePage[x][y] = -1;
4743 Pushed[x][y] = FALSE;
4745 GfxElement[x][y] = EL_UNDEFINED;
4746 GfxAction[x][y] = ACTION_DEFAULT;
4747 GfxDir[x][y] = MV_NONE;
4750 void RemoveMovingField(int x, int y)
4752 int oldx = x, oldy = y, newx = x, newy = y;
4753 int element = Feld[x][y];
4754 int next_element = EL_UNDEFINED;
4756 if (element != EL_BLOCKED && !IS_MOVING(x, y))
4759 if (IS_MOVING(x, y))
4761 Moving2Blocked(x, y, &newx, &newy);
4763 if (Feld[newx][newy] != EL_BLOCKED)
4765 /* element is moving, but target field is not free (blocked), but
4766 already occupied by something different (example: acid pool);
4767 in this case, only remove the moving field, but not the target */
4769 RemoveField(oldx, oldy);
4771 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
4773 TEST_DrawLevelField(oldx, oldy);
4778 else if (element == EL_BLOCKED)
4780 Blocked2Moving(x, y, &oldx, &oldy);
4781 if (!IS_MOVING(oldx, oldy))
4785 if (element == EL_BLOCKED &&
4786 (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
4787 Feld[oldx][oldy] == EL_QUICKSAND_FAST_EMPTYING ||
4788 Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
4789 Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
4790 Feld[oldx][oldy] == EL_DC_MAGIC_WALL_EMPTYING ||
4791 Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
4792 next_element = get_next_element(Feld[oldx][oldy]);
4794 RemoveField(oldx, oldy);
4795 RemoveField(newx, newy);
4797 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
4799 if (next_element != EL_UNDEFINED)
4800 Feld[oldx][oldy] = next_element;
4802 TEST_DrawLevelField(oldx, oldy);
4803 TEST_DrawLevelField(newx, newy);
4806 void DrawDynamite(int x, int y)
4808 int sx = SCREENX(x), sy = SCREENY(y);
4809 int graphic = el2img(Feld[x][y]);
4812 if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
4815 if (IS_WALKABLE_INSIDE(Back[x][y]))
4819 DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
4820 else if (Store[x][y])
4821 DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
4823 frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
4825 if (Back[x][y] || Store[x][y])
4826 DrawGraphicThruMask(sx, sy, graphic, frame);
4828 DrawGraphic(sx, sy, graphic, frame);
4831 void CheckDynamite(int x, int y)
4833 if (MovDelay[x][y] != 0) /* dynamite is still waiting to explode */
4837 if (MovDelay[x][y] != 0)
4840 PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
4846 StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
4851 static void setMinimalPlayerBoundaries(int *sx1, int *sy1, int *sx2, int *sy2)
4853 boolean num_checked_players = 0;
4856 for (i = 0; i < MAX_PLAYERS; i++)
4858 if (stored_player[i].active)
4860 int sx = stored_player[i].jx;
4861 int sy = stored_player[i].jy;
4863 if (num_checked_players == 0)
4870 *sx1 = MIN(*sx1, sx);
4871 *sy1 = MIN(*sy1, sy);
4872 *sx2 = MAX(*sx2, sx);
4873 *sy2 = MAX(*sy2, sy);
4876 num_checked_players++;
4881 static boolean checkIfAllPlayersFitToScreen_RND()
4883 int sx1 = 0, sy1 = 0, sx2 = 0, sy2 = 0;
4885 setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
4887 return (sx2 - sx1 < SCR_FIELDX &&
4888 sy2 - sy1 < SCR_FIELDY);
4891 static void setScreenCenteredToAllPlayers(int *sx, int *sy)
4893 int sx1 = scroll_x, sy1 = scroll_y, sx2 = scroll_x, sy2 = scroll_y;
4895 setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
4897 *sx = (sx1 + sx2) / 2;
4898 *sy = (sy1 + sy2) / 2;
4901 void DrawRelocateScreen(int old_x, int old_y, int x, int y, int move_dir,
4902 boolean center_screen, boolean quick_relocation)
4904 unsigned int frame_delay_value_old = GetVideoFrameDelay();
4905 boolean ffwd_delay = (tape.playing && tape.fast_forward);
4906 boolean no_delay = (tape.warp_forward);
4907 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
4908 int wait_delay_value = (no_delay ? 0 : frame_delay_value);
4909 int new_scroll_x, new_scroll_y;
4911 if (level.lazy_relocation && IN_VIS_FIELD(SCREENX(x), SCREENY(y)))
4913 /* case 1: quick relocation inside visible screen (without scrolling) */
4920 if (!level.shifted_relocation || center_screen)
4922 /* relocation _with_ centering of screen */
4924 new_scroll_x = (x < SBX_Left + MIDPOSX ? SBX_Left :
4925 x > SBX_Right + MIDPOSX ? SBX_Right :
4928 new_scroll_y = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
4929 y > SBY_Lower + MIDPOSY ? SBY_Lower :
4934 /* relocation _without_ centering of screen */
4936 int center_scroll_x = (old_x < SBX_Left + MIDPOSX ? SBX_Left :
4937 old_x > SBX_Right + MIDPOSX ? SBX_Right :
4940 int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
4941 old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
4944 int offset_x = x + (scroll_x - center_scroll_x);
4945 int offset_y = y + (scroll_y - center_scroll_y);
4947 new_scroll_x = (offset_x < SBX_Left + MIDPOSX ? SBX_Left :
4948 offset_x > SBX_Right + MIDPOSX ? SBX_Right :
4949 offset_x - MIDPOSX);
4951 new_scroll_y = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
4952 offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
4953 offset_y - MIDPOSY);
4956 if (quick_relocation)
4958 /* case 2: quick relocation (redraw without visible scrolling) */
4960 scroll_x = new_scroll_x;
4961 scroll_y = new_scroll_y;
4968 /* case 3: visible relocation (with scrolling to new position) */
4970 ScrollScreen(NULL, SCROLL_GO_ON); /* scroll last frame to full tile */
4972 SetVideoFrameDelay(wait_delay_value);
4974 while (scroll_x != new_scroll_x || scroll_y != new_scroll_y)
4977 int fx = FX, fy = FY;
4979 dx = (new_scroll_x < scroll_x ? +1 : new_scroll_x > scroll_x ? -1 : 0);
4980 dy = (new_scroll_y < scroll_y ? +1 : new_scroll_y > scroll_y ? -1 : 0);
4982 if (dx == 0 && dy == 0) /* no scrolling needed at all */
4988 fx += dx * TILEX / 2;
4989 fy += dy * TILEY / 2;
4991 ScrollLevel(dx, dy);
4994 /* scroll in two steps of half tile size to make things smoother */
4995 BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
4997 /* scroll second step to align at full tile size */
4998 BlitScreenToBitmap(window);
5004 SetVideoFrameDelay(frame_delay_value_old);
5007 void RelocatePlayer(int jx, int jy, int el_player_raw)
5009 int el_player = GET_PLAYER_ELEMENT(el_player_raw);
5010 int player_nr = GET_PLAYER_NR(el_player);
5011 struct PlayerInfo *player = &stored_player[player_nr];
5012 boolean ffwd_delay = (tape.playing && tape.fast_forward);
5013 boolean no_delay = (tape.warp_forward);
5014 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5015 int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5016 int old_jx = player->jx;
5017 int old_jy = player->jy;
5018 int old_element = Feld[old_jx][old_jy];
5019 int element = Feld[jx][jy];
5020 boolean player_relocated = (old_jx != jx || old_jy != jy);
5022 int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
5023 int move_dir_vert = (jy < old_jy ? MV_UP : jy > old_jy ? MV_DOWN : 0);
5024 int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
5025 int enter_side_vert = MV_DIR_OPPOSITE(move_dir_vert);
5026 int leave_side_horiz = move_dir_horiz;
5027 int leave_side_vert = move_dir_vert;
5028 int enter_side = enter_side_horiz | enter_side_vert;
5029 int leave_side = leave_side_horiz | leave_side_vert;
5031 if (player->GameOver) /* do not reanimate dead player */
5034 if (!player_relocated) /* no need to relocate the player */
5037 if (IS_PLAYER(jx, jy)) /* player already placed at new position */
5039 RemoveField(jx, jy); /* temporarily remove newly placed player */
5040 DrawLevelField(jx, jy);
5043 if (player->present)
5045 while (player->MovPos)
5047 ScrollPlayer(player, SCROLL_GO_ON);
5048 ScrollScreen(NULL, SCROLL_GO_ON);
5050 AdvanceFrameAndPlayerCounters(player->index_nr);
5054 BackToFront_WithFrameDelay(wait_delay_value);
5057 DrawPlayer(player); /* needed here only to cleanup last field */
5058 DrawLevelField(player->jx, player->jy); /* remove player graphic */
5060 player->is_moving = FALSE;
5063 if (IS_CUSTOM_ELEMENT(old_element))
5064 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
5066 player->index_bit, leave_side);
5068 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
5070 player->index_bit, leave_side);
5072 Feld[jx][jy] = el_player;
5073 InitPlayerField(jx, jy, el_player, TRUE);
5075 /* "InitPlayerField()" above sets Feld[jx][jy] to EL_EMPTY, but it may be
5076 possible that the relocation target field did not contain a player element,
5077 but a walkable element, to which the new player was relocated -- in this
5078 case, restore that (already initialized!) element on the player field */
5079 if (!ELEM_IS_PLAYER(element)) /* player may be set on walkable element */
5081 Feld[jx][jy] = element; /* restore previously existing element */
5084 /* only visually relocate centered player */
5085 DrawRelocateScreen(old_jx, old_jy, player->jx, player->jy, player->MovDir,
5086 FALSE, level.instant_relocation);
5088 TestIfPlayerTouchesBadThing(jx, jy);
5089 TestIfPlayerTouchesCustomElement(jx, jy);
5091 if (IS_CUSTOM_ELEMENT(element))
5092 CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
5093 player->index_bit, enter_side);
5095 CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
5096 player->index_bit, enter_side);
5098 if (player->is_switching)
5100 /* ensure that relocation while still switching an element does not cause
5101 a new element to be treated as also switched directly after relocation
5102 (this is important for teleporter switches that teleport the player to
5103 a place where another teleporter switch is in the same direction, which
5104 would then incorrectly be treated as immediately switched before the
5105 direction key that caused the switch was released) */
5107 player->switch_x += jx - old_jx;
5108 player->switch_y += jy - old_jy;
5112 void Explode(int ex, int ey, int phase, int mode)
5118 /* !!! eliminate this variable !!! */
5119 int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
5121 if (game.explosions_delayed)
5123 ExplodeField[ex][ey] = mode;
5127 if (phase == EX_PHASE_START) /* initialize 'Store[][]' field */
5129 int center_element = Feld[ex][ey];
5130 int artwork_element, explosion_element; /* set these values later */
5132 /* remove things displayed in background while burning dynamite */
5133 if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
5136 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
5138 /* put moving element to center field (and let it explode there) */
5139 center_element = MovingOrBlocked2Element(ex, ey);
5140 RemoveMovingField(ex, ey);
5141 Feld[ex][ey] = center_element;
5144 /* now "center_element" is finally determined -- set related values now */
5145 artwork_element = center_element; /* for custom player artwork */
5146 explosion_element = center_element; /* for custom player artwork */
5148 if (IS_PLAYER(ex, ey))
5150 int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
5152 artwork_element = stored_player[player_nr].artwork_element;
5154 if (level.use_explosion_element[player_nr])
5156 explosion_element = level.explosion_element[player_nr];
5157 artwork_element = explosion_element;
5161 if (mode == EX_TYPE_NORMAL ||
5162 mode == EX_TYPE_CENTER ||
5163 mode == EX_TYPE_CROSS)
5164 PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
5166 last_phase = element_info[explosion_element].explosion_delay + 1;
5168 for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
5170 int xx = x - ex + 1;
5171 int yy = y - ey + 1;
5174 if (!IN_LEV_FIELD(x, y) ||
5175 (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
5176 (mode == EX_TYPE_CROSS && (x != ex && y != ey)))
5179 element = Feld[x][y];
5181 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
5183 element = MovingOrBlocked2Element(x, y);
5185 if (!IS_EXPLOSION_PROOF(element))
5186 RemoveMovingField(x, y);
5189 /* indestructible elements can only explode in center (but not flames) */
5190 if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
5191 mode == EX_TYPE_BORDER)) ||
5192 element == EL_FLAMES)
5195 /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
5196 behaviour, for example when touching a yamyam that explodes to rocks
5197 with active deadly shield, a rock is created under the player !!! */
5198 /* (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8) */
5200 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
5201 (game.engine_version < VERSION_IDENT(3,1,0,0) ||
5202 (x == ex && y == ey && mode != EX_TYPE_BORDER)))
5204 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
5207 if (IS_ACTIVE_BOMB(element))
5209 /* re-activate things under the bomb like gate or penguin */
5210 Feld[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
5217 /* save walkable background elements while explosion on same tile */
5218 if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
5219 (x != ex || y != ey || mode == EX_TYPE_BORDER))
5220 Back[x][y] = element;
5222 /* ignite explodable elements reached by other explosion */
5223 if (element == EL_EXPLOSION)
5224 element = Store2[x][y];
5226 if (AmoebaNr[x][y] &&
5227 (element == EL_AMOEBA_FULL ||
5228 element == EL_BD_AMOEBA ||
5229 element == EL_AMOEBA_GROWING))
5231 AmoebaCnt[AmoebaNr[x][y]]--;
5232 AmoebaCnt2[AmoebaNr[x][y]]--;
5237 if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
5239 int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
5241 Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
5243 if (PLAYERINFO(ex, ey)->use_murphy)
5244 Store[x][y] = EL_EMPTY;
5247 /* !!! check this case -- currently needed for rnd_rado_negundo_v,
5248 !!! levels 015 018 019 020 021 022 023 026 027 028 !!! */
5249 else if (ELEM_IS_PLAYER(center_element))
5250 Store[x][y] = EL_EMPTY;
5251 else if (center_element == EL_YAMYAM)
5252 Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
5253 else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
5254 Store[x][y] = element_info[center_element].content.e[xx][yy];
5256 /* needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
5257 (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
5258 otherwise) -- FIX THIS !!! */
5259 else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
5260 Store[x][y] = element_info[element].content.e[1][1];
5262 else if (!CAN_EXPLODE(element))
5263 Store[x][y] = element_info[element].content.e[1][1];
5266 Store[x][y] = EL_EMPTY;
5268 if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
5269 center_element == EL_AMOEBA_TO_DIAMOND)
5270 Store2[x][y] = element;
5272 Feld[x][y] = EL_EXPLOSION;
5273 GfxElement[x][y] = artwork_element;
5275 ExplodePhase[x][y] = 1;
5276 ExplodeDelay[x][y] = last_phase;
5281 if (center_element == EL_YAMYAM)
5282 game.yamyam_content_nr =
5283 (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
5295 GfxFrame[x][y] = 0; /* restart explosion animation */
5297 last_phase = ExplodeDelay[x][y];
5299 ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
5301 /* this can happen if the player leaves an explosion just in time */
5302 if (GfxElement[x][y] == EL_UNDEFINED)
5303 GfxElement[x][y] = EL_EMPTY;
5305 border_element = Store2[x][y];
5306 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5307 border_element = StorePlayer[x][y];
5309 if (phase == element_info[border_element].ignition_delay ||
5310 phase == last_phase)
5312 boolean border_explosion = FALSE;
5314 if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
5315 !PLAYER_EXPLOSION_PROTECTED(x, y))
5317 KillPlayerUnlessExplosionProtected(x, y);
5318 border_explosion = TRUE;
5320 else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
5322 Feld[x][y] = Store2[x][y];
5325 border_explosion = TRUE;
5327 else if (border_element == EL_AMOEBA_TO_DIAMOND)
5329 AmoebeUmwandeln(x, y);
5331 border_explosion = TRUE;
5334 /* if an element just explodes due to another explosion (chain-reaction),
5335 do not immediately end the new explosion when it was the last frame of
5336 the explosion (as it would be done in the following "if"-statement!) */
5337 if (border_explosion && phase == last_phase)
5341 if (phase == last_phase)
5345 element = Feld[x][y] = Store[x][y];
5346 Store[x][y] = Store2[x][y] = 0;
5347 GfxElement[x][y] = EL_UNDEFINED;
5349 /* player can escape from explosions and might therefore be still alive */
5350 if (element >= EL_PLAYER_IS_EXPLODING_1 &&
5351 element <= EL_PLAYER_IS_EXPLODING_4)
5353 int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
5354 int explosion_element = EL_PLAYER_1 + player_nr;
5355 int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
5356 int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
5358 if (level.use_explosion_element[player_nr])
5359 explosion_element = level.explosion_element[player_nr];
5361 Feld[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
5362 element_info[explosion_element].content.e[xx][yy]);
5365 /* restore probably existing indestructible background element */
5366 if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
5367 element = Feld[x][y] = Back[x][y];
5370 MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
5371 GfxDir[x][y] = MV_NONE;
5372 ChangeDelay[x][y] = 0;
5373 ChangePage[x][y] = -1;
5375 CustomValue[x][y] = 0;
5377 InitField_WithBug2(x, y, FALSE);
5379 TEST_DrawLevelField(x, y);
5381 TestIfElementTouchesCustomElement(x, y);
5383 if (GFX_CRUMBLED(element))
5384 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
5386 if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
5387 StorePlayer[x][y] = 0;
5389 if (ELEM_IS_PLAYER(element))
5390 RelocatePlayer(x, y, element);
5392 else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
5394 int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
5395 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5398 TEST_DrawLevelFieldCrumbled(x, y);
5400 if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
5402 DrawLevelElement(x, y, Back[x][y]);
5403 DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
5405 else if (IS_WALKABLE_UNDER(Back[x][y]))
5407 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5408 DrawLevelElementThruMask(x, y, Back[x][y]);
5410 else if (!IS_WALKABLE_INSIDE(Back[x][y]))
5411 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5415 void DynaExplode(int ex, int ey)
5418 int dynabomb_element = Feld[ex][ey];
5419 int dynabomb_size = 1;
5420 boolean dynabomb_xl = FALSE;
5421 struct PlayerInfo *player;
5422 static int xy[4][2] =
5430 if (IS_ACTIVE_BOMB(dynabomb_element))
5432 player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
5433 dynabomb_size = player->dynabomb_size;
5434 dynabomb_xl = player->dynabomb_xl;
5435 player->dynabombs_left++;
5438 Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
5440 for (i = 0; i < NUM_DIRECTIONS; i++)
5442 for (j = 1; j <= dynabomb_size; j++)
5444 int x = ex + j * xy[i][0];
5445 int y = ey + j * xy[i][1];
5448 if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
5451 element = Feld[x][y];
5453 /* do not restart explosions of fields with active bombs */
5454 if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
5457 Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
5459 if (element != EL_EMPTY && element != EL_EXPLOSION &&
5460 !IS_DIGGABLE(element) && !dynabomb_xl)
5466 void Bang(int x, int y)
5468 int element = MovingOrBlocked2Element(x, y);
5469 int explosion_type = EX_TYPE_NORMAL;
5471 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5473 struct PlayerInfo *player = PLAYERINFO(x, y);
5475 element = Feld[x][y] = player->initial_element;
5477 if (level.use_explosion_element[player->index_nr])
5479 int explosion_element = level.explosion_element[player->index_nr];
5481 if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
5482 explosion_type = EX_TYPE_CROSS;
5483 else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
5484 explosion_type = EX_TYPE_CENTER;
5492 case EL_BD_BUTTERFLY:
5495 case EL_DARK_YAMYAM:
5499 RaiseScoreElement(element);
5502 case EL_DYNABOMB_PLAYER_1_ACTIVE:
5503 case EL_DYNABOMB_PLAYER_2_ACTIVE:
5504 case EL_DYNABOMB_PLAYER_3_ACTIVE:
5505 case EL_DYNABOMB_PLAYER_4_ACTIVE:
5506 case EL_DYNABOMB_INCREASE_NUMBER:
5507 case EL_DYNABOMB_INCREASE_SIZE:
5508 case EL_DYNABOMB_INCREASE_POWER:
5509 explosion_type = EX_TYPE_DYNA;
5512 case EL_DC_LANDMINE:
5513 explosion_type = EX_TYPE_CENTER;
5518 case EL_LAMP_ACTIVE:
5519 case EL_AMOEBA_TO_DIAMOND:
5520 if (!IS_PLAYER(x, y)) /* penguin and player may be at same field */
5521 explosion_type = EX_TYPE_CENTER;
5525 if (element_info[element].explosion_type == EXPLODES_CROSS)
5526 explosion_type = EX_TYPE_CROSS;
5527 else if (element_info[element].explosion_type == EXPLODES_1X1)
5528 explosion_type = EX_TYPE_CENTER;
5532 if (explosion_type == EX_TYPE_DYNA)
5535 Explode(x, y, EX_PHASE_START, explosion_type);
5537 CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
5540 void SplashAcid(int x, int y)
5542 if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
5543 (!IN_LEV_FIELD(x - 1, y - 2) ||
5544 !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
5545 Feld[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
5547 if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
5548 (!IN_LEV_FIELD(x + 1, y - 2) ||
5549 !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
5550 Feld[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
5552 PlayLevelSound(x, y, SND_ACID_SPLASHING);
5555 static void InitBeltMovement()
5557 static int belt_base_element[4] =
5559 EL_CONVEYOR_BELT_1_LEFT,
5560 EL_CONVEYOR_BELT_2_LEFT,
5561 EL_CONVEYOR_BELT_3_LEFT,
5562 EL_CONVEYOR_BELT_4_LEFT
5564 static int belt_base_active_element[4] =
5566 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
5567 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
5568 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
5569 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
5574 /* set frame order for belt animation graphic according to belt direction */
5575 for (i = 0; i < NUM_BELTS; i++)
5579 for (j = 0; j < NUM_BELT_PARTS; j++)
5581 int element = belt_base_active_element[belt_nr] + j;
5582 int graphic_1 = el2img(element);
5583 int graphic_2 = el2panelimg(element);
5585 if (game.belt_dir[i] == MV_LEFT)
5587 graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
5588 graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
5592 graphic_info[graphic_1].anim_mode |= ANIM_REVERSE;
5593 graphic_info[graphic_2].anim_mode |= ANIM_REVERSE;
5598 SCAN_PLAYFIELD(x, y)
5600 int element = Feld[x][y];
5602 for (i = 0; i < NUM_BELTS; i++)
5604 if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
5606 int e_belt_nr = getBeltNrFromBeltElement(element);
5609 if (e_belt_nr == belt_nr)
5611 int belt_part = Feld[x][y] - belt_base_element[belt_nr];
5613 Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
5620 static void ToggleBeltSwitch(int x, int y)
5622 static int belt_base_element[4] =
5624 EL_CONVEYOR_BELT_1_LEFT,
5625 EL_CONVEYOR_BELT_2_LEFT,
5626 EL_CONVEYOR_BELT_3_LEFT,
5627 EL_CONVEYOR_BELT_4_LEFT
5629 static int belt_base_active_element[4] =
5631 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
5632 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
5633 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
5634 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
5636 static int belt_base_switch_element[4] =
5638 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
5639 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
5640 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
5641 EL_CONVEYOR_BELT_4_SWITCH_LEFT
5643 static int belt_move_dir[4] =
5651 int element = Feld[x][y];
5652 int belt_nr = getBeltNrFromBeltSwitchElement(element);
5653 int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
5654 int belt_dir = belt_move_dir[belt_dir_nr];
5657 if (!IS_BELT_SWITCH(element))
5660 game.belt_dir_nr[belt_nr] = belt_dir_nr;
5661 game.belt_dir[belt_nr] = belt_dir;
5663 if (belt_dir_nr == 3)
5666 /* set frame order for belt animation graphic according to belt direction */
5667 for (i = 0; i < NUM_BELT_PARTS; i++)
5669 int element = belt_base_active_element[belt_nr] + i;
5670 int graphic_1 = el2img(element);
5671 int graphic_2 = el2panelimg(element);
5673 if (belt_dir == MV_LEFT)
5675 graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
5676 graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
5680 graphic_info[graphic_1].anim_mode |= ANIM_REVERSE;
5681 graphic_info[graphic_2].anim_mode |= ANIM_REVERSE;
5685 SCAN_PLAYFIELD(xx, yy)
5687 int element = Feld[xx][yy];
5689 if (IS_BELT_SWITCH(element))
5691 int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
5693 if (e_belt_nr == belt_nr)
5695 Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
5696 TEST_DrawLevelField(xx, yy);
5699 else if (IS_BELT(element) && belt_dir != MV_NONE)
5701 int e_belt_nr = getBeltNrFromBeltElement(element);
5703 if (e_belt_nr == belt_nr)
5705 int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
5707 Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
5708 TEST_DrawLevelField(xx, yy);
5711 else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
5713 int e_belt_nr = getBeltNrFromBeltActiveElement(element);
5715 if (e_belt_nr == belt_nr)
5717 int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
5719 Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
5720 TEST_DrawLevelField(xx, yy);
5726 static void ToggleSwitchgateSwitch(int x, int y)
5730 game.switchgate_pos = !game.switchgate_pos;
5732 SCAN_PLAYFIELD(xx, yy)
5734 int element = Feld[xx][yy];
5736 if (element == EL_SWITCHGATE_SWITCH_UP)
5738 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_DOWN;
5739 TEST_DrawLevelField(xx, yy);
5741 else if (element == EL_SWITCHGATE_SWITCH_DOWN)
5743 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP;
5744 TEST_DrawLevelField(xx, yy);
5746 else if (element == EL_DC_SWITCHGATE_SWITCH_UP)
5748 Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_DOWN;
5749 TEST_DrawLevelField(xx, yy);
5751 else if (element == EL_DC_SWITCHGATE_SWITCH_DOWN)
5753 Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP;
5754 TEST_DrawLevelField(xx, yy);
5756 else if (element == EL_SWITCHGATE_OPEN ||
5757 element == EL_SWITCHGATE_OPENING)
5759 Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
5761 PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
5763 else if (element == EL_SWITCHGATE_CLOSED ||
5764 element == EL_SWITCHGATE_CLOSING)
5766 Feld[xx][yy] = EL_SWITCHGATE_OPENING;
5768 PlayLevelSoundAction(xx, yy, ACTION_OPENING);
5773 static int getInvisibleActiveFromInvisibleElement(int element)
5775 return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
5776 element == EL_INVISIBLE_WALL ? EL_INVISIBLE_WALL_ACTIVE :
5777 element == EL_INVISIBLE_SAND ? EL_INVISIBLE_SAND_ACTIVE :
5781 static int getInvisibleFromInvisibleActiveElement(int element)
5783 return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
5784 element == EL_INVISIBLE_WALL_ACTIVE ? EL_INVISIBLE_WALL :
5785 element == EL_INVISIBLE_SAND_ACTIVE ? EL_INVISIBLE_SAND :
5789 static void RedrawAllLightSwitchesAndInvisibleElements()
5793 SCAN_PLAYFIELD(x, y)
5795 int element = Feld[x][y];
5797 if (element == EL_LIGHT_SWITCH &&
5798 game.light_time_left > 0)
5800 Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
5801 TEST_DrawLevelField(x, y);
5803 else if (element == EL_LIGHT_SWITCH_ACTIVE &&
5804 game.light_time_left == 0)
5806 Feld[x][y] = EL_LIGHT_SWITCH;
5807 TEST_DrawLevelField(x, y);
5809 else if (element == EL_EMC_DRIPPER &&
5810 game.light_time_left > 0)
5812 Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
5813 TEST_DrawLevelField(x, y);
5815 else if (element == EL_EMC_DRIPPER_ACTIVE &&
5816 game.light_time_left == 0)
5818 Feld[x][y] = EL_EMC_DRIPPER;
5819 TEST_DrawLevelField(x, y);
5821 else if (element == EL_INVISIBLE_STEELWALL ||
5822 element == EL_INVISIBLE_WALL ||
5823 element == EL_INVISIBLE_SAND)
5825 if (game.light_time_left > 0)
5826 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
5828 TEST_DrawLevelField(x, y);
5830 /* uncrumble neighbour fields, if needed */
5831 if (element == EL_INVISIBLE_SAND)
5832 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
5834 else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
5835 element == EL_INVISIBLE_WALL_ACTIVE ||
5836 element == EL_INVISIBLE_SAND_ACTIVE)
5838 if (game.light_time_left == 0)
5839 Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
5841 TEST_DrawLevelField(x, y);
5843 /* re-crumble neighbour fields, if needed */
5844 if (element == EL_INVISIBLE_SAND)
5845 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
5850 static void RedrawAllInvisibleElementsForLenses()
5854 SCAN_PLAYFIELD(x, y)
5856 int element = Feld[x][y];
5858 if (element == EL_EMC_DRIPPER &&
5859 game.lenses_time_left > 0)
5861 Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
5862 TEST_DrawLevelField(x, y);
5864 else if (element == EL_EMC_DRIPPER_ACTIVE &&
5865 game.lenses_time_left == 0)
5867 Feld[x][y] = EL_EMC_DRIPPER;
5868 TEST_DrawLevelField(x, y);
5870 else if (element == EL_INVISIBLE_STEELWALL ||
5871 element == EL_INVISIBLE_WALL ||
5872 element == EL_INVISIBLE_SAND)
5874 if (game.lenses_time_left > 0)
5875 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
5877 TEST_DrawLevelField(x, y);
5879 /* uncrumble neighbour fields, if needed */
5880 if (element == EL_INVISIBLE_SAND)
5881 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
5883 else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
5884 element == EL_INVISIBLE_WALL_ACTIVE ||
5885 element == EL_INVISIBLE_SAND_ACTIVE)
5887 if (game.lenses_time_left == 0)
5888 Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
5890 TEST_DrawLevelField(x, y);
5892 /* re-crumble neighbour fields, if needed */
5893 if (element == EL_INVISIBLE_SAND)
5894 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
5899 static void RedrawAllInvisibleElementsForMagnifier()
5903 SCAN_PLAYFIELD(x, y)
5905 int element = Feld[x][y];
5907 if (element == EL_EMC_FAKE_GRASS &&
5908 game.magnify_time_left > 0)
5910 Feld[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
5911 TEST_DrawLevelField(x, y);
5913 else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
5914 game.magnify_time_left == 0)
5916 Feld[x][y] = EL_EMC_FAKE_GRASS;
5917 TEST_DrawLevelField(x, y);
5919 else if (IS_GATE_GRAY(element) &&
5920 game.magnify_time_left > 0)
5922 Feld[x][y] = (IS_RND_GATE_GRAY(element) ?
5923 element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
5924 IS_EM_GATE_GRAY(element) ?
5925 element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
5926 IS_EMC_GATE_GRAY(element) ?
5927 element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
5928 IS_DC_GATE_GRAY(element) ?
5929 EL_DC_GATE_WHITE_GRAY_ACTIVE :
5931 TEST_DrawLevelField(x, y);
5933 else if (IS_GATE_GRAY_ACTIVE(element) &&
5934 game.magnify_time_left == 0)
5936 Feld[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
5937 element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
5938 IS_EM_GATE_GRAY_ACTIVE(element) ?
5939 element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
5940 IS_EMC_GATE_GRAY_ACTIVE(element) ?
5941 element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
5942 IS_DC_GATE_GRAY_ACTIVE(element) ?
5943 EL_DC_GATE_WHITE_GRAY :
5945 TEST_DrawLevelField(x, y);
5950 static void ToggleLightSwitch(int x, int y)
5952 int element = Feld[x][y];
5954 game.light_time_left =
5955 (element == EL_LIGHT_SWITCH ?
5956 level.time_light * FRAMES_PER_SECOND : 0);
5958 RedrawAllLightSwitchesAndInvisibleElements();
5961 static void ActivateTimegateSwitch(int x, int y)
5965 game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
5967 SCAN_PLAYFIELD(xx, yy)
5969 int element = Feld[xx][yy];
5971 if (element == EL_TIMEGATE_CLOSED ||
5972 element == EL_TIMEGATE_CLOSING)
5974 Feld[xx][yy] = EL_TIMEGATE_OPENING;
5975 PlayLevelSound(xx, yy, SND_CLASS_TIMEGATE_OPENING);
5979 else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
5981 Feld[xx][yy] = EL_TIMEGATE_SWITCH;
5982 TEST_DrawLevelField(xx, yy);
5988 Feld[x][y] = (Feld[x][y] == EL_TIMEGATE_SWITCH ? EL_TIMEGATE_SWITCH_ACTIVE :
5989 EL_DC_TIMEGATE_SWITCH_ACTIVE);
5992 void Impact(int x, int y)
5994 boolean last_line = (y == lev_fieldy - 1);
5995 boolean object_hit = FALSE;
5996 boolean impact = (last_line || object_hit);
5997 int element = Feld[x][y];
5998 int smashed = EL_STEELWALL;
6000 if (!last_line) /* check if element below was hit */
6002 if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
6005 object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
6006 MovDir[x][y + 1] != MV_DOWN ||
6007 MovPos[x][y + 1] <= TILEY / 2));
6009 /* do not smash moving elements that left the smashed field in time */
6010 if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
6011 ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
6014 #if USE_QUICKSAND_IMPACT_BUGFIX
6015 if (Feld[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
6017 RemoveMovingField(x, y + 1);
6018 Feld[x][y + 1] = EL_QUICKSAND_EMPTY;
6019 Feld[x][y + 2] = EL_ROCK;
6020 TEST_DrawLevelField(x, y + 2);
6025 if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTYING && object_hit == FALSE)
6027 RemoveMovingField(x, y + 1);
6028 Feld[x][y + 1] = EL_QUICKSAND_FAST_EMPTY;
6029 Feld[x][y + 2] = EL_ROCK;
6030 TEST_DrawLevelField(x, y + 2);
6037 smashed = MovingOrBlocked2Element(x, y + 1);
6039 impact = (last_line || object_hit);
6042 if (!last_line && smashed == EL_ACID) /* element falls into acid */
6044 SplashAcid(x, y + 1);
6048 /* !!! not sufficient for all cases -- see EL_PEARL below !!! */
6049 /* only reset graphic animation if graphic really changes after impact */
6051 el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
6053 ResetGfxAnimation(x, y);
6054 TEST_DrawLevelField(x, y);
6057 if (impact && CAN_EXPLODE_IMPACT(element))
6062 else if (impact && element == EL_PEARL &&
6063 smashed != EL_DC_MAGIC_WALL && smashed != EL_DC_MAGIC_WALL_ACTIVE)
6065 ResetGfxAnimation(x, y);
6067 Feld[x][y] = EL_PEARL_BREAKING;
6068 PlayLevelSound(x, y, SND_PEARL_BREAKING);
6071 else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
6073 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6078 if (impact && element == EL_AMOEBA_DROP)
6080 if (object_hit && IS_PLAYER(x, y + 1))
6081 KillPlayerUnlessEnemyProtected(x, y + 1);
6082 else if (object_hit && smashed == EL_PENGUIN)
6086 Feld[x][y] = EL_AMOEBA_GROWING;
6087 Store[x][y] = EL_AMOEBA_WET;
6089 ResetRandomAnimationValue(x, y);
6094 if (object_hit) /* check which object was hit */
6096 if ((CAN_PASS_MAGIC_WALL(element) &&
6097 (smashed == EL_MAGIC_WALL ||
6098 smashed == EL_BD_MAGIC_WALL)) ||
6099 (CAN_PASS_DC_MAGIC_WALL(element) &&
6100 smashed == EL_DC_MAGIC_WALL))
6103 int activated_magic_wall =
6104 (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
6105 smashed == EL_BD_MAGIC_WALL ? EL_BD_MAGIC_WALL_ACTIVE :
6106 EL_DC_MAGIC_WALL_ACTIVE);
6108 /* activate magic wall / mill */
6109 SCAN_PLAYFIELD(xx, yy)
6111 if (Feld[xx][yy] == smashed)
6112 Feld[xx][yy] = activated_magic_wall;
6115 game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
6116 game.magic_wall_active = TRUE;
6118 PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
6119 SND_MAGIC_WALL_ACTIVATING :
6120 smashed == EL_BD_MAGIC_WALL ?
6121 SND_BD_MAGIC_WALL_ACTIVATING :
6122 SND_DC_MAGIC_WALL_ACTIVATING));
6125 if (IS_PLAYER(x, y + 1))
6127 if (CAN_SMASH_PLAYER(element))
6129 KillPlayerUnlessEnemyProtected(x, y + 1);
6133 else if (smashed == EL_PENGUIN)
6135 if (CAN_SMASH_PLAYER(element))
6141 else if (element == EL_BD_DIAMOND)
6143 if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
6149 else if (((element == EL_SP_INFOTRON ||
6150 element == EL_SP_ZONK) &&
6151 (smashed == EL_SP_SNIKSNAK ||
6152 smashed == EL_SP_ELECTRON ||
6153 smashed == EL_SP_DISK_ORANGE)) ||
6154 (element == EL_SP_INFOTRON &&
6155 smashed == EL_SP_DISK_YELLOW))
6160 else if (CAN_SMASH_EVERYTHING(element))
6162 if (IS_CLASSIC_ENEMY(smashed) ||
6163 CAN_EXPLODE_SMASHED(smashed))
6168 else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
6170 if (smashed == EL_LAMP ||
6171 smashed == EL_LAMP_ACTIVE)
6176 else if (smashed == EL_NUT)
6178 Feld[x][y + 1] = EL_NUT_BREAKING;
6179 PlayLevelSound(x, y, SND_NUT_BREAKING);
6180 RaiseScoreElement(EL_NUT);
6183 else if (smashed == EL_PEARL)
6185 ResetGfxAnimation(x, y);
6187 Feld[x][y + 1] = EL_PEARL_BREAKING;
6188 PlayLevelSound(x, y, SND_PEARL_BREAKING);
6191 else if (smashed == EL_DIAMOND)
6193 Feld[x][y + 1] = EL_DIAMOND_BREAKING;
6194 PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
6197 else if (IS_BELT_SWITCH(smashed))
6199 ToggleBeltSwitch(x, y + 1);
6201 else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
6202 smashed == EL_SWITCHGATE_SWITCH_DOWN ||
6203 smashed == EL_DC_SWITCHGATE_SWITCH_UP ||
6204 smashed == EL_DC_SWITCHGATE_SWITCH_DOWN)
6206 ToggleSwitchgateSwitch(x, y + 1);
6208 else if (smashed == EL_LIGHT_SWITCH ||
6209 smashed == EL_LIGHT_SWITCH_ACTIVE)
6211 ToggleLightSwitch(x, y + 1);
6215 CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6217 CheckElementChangeBySide(x, y + 1, smashed, element,
6218 CE_SWITCHED, CH_SIDE_TOP);
6219 CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
6225 CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6230 /* play sound of magic wall / mill */
6232 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
6233 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ||
6234 Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE))
6236 if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
6237 PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
6238 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
6239 PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
6240 else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
6241 PlayLevelSound(x, y, SND_DC_MAGIC_WALL_FILLING);
6246 /* play sound of object that hits the ground */
6247 if (last_line || object_hit)
6248 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6251 inline static void TurnRoundExt(int x, int y)
6263 { 0, 0 }, { 0, 0 }, { 0, 0 },
6268 int left, right, back;
6272 { MV_DOWN, MV_UP, MV_RIGHT },
6273 { MV_UP, MV_DOWN, MV_LEFT },
6275 { MV_LEFT, MV_RIGHT, MV_DOWN },
6279 { MV_RIGHT, MV_LEFT, MV_UP }
6282 int element = Feld[x][y];
6283 int move_pattern = element_info[element].move_pattern;
6285 int old_move_dir = MovDir[x][y];
6286 int left_dir = turn[old_move_dir].left;
6287 int right_dir = turn[old_move_dir].right;
6288 int back_dir = turn[old_move_dir].back;
6290 int left_dx = move_xy[left_dir].dx, left_dy = move_xy[left_dir].dy;
6291 int right_dx = move_xy[right_dir].dx, right_dy = move_xy[right_dir].dy;
6292 int move_dx = move_xy[old_move_dir].dx, move_dy = move_xy[old_move_dir].dy;
6293 int back_dx = move_xy[back_dir].dx, back_dy = move_xy[back_dir].dy;
6295 int left_x = x + left_dx, left_y = y + left_dy;
6296 int right_x = x + right_dx, right_y = y + right_dy;
6297 int move_x = x + move_dx, move_y = y + move_dy;
6301 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
6303 TestIfBadThingTouchesOtherBadThing(x, y);
6305 if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
6306 MovDir[x][y] = right_dir;
6307 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6308 MovDir[x][y] = left_dir;
6310 if (element == EL_BUG && MovDir[x][y] != old_move_dir)
6312 else if (element == EL_BD_BUTTERFLY) /* && MovDir[x][y] == left_dir) */
6315 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
6317 TestIfBadThingTouchesOtherBadThing(x, y);
6319 if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
6320 MovDir[x][y] = left_dir;
6321 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6322 MovDir[x][y] = right_dir;
6324 if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
6326 else if (element == EL_BD_FIREFLY) /* && MovDir[x][y] == right_dir) */
6329 else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
6331 TestIfBadThingTouchesOtherBadThing(x, y);
6333 if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
6334 MovDir[x][y] = left_dir;
6335 else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
6336 MovDir[x][y] = right_dir;
6338 if (MovDir[x][y] != old_move_dir)
6341 else if (element == EL_YAMYAM)
6343 boolean can_turn_left = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
6344 boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
6346 if (can_turn_left && can_turn_right)
6347 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6348 else if (can_turn_left)
6349 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6350 else if (can_turn_right)
6351 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6353 MovDir[x][y] = back_dir;
6355 MovDelay[x][y] = 16 + 16 * RND(3);
6357 else if (element == EL_DARK_YAMYAM)
6359 boolean can_turn_left = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6361 boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6364 if (can_turn_left && can_turn_right)
6365 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6366 else if (can_turn_left)
6367 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6368 else if (can_turn_right)
6369 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6371 MovDir[x][y] = back_dir;
6373 MovDelay[x][y] = 16 + 16 * RND(3);
6375 else if (element == EL_PACMAN)
6377 boolean can_turn_left = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
6378 boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
6380 if (can_turn_left && can_turn_right)
6381 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6382 else if (can_turn_left)
6383 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6384 else if (can_turn_right)
6385 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6387 MovDir[x][y] = back_dir;
6389 MovDelay[x][y] = 6 + RND(40);
6391 else if (element == EL_PIG)
6393 boolean can_turn_left = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
6394 boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
6395 boolean can_move_on = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
6396 boolean should_turn_left, should_turn_right, should_move_on;
6398 int rnd = RND(rnd_value);
6400 should_turn_left = (can_turn_left &&
6402 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
6403 y + back_dy + left_dy)));
6404 should_turn_right = (can_turn_right &&
6406 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
6407 y + back_dy + right_dy)));
6408 should_move_on = (can_move_on &&
6411 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
6412 y + move_dy + left_dy) ||
6413 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
6414 y + move_dy + right_dy)));
6416 if (should_turn_left || should_turn_right || should_move_on)
6418 if (should_turn_left && should_turn_right && should_move_on)
6419 MovDir[x][y] = (rnd < rnd_value / 3 ? left_dir :
6420 rnd < 2 * rnd_value / 3 ? right_dir :
6422 else if (should_turn_left && should_turn_right)
6423 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6424 else if (should_turn_left && should_move_on)
6425 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
6426 else if (should_turn_right && should_move_on)
6427 MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
6428 else if (should_turn_left)
6429 MovDir[x][y] = left_dir;
6430 else if (should_turn_right)
6431 MovDir[x][y] = right_dir;
6432 else if (should_move_on)
6433 MovDir[x][y] = old_move_dir;
6435 else if (can_move_on && rnd > rnd_value / 8)
6436 MovDir[x][y] = old_move_dir;
6437 else if (can_turn_left && can_turn_right)
6438 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6439 else if (can_turn_left && rnd > rnd_value / 8)
6440 MovDir[x][y] = left_dir;
6441 else if (can_turn_right && rnd > rnd_value/8)
6442 MovDir[x][y] = right_dir;
6444 MovDir[x][y] = back_dir;
6446 xx = x + move_xy[MovDir[x][y]].dx;
6447 yy = y + move_xy[MovDir[x][y]].dy;
6449 if (!IN_LEV_FIELD(xx, yy) ||
6450 (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy])))
6451 MovDir[x][y] = old_move_dir;
6455 else if (element == EL_DRAGON)
6457 boolean can_turn_left = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
6458 boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
6459 boolean can_move_on = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
6461 int rnd = RND(rnd_value);
6463 if (can_move_on && rnd > rnd_value / 8)
6464 MovDir[x][y] = old_move_dir;
6465 else if (can_turn_left && can_turn_right)
6466 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6467 else if (can_turn_left && rnd > rnd_value / 8)
6468 MovDir[x][y] = left_dir;
6469 else if (can_turn_right && rnd > rnd_value / 8)
6470 MovDir[x][y] = right_dir;
6472 MovDir[x][y] = back_dir;
6474 xx = x + move_xy[MovDir[x][y]].dx;
6475 yy = y + move_xy[MovDir[x][y]].dy;
6477 if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
6478 MovDir[x][y] = old_move_dir;
6482 else if (element == EL_MOLE)
6484 boolean can_move_on =
6485 (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
6486 IS_AMOEBOID(Feld[move_x][move_y]) ||
6487 Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
6490 boolean can_turn_left =
6491 (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
6492 IS_AMOEBOID(Feld[left_x][left_y])));
6494 boolean can_turn_right =
6495 (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
6496 IS_AMOEBOID(Feld[right_x][right_y])));
6498 if (can_turn_left && can_turn_right)
6499 MovDir[x][y] = (RND(2) ? left_dir : right_dir);
6500 else if (can_turn_left)
6501 MovDir[x][y] = left_dir;
6503 MovDir[x][y] = right_dir;
6506 if (MovDir[x][y] != old_move_dir)
6509 else if (element == EL_BALLOON)
6511 MovDir[x][y] = game.wind_direction;
6514 else if (element == EL_SPRING)
6516 if (MovDir[x][y] & MV_HORIZONTAL)
6518 if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
6519 !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
6521 Feld[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
6522 ResetGfxAnimation(move_x, move_y);
6523 TEST_DrawLevelField(move_x, move_y);
6525 MovDir[x][y] = back_dir;
6527 else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
6528 SPRING_CAN_ENTER_FIELD(element, x, y + 1))
6529 MovDir[x][y] = MV_NONE;
6534 else if (element == EL_ROBOT ||
6535 element == EL_SATELLITE ||
6536 element == EL_PENGUIN ||
6537 element == EL_EMC_ANDROID)
6539 int attr_x = -1, attr_y = -1;
6550 for (i = 0; i < MAX_PLAYERS; i++)
6552 struct PlayerInfo *player = &stored_player[i];
6553 int jx = player->jx, jy = player->jy;
6555 if (!player->active)
6559 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
6567 if (element == EL_ROBOT && ZX >= 0 && ZY >= 0 &&
6568 (Feld[ZX][ZY] == EL_ROBOT_WHEEL_ACTIVE ||
6569 game.engine_version < VERSION_IDENT(3,1,0,0)))
6575 if (element == EL_PENGUIN)
6578 static int xy[4][2] =
6586 for (i = 0; i < NUM_DIRECTIONS; i++)
6588 int ex = x + xy[i][0];
6589 int ey = y + xy[i][1];
6591 if (IN_LEV_FIELD(ex, ey) && (Feld[ex][ey] == EL_EXIT_OPEN ||
6592 Feld[ex][ey] == EL_EM_EXIT_OPEN ||
6593 Feld[ex][ey] == EL_STEEL_EXIT_OPEN ||
6594 Feld[ex][ey] == EL_EM_STEEL_EXIT_OPEN))
6603 MovDir[x][y] = MV_NONE;
6605 MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
6606 else if (attr_x > x)
6607 MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
6609 MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
6610 else if (attr_y > y)
6611 MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
6613 if (element == EL_ROBOT)
6617 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6618 MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
6619 Moving2Blocked(x, y, &newx, &newy);
6621 if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
6622 MovDelay[x][y] = 8 + 8 * !RND(3);
6624 MovDelay[x][y] = 16;
6626 else if (element == EL_PENGUIN)
6632 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6634 boolean first_horiz = RND(2);
6635 int new_move_dir = MovDir[x][y];
6638 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6639 Moving2Blocked(x, y, &newx, &newy);
6641 if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
6645 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6646 Moving2Blocked(x, y, &newx, &newy);
6648 if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
6651 MovDir[x][y] = old_move_dir;
6655 else if (element == EL_SATELLITE)
6661 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6663 boolean first_horiz = RND(2);
6664 int new_move_dir = MovDir[x][y];
6667 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6668 Moving2Blocked(x, y, &newx, &newy);
6670 if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
6674 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6675 Moving2Blocked(x, y, &newx, &newy);
6677 if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
6680 MovDir[x][y] = old_move_dir;
6684 else if (element == EL_EMC_ANDROID)
6686 static int check_pos[16] =
6688 -1, /* 0 => (invalid) */
6689 7, /* 1 => MV_LEFT */
6690 3, /* 2 => MV_RIGHT */
6691 -1, /* 3 => (invalid) */
6693 0, /* 5 => MV_LEFT | MV_UP */
6694 2, /* 6 => MV_RIGHT | MV_UP */
6695 -1, /* 7 => (invalid) */
6696 5, /* 8 => MV_DOWN */
6697 6, /* 9 => MV_LEFT | MV_DOWN */
6698 4, /* 10 => MV_RIGHT | MV_DOWN */
6699 -1, /* 11 => (invalid) */
6700 -1, /* 12 => (invalid) */
6701 -1, /* 13 => (invalid) */
6702 -1, /* 14 => (invalid) */
6703 -1, /* 15 => (invalid) */
6711 { -1, -1, MV_LEFT | MV_UP },
6713 { +1, -1, MV_RIGHT | MV_UP },
6714 { +1, 0, MV_RIGHT },
6715 { +1, +1, MV_RIGHT | MV_DOWN },
6717 { -1, +1, MV_LEFT | MV_DOWN },
6720 int start_pos, check_order;
6721 boolean can_clone = FALSE;
6724 /* check if there is any free field around current position */
6725 for (i = 0; i < 8; i++)
6727 int newx = x + check_xy[i].dx;
6728 int newy = y + check_xy[i].dy;
6730 if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
6738 if (can_clone) /* randomly find an element to clone */
6742 start_pos = check_pos[RND(8)];
6743 check_order = (RND(2) ? -1 : +1);
6745 for (i = 0; i < 8; i++)
6747 int pos_raw = start_pos + i * check_order;
6748 int pos = (pos_raw + 8) % 8;
6749 int newx = x + check_xy[pos].dx;
6750 int newy = y + check_xy[pos].dy;
6752 if (ANDROID_CAN_CLONE_FIELD(newx, newy))
6754 element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
6755 element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
6757 Store[x][y] = Feld[newx][newy];
6766 if (can_clone) /* randomly find a direction to move */
6770 start_pos = check_pos[RND(8)];
6771 check_order = (RND(2) ? -1 : +1);
6773 for (i = 0; i < 8; i++)
6775 int pos_raw = start_pos + i * check_order;
6776 int pos = (pos_raw + 8) % 8;
6777 int newx = x + check_xy[pos].dx;
6778 int newy = y + check_xy[pos].dy;
6779 int new_move_dir = check_xy[pos].dir;
6781 if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
6783 MovDir[x][y] = new_move_dir;
6784 MovDelay[x][y] = level.android_clone_time * 8 + 1;
6793 if (can_clone) /* cloning and moving successful */
6796 /* cannot clone -- try to move towards player */
6798 start_pos = check_pos[MovDir[x][y] & 0x0f];
6799 check_order = (RND(2) ? -1 : +1);
6801 for (i = 0; i < 3; i++)
6803 /* first check start_pos, then previous/next or (next/previous) pos */
6804 int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
6805 int pos = (pos_raw + 8) % 8;
6806 int newx = x + check_xy[pos].dx;
6807 int newy = y + check_xy[pos].dy;
6808 int new_move_dir = check_xy[pos].dir;
6810 if (IS_PLAYER(newx, newy))
6813 if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
6815 MovDir[x][y] = new_move_dir;
6816 MovDelay[x][y] = level.android_move_time * 8 + 1;
6823 else if (move_pattern == MV_TURNING_LEFT ||
6824 move_pattern == MV_TURNING_RIGHT ||
6825 move_pattern == MV_TURNING_LEFT_RIGHT ||
6826 move_pattern == MV_TURNING_RIGHT_LEFT ||
6827 move_pattern == MV_TURNING_RANDOM ||
6828 move_pattern == MV_ALL_DIRECTIONS)
6830 boolean can_turn_left =
6831 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
6832 boolean can_turn_right =
6833 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
6835 if (element_info[element].move_stepsize == 0) /* "not moving" */
6838 if (move_pattern == MV_TURNING_LEFT)
6839 MovDir[x][y] = left_dir;
6840 else if (move_pattern == MV_TURNING_RIGHT)
6841 MovDir[x][y] = right_dir;
6842 else if (move_pattern == MV_TURNING_LEFT_RIGHT)
6843 MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
6844 else if (move_pattern == MV_TURNING_RIGHT_LEFT)
6845 MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
6846 else if (move_pattern == MV_TURNING_RANDOM)
6847 MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
6848 can_turn_right && !can_turn_left ? right_dir :
6849 RND(2) ? left_dir : right_dir);
6850 else if (can_turn_left && can_turn_right)
6851 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6852 else if (can_turn_left)
6853 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6854 else if (can_turn_right)
6855 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6857 MovDir[x][y] = back_dir;
6859 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6861 else if (move_pattern == MV_HORIZONTAL ||
6862 move_pattern == MV_VERTICAL)
6864 if (move_pattern & old_move_dir)
6865 MovDir[x][y] = back_dir;
6866 else if (move_pattern == MV_HORIZONTAL)
6867 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
6868 else if (move_pattern == MV_VERTICAL)
6869 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
6871 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6873 else if (move_pattern & MV_ANY_DIRECTION)
6875 MovDir[x][y] = move_pattern;
6876 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6878 else if (move_pattern & MV_WIND_DIRECTION)
6880 MovDir[x][y] = game.wind_direction;
6881 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6883 else if (move_pattern == MV_ALONG_LEFT_SIDE)
6885 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
6886 MovDir[x][y] = left_dir;
6887 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
6888 MovDir[x][y] = right_dir;
6890 if (MovDir[x][y] != old_move_dir)
6891 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6893 else if (move_pattern == MV_ALONG_RIGHT_SIDE)
6895 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
6896 MovDir[x][y] = right_dir;
6897 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
6898 MovDir[x][y] = left_dir;
6900 if (MovDir[x][y] != old_move_dir)
6901 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6903 else if (move_pattern == MV_TOWARDS_PLAYER ||
6904 move_pattern == MV_AWAY_FROM_PLAYER)
6906 int attr_x = -1, attr_y = -1;
6908 boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
6919 for (i = 0; i < MAX_PLAYERS; i++)
6921 struct PlayerInfo *player = &stored_player[i];
6922 int jx = player->jx, jy = player->jy;
6924 if (!player->active)
6928 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
6936 MovDir[x][y] = MV_NONE;
6938 MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
6939 else if (attr_x > x)
6940 MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
6942 MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
6943 else if (attr_y > y)
6944 MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
6946 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6948 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6950 boolean first_horiz = RND(2);
6951 int new_move_dir = MovDir[x][y];
6953 if (element_info[element].move_stepsize == 0) /* "not moving" */
6955 first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
6956 MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6962 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6963 Moving2Blocked(x, y, &newx, &newy);
6965 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
6969 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6970 Moving2Blocked(x, y, &newx, &newy);
6972 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
6975 MovDir[x][y] = old_move_dir;
6978 else if (move_pattern == MV_WHEN_PUSHED ||
6979 move_pattern == MV_WHEN_DROPPED)
6981 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
6982 MovDir[x][y] = MV_NONE;
6986 else if (move_pattern & MV_MAZE_RUNNER_STYLE)
6988 static int test_xy[7][2] =
6998 static int test_dir[7] =
7008 boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
7009 int move_preference = -1000000; /* start with very low preference */
7010 int new_move_dir = MV_NONE;
7011 int start_test = RND(4);
7014 for (i = 0; i < NUM_DIRECTIONS; i++)
7016 int move_dir = test_dir[start_test + i];
7017 int move_dir_preference;
7019 xx = x + test_xy[start_test + i][0];
7020 yy = y + test_xy[start_test + i][1];
7022 if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
7023 (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
7025 new_move_dir = move_dir;
7030 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
7033 move_dir_preference = -1 * RunnerVisit[xx][yy];
7034 if (hunter_mode && PlayerVisit[xx][yy] > 0)
7035 move_dir_preference = PlayerVisit[xx][yy];
7037 if (move_dir_preference > move_preference)
7039 /* prefer field that has not been visited for the longest time */
7040 move_preference = move_dir_preference;
7041 new_move_dir = move_dir;
7043 else if (move_dir_preference == move_preference &&
7044 move_dir == old_move_dir)
7046 /* prefer last direction when all directions are preferred equally */
7047 move_preference = move_dir_preference;
7048 new_move_dir = move_dir;
7052 MovDir[x][y] = new_move_dir;
7053 if (old_move_dir != new_move_dir)
7054 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7058 static void TurnRound(int x, int y)
7060 int direction = MovDir[x][y];
7064 GfxDir[x][y] = MovDir[x][y];
7066 if (direction != MovDir[x][y])
7070 GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
7072 ResetGfxFrame(x, y, FALSE);
7075 static boolean JustBeingPushed(int x, int y)
7079 for (i = 0; i < MAX_PLAYERS; i++)
7081 struct PlayerInfo *player = &stored_player[i];
7083 if (player->active && player->is_pushing && player->MovPos)
7085 int next_jx = player->jx + (player->jx - player->last_jx);
7086 int next_jy = player->jy + (player->jy - player->last_jy);
7088 if (x == next_jx && y == next_jy)
7096 void StartMoving(int x, int y)
7098 boolean started_moving = FALSE; /* some elements can fall _and_ move */
7099 int element = Feld[x][y];
7104 if (MovDelay[x][y] == 0)
7105 GfxAction[x][y] = ACTION_DEFAULT;
7107 if (CAN_FALL(element) && y < lev_fieldy - 1)
7109 if ((x > 0 && IS_PLAYER(x - 1, y)) ||
7110 (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
7111 if (JustBeingPushed(x, y))
7114 if (element == EL_QUICKSAND_FULL)
7116 if (IS_FREE(x, y + 1))
7118 InitMovingField(x, y, MV_DOWN);
7119 started_moving = TRUE;
7121 Feld[x][y] = EL_QUICKSAND_EMPTYING;
7122 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7123 if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7124 Store[x][y] = EL_ROCK;
7126 Store[x][y] = EL_ROCK;
7129 PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7131 else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7133 if (!MovDelay[x][y])
7135 MovDelay[x][y] = TILEY + 1;
7137 ResetGfxAnimation(x, y);
7138 ResetGfxAnimation(x, y + 1);
7143 DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7144 DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7151 Feld[x][y] = EL_QUICKSAND_EMPTY;
7152 Feld[x][y + 1] = EL_QUICKSAND_FULL;
7153 Store[x][y + 1] = Store[x][y];
7156 PlayLevelSoundAction(x, y, ACTION_FILLING);
7158 else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7160 if (!MovDelay[x][y])
7162 MovDelay[x][y] = TILEY + 1;
7164 ResetGfxAnimation(x, y);
7165 ResetGfxAnimation(x, y + 1);
7170 DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7171 DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7178 Feld[x][y] = EL_QUICKSAND_EMPTY;
7179 Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7180 Store[x][y + 1] = Store[x][y];
7183 PlayLevelSoundAction(x, y, ACTION_FILLING);
7186 else if (element == EL_QUICKSAND_FAST_FULL)
7188 if (IS_FREE(x, y + 1))
7190 InitMovingField(x, y, MV_DOWN);
7191 started_moving = TRUE;
7193 Feld[x][y] = EL_QUICKSAND_FAST_EMPTYING;
7194 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7195 if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7196 Store[x][y] = EL_ROCK;
7198 Store[x][y] = EL_ROCK;
7201 PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7203 else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7205 if (!MovDelay[x][y])
7207 MovDelay[x][y] = TILEY + 1;
7209 ResetGfxAnimation(x, y);
7210 ResetGfxAnimation(x, y + 1);
7215 DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7216 DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7223 Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
7224 Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7225 Store[x][y + 1] = Store[x][y];
7228 PlayLevelSoundAction(x, y, ACTION_FILLING);
7230 else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7232 if (!MovDelay[x][y])
7234 MovDelay[x][y] = TILEY + 1;
7236 ResetGfxAnimation(x, y);
7237 ResetGfxAnimation(x, y + 1);
7242 DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7243 DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7250 Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
7251 Feld[x][y + 1] = EL_QUICKSAND_FULL;
7252 Store[x][y + 1] = Store[x][y];
7255 PlayLevelSoundAction(x, y, ACTION_FILLING);
7258 else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7259 Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7261 InitMovingField(x, y, MV_DOWN);
7262 started_moving = TRUE;
7264 Feld[x][y] = EL_QUICKSAND_FILLING;
7265 Store[x][y] = element;
7267 PlayLevelSoundAction(x, y, ACTION_FILLING);
7269 else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7270 Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7272 InitMovingField(x, y, MV_DOWN);
7273 started_moving = TRUE;
7275 Feld[x][y] = EL_QUICKSAND_FAST_FILLING;
7276 Store[x][y] = element;
7278 PlayLevelSoundAction(x, y, ACTION_FILLING);
7280 else if (element == EL_MAGIC_WALL_FULL)
7282 if (IS_FREE(x, y + 1))
7284 InitMovingField(x, y, MV_DOWN);
7285 started_moving = TRUE;
7287 Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
7288 Store[x][y] = EL_CHANGED(Store[x][y]);
7290 else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
7292 if (!MovDelay[x][y])
7293 MovDelay[x][y] = TILEY / 4 + 1;
7302 Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
7303 Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
7304 Store[x][y + 1] = EL_CHANGED(Store[x][y]);
7308 else if (element == EL_BD_MAGIC_WALL_FULL)
7310 if (IS_FREE(x, y + 1))
7312 InitMovingField(x, y, MV_DOWN);
7313 started_moving = TRUE;
7315 Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
7316 Store[x][y] = EL_CHANGED_BD(Store[x][y]);
7318 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
7320 if (!MovDelay[x][y])
7321 MovDelay[x][y] = TILEY / 4 + 1;
7330 Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
7331 Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
7332 Store[x][y + 1] = EL_CHANGED_BD(Store[x][y]);
7336 else if (element == EL_DC_MAGIC_WALL_FULL)
7338 if (IS_FREE(x, y + 1))
7340 InitMovingField(x, y, MV_DOWN);
7341 started_moving = TRUE;
7343 Feld[x][y] = EL_DC_MAGIC_WALL_EMPTYING;
7344 Store[x][y] = EL_CHANGED_DC(Store[x][y]);
7346 else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
7348 if (!MovDelay[x][y])
7349 MovDelay[x][y] = TILEY / 4 + 1;
7358 Feld[x][y] = EL_DC_MAGIC_WALL_ACTIVE;
7359 Feld[x][y + 1] = EL_DC_MAGIC_WALL_FULL;
7360 Store[x][y + 1] = EL_CHANGED_DC(Store[x][y]);
7364 else if ((CAN_PASS_MAGIC_WALL(element) &&
7365 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
7366 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)) ||
7367 (CAN_PASS_DC_MAGIC_WALL(element) &&
7368 (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)))
7371 InitMovingField(x, y, MV_DOWN);
7372 started_moving = TRUE;
7375 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
7376 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ? EL_BD_MAGIC_WALL_FILLING :
7377 EL_DC_MAGIC_WALL_FILLING);
7378 Store[x][y] = element;
7380 else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
7382 SplashAcid(x, y + 1);
7384 InitMovingField(x, y, MV_DOWN);
7385 started_moving = TRUE;
7387 Store[x][y] = EL_ACID;
7390 (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7391 CheckImpact[x][y] && !IS_FREE(x, y + 1)) ||
7392 (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
7393 CAN_FALL(element) && WasJustFalling[x][y] &&
7394 (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
7396 (game.engine_version < VERSION_IDENT(2,2,0,7) &&
7397 CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
7398 (Feld[x][y + 1] == EL_BLOCKED)))
7400 /* this is needed for a special case not covered by calling "Impact()"
7401 from "ContinueMoving()": if an element moves to a tile directly below
7402 another element which was just falling on that tile (which was empty
7403 in the previous frame), the falling element above would just stop
7404 instead of smashing the element below (in previous version, the above
7405 element was just checked for "moving" instead of "falling", resulting
7406 in incorrect smashes caused by horizontal movement of the above
7407 element; also, the case of the player being the element to smash was
7408 simply not covered here... :-/ ) */
7410 CheckCollision[x][y] = 0;
7411 CheckImpact[x][y] = 0;
7415 else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
7417 if (MovDir[x][y] == MV_NONE)
7419 InitMovingField(x, y, MV_DOWN);
7420 started_moving = TRUE;
7423 else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
7425 if (WasJustFalling[x][y]) /* prevent animation from being restarted */
7426 MovDir[x][y] = MV_DOWN;
7428 InitMovingField(x, y, MV_DOWN);
7429 started_moving = TRUE;
7431 else if (element == EL_AMOEBA_DROP)
7433 Feld[x][y] = EL_AMOEBA_GROWING;
7434 Store[x][y] = EL_AMOEBA_WET;
7436 else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
7437 (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
7438 !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
7439 element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
7441 boolean can_fall_left = (x > 0 && IS_FREE(x - 1, y) &&
7442 (IS_FREE(x - 1, y + 1) ||
7443 Feld[x - 1][y + 1] == EL_ACID));
7444 boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
7445 (IS_FREE(x + 1, y + 1) ||
7446 Feld[x + 1][y + 1] == EL_ACID));
7447 boolean can_fall_any = (can_fall_left || can_fall_right);
7448 boolean can_fall_both = (can_fall_left && can_fall_right);
7449 int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
7451 if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
7453 if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
7454 can_fall_right = FALSE;
7455 else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
7456 can_fall_left = FALSE;
7457 else if (slippery_type == SLIPPERY_ONLY_LEFT)
7458 can_fall_right = FALSE;
7459 else if (slippery_type == SLIPPERY_ONLY_RIGHT)
7460 can_fall_left = FALSE;
7462 can_fall_any = (can_fall_left || can_fall_right);
7463 can_fall_both = FALSE;
7468 if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
7469 can_fall_right = FALSE; /* slip down on left side */
7471 can_fall_left = !(can_fall_right = RND(2));
7473 can_fall_both = FALSE;
7478 /* if not determined otherwise, prefer left side for slipping down */
7479 InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
7480 started_moving = TRUE;
7483 else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
7485 boolean left_is_free = (x > 0 && IS_FREE(x - 1, y));
7486 boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
7487 int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
7488 int belt_dir = game.belt_dir[belt_nr];
7490 if ((belt_dir == MV_LEFT && left_is_free) ||
7491 (belt_dir == MV_RIGHT && right_is_free))
7493 int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
7495 InitMovingField(x, y, belt_dir);
7496 started_moving = TRUE;
7498 Pushed[x][y] = TRUE;
7499 Pushed[nextx][y] = TRUE;
7501 GfxAction[x][y] = ACTION_DEFAULT;
7505 MovDir[x][y] = 0; /* if element was moving, stop it */
7510 /* not "else if" because of elements that can fall and move (EL_SPRING) */
7511 if (CAN_MOVE(element) && !started_moving)
7513 int move_pattern = element_info[element].move_pattern;
7516 Moving2Blocked(x, y, &newx, &newy);
7518 if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
7521 if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7522 CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7524 WasJustMoving[x][y] = 0;
7525 CheckCollision[x][y] = 0;
7527 TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
7529 if (Feld[x][y] != element) /* element has changed */
7533 if (!MovDelay[x][y]) /* start new movement phase */
7535 /* all objects that can change their move direction after each step
7536 (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
7538 if (element != EL_YAMYAM &&
7539 element != EL_DARK_YAMYAM &&
7540 element != EL_PACMAN &&
7541 !(move_pattern & MV_ANY_DIRECTION) &&
7542 move_pattern != MV_TURNING_LEFT &&
7543 move_pattern != MV_TURNING_RIGHT &&
7544 move_pattern != MV_TURNING_LEFT_RIGHT &&
7545 move_pattern != MV_TURNING_RIGHT_LEFT &&
7546 move_pattern != MV_TURNING_RANDOM)
7550 if (MovDelay[x][y] && (element == EL_BUG ||
7551 element == EL_SPACESHIP ||
7552 element == EL_SP_SNIKSNAK ||
7553 element == EL_SP_ELECTRON ||
7554 element == EL_MOLE))
7555 TEST_DrawLevelField(x, y);
7559 if (MovDelay[x][y]) /* wait some time before next movement */
7563 if (element == EL_ROBOT ||
7564 element == EL_YAMYAM ||
7565 element == EL_DARK_YAMYAM)
7567 DrawLevelElementAnimationIfNeeded(x, y, element);
7568 PlayLevelSoundAction(x, y, ACTION_WAITING);
7570 else if (element == EL_SP_ELECTRON)
7571 DrawLevelElementAnimationIfNeeded(x, y, element);
7572 else if (element == EL_DRAGON)
7575 int dir = MovDir[x][y];
7576 int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
7577 int dy = (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
7578 int graphic = (dir == MV_LEFT ? IMG_FLAMES_1_LEFT :
7579 dir == MV_RIGHT ? IMG_FLAMES_1_RIGHT :
7580 dir == MV_UP ? IMG_FLAMES_1_UP :
7581 dir == MV_DOWN ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
7582 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
7584 GfxAction[x][y] = ACTION_ATTACKING;
7586 if (IS_PLAYER(x, y))
7587 DrawPlayerField(x, y);
7589 TEST_DrawLevelField(x, y);
7591 PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
7593 for (i = 1; i <= 3; i++)
7595 int xx = x + i * dx;
7596 int yy = y + i * dy;
7597 int sx = SCREENX(xx);
7598 int sy = SCREENY(yy);
7599 int flame_graphic = graphic + (i - 1);
7601 if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
7606 int flamed = MovingOrBlocked2Element(xx, yy);
7608 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
7611 RemoveMovingField(xx, yy);
7613 ChangeDelay[xx][yy] = 0;
7615 Feld[xx][yy] = EL_FLAMES;
7617 if (IN_SCR_FIELD(sx, sy))
7619 TEST_DrawLevelFieldCrumbled(xx, yy);
7620 DrawGraphic(sx, sy, flame_graphic, frame);
7625 if (Feld[xx][yy] == EL_FLAMES)
7626 Feld[xx][yy] = EL_EMPTY;
7627 TEST_DrawLevelField(xx, yy);
7632 if (MovDelay[x][y]) /* element still has to wait some time */
7634 PlayLevelSoundAction(x, y, ACTION_WAITING);
7640 /* now make next step */
7642 Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
7644 if (DONT_COLLIDE_WITH(element) &&
7645 IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
7646 !PLAYER_ENEMY_PROTECTED(newx, newy))
7648 TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
7653 else if (CAN_MOVE_INTO_ACID(element) &&
7654 IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID &&
7655 !IS_MV_DIAGONAL(MovDir[x][y]) &&
7656 (MovDir[x][y] == MV_DOWN ||
7657 game.engine_version >= VERSION_IDENT(3,1,0,0)))
7659 SplashAcid(newx, newy);
7660 Store[x][y] = EL_ACID;
7662 else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
7664 if (Feld[newx][newy] == EL_EXIT_OPEN ||
7665 Feld[newx][newy] == EL_EM_EXIT_OPEN ||
7666 Feld[newx][newy] == EL_STEEL_EXIT_OPEN ||
7667 Feld[newx][newy] == EL_EM_STEEL_EXIT_OPEN)
7670 TEST_DrawLevelField(x, y);
7672 PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
7673 if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
7674 DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
7676 local_player->friends_still_needed--;
7677 if (!local_player->friends_still_needed &&
7678 !local_player->GameOver && AllPlayersGone)
7679 PlayerWins(local_player);
7683 else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
7685 if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MP_MOVING)
7686 TEST_DrawLevelField(newx, newy);
7688 GfxDir[x][y] = MovDir[x][y] = MV_NONE;
7690 else if (!IS_FREE(newx, newy))
7692 GfxAction[x][y] = ACTION_WAITING;
7694 if (IS_PLAYER(x, y))
7695 DrawPlayerField(x, y);
7697 TEST_DrawLevelField(x, y);
7702 else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
7704 if (IS_FOOD_PIG(Feld[newx][newy]))
7706 if (IS_MOVING(newx, newy))
7707 RemoveMovingField(newx, newy);
7710 Feld[newx][newy] = EL_EMPTY;
7711 TEST_DrawLevelField(newx, newy);
7714 PlayLevelSound(x, y, SND_PIG_DIGGING);
7716 else if (!IS_FREE(newx, newy))
7718 if (IS_PLAYER(x, y))
7719 DrawPlayerField(x, y);
7721 TEST_DrawLevelField(x, y);
7726 else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
7728 if (Store[x][y] != EL_EMPTY)
7730 boolean can_clone = FALSE;
7733 /* check if element to clone is still there */
7734 for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
7736 if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == Store[x][y])
7744 /* cannot clone or target field not free anymore -- do not clone */
7745 if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7746 Store[x][y] = EL_EMPTY;
7749 if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7751 if (IS_MV_DIAGONAL(MovDir[x][y]))
7753 int diagonal_move_dir = MovDir[x][y];
7754 int stored = Store[x][y];
7755 int change_delay = 8;
7758 /* android is moving diagonally */
7760 CreateField(x, y, EL_DIAGONAL_SHRINKING);
7762 Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
7763 GfxElement[x][y] = EL_EMC_ANDROID;
7764 GfxAction[x][y] = ACTION_SHRINKING;
7765 GfxDir[x][y] = diagonal_move_dir;
7766 ChangeDelay[x][y] = change_delay;
7768 graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
7771 DrawLevelGraphicAnimation(x, y, graphic);
7772 PlayLevelSoundAction(x, y, ACTION_SHRINKING);
7774 if (Feld[newx][newy] == EL_ACID)
7776 SplashAcid(newx, newy);
7781 CreateField(newx, newy, EL_DIAGONAL_GROWING);
7783 Store[newx][newy] = EL_EMC_ANDROID;
7784 GfxElement[newx][newy] = EL_EMC_ANDROID;
7785 GfxAction[newx][newy] = ACTION_GROWING;
7786 GfxDir[newx][newy] = diagonal_move_dir;
7787 ChangeDelay[newx][newy] = change_delay;
7789 graphic = el_act_dir2img(GfxElement[newx][newy],
7790 GfxAction[newx][newy], GfxDir[newx][newy]);
7792 DrawLevelGraphicAnimation(newx, newy, graphic);
7793 PlayLevelSoundAction(newx, newy, ACTION_GROWING);
7799 Feld[newx][newy] = EL_EMPTY;
7800 TEST_DrawLevelField(newx, newy);
7802 PlayLevelSoundAction(x, y, ACTION_DIGGING);
7805 else if (!IS_FREE(newx, newy))
7810 else if (IS_CUSTOM_ELEMENT(element) &&
7811 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7813 if (!DigFieldByCE(newx, newy, element))
7816 if (move_pattern & MV_MAZE_RUNNER_STYLE)
7818 RunnerVisit[x][y] = FrameCounter;
7819 PlayerVisit[x][y] /= 8; /* expire player visit path */
7822 else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
7824 if (!IS_FREE(newx, newy))
7826 if (IS_PLAYER(x, y))
7827 DrawPlayerField(x, y);
7829 TEST_DrawLevelField(x, y);
7835 boolean wanna_flame = !RND(10);
7836 int dx = newx - x, dy = newy - y;
7837 int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
7838 int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
7839 int element1 = (IN_LEV_FIELD(newx1, newy1) ?
7840 MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
7841 int element2 = (IN_LEV_FIELD(newx2, newy2) ?
7842 MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
7845 IS_CLASSIC_ENEMY(element1) ||
7846 IS_CLASSIC_ENEMY(element2)) &&
7847 element1 != EL_DRAGON && element2 != EL_DRAGON &&
7848 element1 != EL_FLAMES && element2 != EL_FLAMES)
7850 ResetGfxAnimation(x, y);
7851 GfxAction[x][y] = ACTION_ATTACKING;
7853 if (IS_PLAYER(x, y))
7854 DrawPlayerField(x, y);
7856 TEST_DrawLevelField(x, y);
7858 PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
7860 MovDelay[x][y] = 50;
7862 Feld[newx][newy] = EL_FLAMES;
7863 if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
7864 Feld[newx1][newy1] = EL_FLAMES;
7865 if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
7866 Feld[newx2][newy2] = EL_FLAMES;
7872 else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
7873 Feld[newx][newy] == EL_DIAMOND)
7875 if (IS_MOVING(newx, newy))
7876 RemoveMovingField(newx, newy);
7879 Feld[newx][newy] = EL_EMPTY;
7880 TEST_DrawLevelField(newx, newy);
7883 PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
7885 else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
7886 IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
7888 if (AmoebaNr[newx][newy])
7890 AmoebaCnt2[AmoebaNr[newx][newy]]--;
7891 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
7892 Feld[newx][newy] == EL_BD_AMOEBA)
7893 AmoebaCnt[AmoebaNr[newx][newy]]--;
7896 if (IS_MOVING(newx, newy))
7898 RemoveMovingField(newx, newy);
7902 Feld[newx][newy] = EL_EMPTY;
7903 TEST_DrawLevelField(newx, newy);
7906 PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
7908 else if ((element == EL_PACMAN || element == EL_MOLE)
7909 && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
7911 if (AmoebaNr[newx][newy])
7913 AmoebaCnt2[AmoebaNr[newx][newy]]--;
7914 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
7915 Feld[newx][newy] == EL_BD_AMOEBA)
7916 AmoebaCnt[AmoebaNr[newx][newy]]--;
7919 if (element == EL_MOLE)
7921 Feld[newx][newy] = EL_AMOEBA_SHRINKING;
7922 PlayLevelSound(x, y, SND_MOLE_DIGGING);
7924 ResetGfxAnimation(x, y);
7925 GfxAction[x][y] = ACTION_DIGGING;
7926 TEST_DrawLevelField(x, y);
7928 MovDelay[newx][newy] = 0; /* start amoeba shrinking delay */
7930 return; /* wait for shrinking amoeba */
7932 else /* element == EL_PACMAN */
7934 Feld[newx][newy] = EL_EMPTY;
7935 TEST_DrawLevelField(newx, newy);
7936 PlayLevelSound(x, y, SND_PACMAN_DIGGING);
7939 else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
7940 (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
7941 (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
7943 /* wait for shrinking amoeba to completely disappear */
7946 else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
7948 /* object was running against a wall */
7952 if (GFX_ELEMENT(element) != EL_SAND) /* !!! FIX THIS (crumble) !!! */
7953 DrawLevelElementAnimation(x, y, element);
7955 if (DONT_TOUCH(element))
7956 TestIfBadThingTouchesPlayer(x, y);
7961 InitMovingField(x, y, MovDir[x][y]);
7963 PlayLevelSoundAction(x, y, ACTION_MOVING);
7967 ContinueMoving(x, y);
7970 void ContinueMoving(int x, int y)
7972 int element = Feld[x][y];
7973 struct ElementInfo *ei = &element_info[element];
7974 int direction = MovDir[x][y];
7975 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
7976 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
7977 int newx = x + dx, newy = y + dy;
7978 int stored = Store[x][y];
7979 int stored_new = Store[newx][newy];
7980 boolean pushed_by_player = (Pushed[x][y] && IS_PLAYER(x, y));
7981 boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
7982 boolean last_line = (newy == lev_fieldy - 1);
7984 MovPos[x][y] += getElementMoveStepsize(x, y);
7986 if (pushed_by_player) /* special case: moving object pushed by player */
7987 MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
7989 if (ABS(MovPos[x][y]) < TILEX)
7991 TEST_DrawLevelField(x, y);
7993 return; /* element is still moving */
7996 /* element reached destination field */
7998 Feld[x][y] = EL_EMPTY;
7999 Feld[newx][newy] = element;
8000 MovPos[x][y] = 0; /* force "not moving" for "crumbled sand" */
8002 if (Store[x][y] == EL_ACID) /* element is moving into acid pool */
8004 element = Feld[newx][newy] = EL_ACID;
8006 else if (element == EL_MOLE)
8008 Feld[x][y] = EL_SAND;
8010 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8012 else if (element == EL_QUICKSAND_FILLING)
8014 element = Feld[newx][newy] = get_next_element(element);
8015 Store[newx][newy] = Store[x][y];
8017 else if (element == EL_QUICKSAND_EMPTYING)
8019 Feld[x][y] = get_next_element(element);
8020 element = Feld[newx][newy] = Store[x][y];
8022 else if (element == EL_QUICKSAND_FAST_FILLING)
8024 element = Feld[newx][newy] = get_next_element(element);
8025 Store[newx][newy] = Store[x][y];
8027 else if (element == EL_QUICKSAND_FAST_EMPTYING)
8029 Feld[x][y] = get_next_element(element);
8030 element = Feld[newx][newy] = Store[x][y];
8032 else if (element == EL_MAGIC_WALL_FILLING)
8034 element = Feld[newx][newy] = get_next_element(element);
8035 if (!game.magic_wall_active)
8036 element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
8037 Store[newx][newy] = Store[x][y];
8039 else if (element == EL_MAGIC_WALL_EMPTYING)
8041 Feld[x][y] = get_next_element(element);
8042 if (!game.magic_wall_active)
8043 Feld[x][y] = EL_MAGIC_WALL_DEAD;
8044 element = Feld[newx][newy] = Store[x][y];
8046 InitField(newx, newy, FALSE);
8048 else if (element == EL_BD_MAGIC_WALL_FILLING)
8050 element = Feld[newx][newy] = get_next_element(element);
8051 if (!game.magic_wall_active)
8052 element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
8053 Store[newx][newy] = Store[x][y];
8055 else if (element == EL_BD_MAGIC_WALL_EMPTYING)
8057 Feld[x][y] = get_next_element(element);
8058 if (!game.magic_wall_active)
8059 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
8060 element = Feld[newx][newy] = Store[x][y];
8062 InitField(newx, newy, FALSE);
8064 else if (element == EL_DC_MAGIC_WALL_FILLING)
8066 element = Feld[newx][newy] = get_next_element(element);
8067 if (!game.magic_wall_active)
8068 element = Feld[newx][newy] = EL_DC_MAGIC_WALL_DEAD;
8069 Store[newx][newy] = Store[x][y];
8071 else if (element == EL_DC_MAGIC_WALL_EMPTYING)
8073 Feld[x][y] = get_next_element(element);
8074 if (!game.magic_wall_active)
8075 Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
8076 element = Feld[newx][newy] = Store[x][y];
8078 InitField(newx, newy, FALSE);
8080 else if (element == EL_AMOEBA_DROPPING)
8082 Feld[x][y] = get_next_element(element);
8083 element = Feld[newx][newy] = Store[x][y];
8085 else if (element == EL_SOKOBAN_OBJECT)
8088 Feld[x][y] = Back[x][y];
8090 if (Back[newx][newy])
8091 Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
8093 Back[x][y] = Back[newx][newy] = 0;
8096 Store[x][y] = EL_EMPTY;
8101 MovDelay[newx][newy] = 0;
8103 if (CAN_CHANGE_OR_HAS_ACTION(element))
8105 /* copy element change control values to new field */
8106 ChangeDelay[newx][newy] = ChangeDelay[x][y];
8107 ChangePage[newx][newy] = ChangePage[x][y];
8108 ChangeCount[newx][newy] = ChangeCount[x][y];
8109 ChangeEvent[newx][newy] = ChangeEvent[x][y];
8112 CustomValue[newx][newy] = CustomValue[x][y];
8114 ChangeDelay[x][y] = 0;
8115 ChangePage[x][y] = -1;
8116 ChangeCount[x][y] = 0;
8117 ChangeEvent[x][y] = -1;
8119 CustomValue[x][y] = 0;
8121 /* copy animation control values to new field */
8122 GfxFrame[newx][newy] = GfxFrame[x][y];
8123 GfxRandom[newx][newy] = GfxRandom[x][y]; /* keep same random value */
8124 GfxAction[newx][newy] = GfxAction[x][y]; /* keep action one frame */
8125 GfxDir[newx][newy] = GfxDir[x][y]; /* keep element direction */
8127 Pushed[x][y] = Pushed[newx][newy] = FALSE;
8129 /* some elements can leave other elements behind after moving */
8130 if (ei->move_leave_element != EL_EMPTY &&
8131 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
8132 (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
8134 int move_leave_element = ei->move_leave_element;
8136 /* this makes it possible to leave the removed element again */
8137 if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
8138 move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
8140 Feld[x][y] = move_leave_element;
8142 if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
8143 MovDir[x][y] = direction;
8145 InitField(x, y, FALSE);
8147 if (GFX_CRUMBLED(Feld[x][y]))
8148 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8150 if (ELEM_IS_PLAYER(move_leave_element))
8151 RelocatePlayer(x, y, move_leave_element);
8154 /* do this after checking for left-behind element */
8155 ResetGfxAnimation(x, y); /* reset animation values for old field */
8157 if (!CAN_MOVE(element) ||
8158 (CAN_FALL(element) && direction == MV_DOWN &&
8159 (element == EL_SPRING ||
8160 element_info[element].move_pattern == MV_WHEN_PUSHED ||
8161 element_info[element].move_pattern == MV_WHEN_DROPPED)))
8162 GfxDir[x][y] = MovDir[newx][newy] = 0;
8164 TEST_DrawLevelField(x, y);
8165 TEST_DrawLevelField(newx, newy);
8167 Stop[newx][newy] = TRUE; /* ignore this element until the next frame */
8169 /* prevent pushed element from moving on in pushed direction */
8170 if (pushed_by_player && CAN_MOVE(element) &&
8171 element_info[element].move_pattern & MV_ANY_DIRECTION &&
8172 !(element_info[element].move_pattern & direction))
8173 TurnRound(newx, newy);
8175 /* prevent elements on conveyor belt from moving on in last direction */
8176 if (pushed_by_conveyor && CAN_FALL(element) &&
8177 direction & MV_HORIZONTAL)
8178 MovDir[newx][newy] = 0;
8180 if (!pushed_by_player)
8182 int nextx = newx + dx, nexty = newy + dy;
8183 boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
8185 WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
8187 if (CAN_FALL(element) && direction == MV_DOWN)
8188 WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
8190 if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
8191 CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
8193 if (CAN_FALL(element) && direction == MV_DOWN && check_collision_again)
8194 CheckImpact[newx][newy] = CHECK_DELAY_IMPACT;
8197 if (DONT_TOUCH(element)) /* object may be nasty to player or others */
8199 TestIfBadThingTouchesPlayer(newx, newy);
8200 TestIfBadThingTouchesFriend(newx, newy);
8202 if (!IS_CUSTOM_ELEMENT(element))
8203 TestIfBadThingTouchesOtherBadThing(newx, newy);
8205 else if (element == EL_PENGUIN)
8206 TestIfFriendTouchesBadThing(newx, newy);
8208 if (DONT_GET_HIT_BY(element))
8210 TestIfGoodThingGetsHitByBadThing(newx, newy, direction);
8213 /* give the player one last chance (one more frame) to move away */
8214 if (CAN_FALL(element) && direction == MV_DOWN &&
8215 (last_line || (!IS_FREE(x, newy + 1) &&
8216 (!IS_PLAYER(x, newy + 1) ||
8217 game.engine_version < VERSION_IDENT(3,1,1,0)))))
8220 if (pushed_by_player && !game.use_change_when_pushing_bug)
8222 int push_side = MV_DIR_OPPOSITE(direction);
8223 struct PlayerInfo *player = PLAYERINFO(x, y);
8225 CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
8226 player->index_bit, push_side);
8227 CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
8228 player->index_bit, push_side);
8231 if (element == EL_EMC_ANDROID && pushed_by_player) /* make another move */
8232 MovDelay[newx][newy] = 1;
8234 CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
8236 TestIfElementTouchesCustomElement(x, y); /* empty or new element */
8237 TestIfElementHitsCustomElement(newx, newy, direction);
8238 TestIfPlayerTouchesCustomElement(newx, newy);
8239 TestIfElementTouchesCustomElement(newx, newy);
8241 if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
8242 IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
8243 CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
8244 MV_DIR_OPPOSITE(direction));
8247 int AmoebeNachbarNr(int ax, int ay)
8250 int element = Feld[ax][ay];
8252 static int xy[4][2] =
8260 for (i = 0; i < NUM_DIRECTIONS; i++)
8262 int x = ax + xy[i][0];
8263 int y = ay + xy[i][1];
8265 if (!IN_LEV_FIELD(x, y))
8268 if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
8269 group_nr = AmoebaNr[x][y];
8275 void AmoebenVereinigen(int ax, int ay)
8277 int i, x, y, xx, yy;
8278 int new_group_nr = AmoebaNr[ax][ay];
8279 static int xy[4][2] =
8287 if (new_group_nr == 0)
8290 for (i = 0; i < NUM_DIRECTIONS; i++)
8295 if (!IN_LEV_FIELD(x, y))
8298 if ((Feld[x][y] == EL_AMOEBA_FULL ||
8299 Feld[x][y] == EL_BD_AMOEBA ||
8300 Feld[x][y] == EL_AMOEBA_DEAD) &&
8301 AmoebaNr[x][y] != new_group_nr)
8303 int old_group_nr = AmoebaNr[x][y];
8305 if (old_group_nr == 0)
8308 AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
8309 AmoebaCnt[old_group_nr] = 0;
8310 AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
8311 AmoebaCnt2[old_group_nr] = 0;
8313 SCAN_PLAYFIELD(xx, yy)
8315 if (AmoebaNr[xx][yy] == old_group_nr)
8316 AmoebaNr[xx][yy] = new_group_nr;
8322 void AmoebeUmwandeln(int ax, int ay)
8326 if (Feld[ax][ay] == EL_AMOEBA_DEAD)
8328 int group_nr = AmoebaNr[ax][ay];
8333 printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
8334 printf("AmoebeUmwandeln(): This should never happen!\n");
8339 SCAN_PLAYFIELD(x, y)
8341 if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
8344 Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
8348 PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
8349 SND_AMOEBA_TURNING_TO_GEM :
8350 SND_AMOEBA_TURNING_TO_ROCK));
8355 static int xy[4][2] =
8363 for (i = 0; i < NUM_DIRECTIONS; i++)
8368 if (!IN_LEV_FIELD(x, y))
8371 if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
8373 PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
8374 SND_AMOEBA_TURNING_TO_GEM :
8375 SND_AMOEBA_TURNING_TO_ROCK));
8382 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
8385 int group_nr = AmoebaNr[ax][ay];
8386 boolean done = FALSE;
8391 printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
8392 printf("AmoebeUmwandelnBD(): This should never happen!\n");
8397 SCAN_PLAYFIELD(x, y)
8399 if (AmoebaNr[x][y] == group_nr &&
8400 (Feld[x][y] == EL_AMOEBA_DEAD ||
8401 Feld[x][y] == EL_BD_AMOEBA ||
8402 Feld[x][y] == EL_AMOEBA_GROWING))
8405 Feld[x][y] = new_element;
8406 InitField(x, y, FALSE);
8407 TEST_DrawLevelField(x, y);
8413 PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
8414 SND_BD_AMOEBA_TURNING_TO_ROCK :
8415 SND_BD_AMOEBA_TURNING_TO_GEM));
8418 void AmoebeWaechst(int x, int y)
8420 static unsigned int sound_delay = 0;
8421 static unsigned int sound_delay_value = 0;
8423 if (!MovDelay[x][y]) /* start new growing cycle */
8427 if (DelayReached(&sound_delay, sound_delay_value))
8429 PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
8430 sound_delay_value = 30;
8434 if (MovDelay[x][y]) /* wait some time before growing bigger */
8437 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8439 int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
8440 6 - MovDelay[x][y]);
8442 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
8445 if (!MovDelay[x][y])
8447 Feld[x][y] = Store[x][y];
8449 TEST_DrawLevelField(x, y);
8454 void AmoebaDisappearing(int x, int y)
8456 static unsigned int sound_delay = 0;
8457 static unsigned int sound_delay_value = 0;
8459 if (!MovDelay[x][y]) /* start new shrinking cycle */
8463 if (DelayReached(&sound_delay, sound_delay_value))
8464 sound_delay_value = 30;
8467 if (MovDelay[x][y]) /* wait some time before shrinking */
8470 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8472 int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
8473 6 - MovDelay[x][y]);
8475 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
8478 if (!MovDelay[x][y])
8480 Feld[x][y] = EL_EMPTY;
8481 TEST_DrawLevelField(x, y);
8483 /* don't let mole enter this field in this cycle;
8484 (give priority to objects falling to this field from above) */
8490 void AmoebeAbleger(int ax, int ay)
8493 int element = Feld[ax][ay];
8494 int graphic = el2img(element);
8495 int newax = ax, neway = ay;
8496 boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
8497 static int xy[4][2] =
8505 if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
8507 Feld[ax][ay] = EL_AMOEBA_DEAD;
8508 TEST_DrawLevelField(ax, ay);
8512 if (IS_ANIMATED(graphic))
8513 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
8515 if (!MovDelay[ax][ay]) /* start making new amoeba field */
8516 MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
8518 if (MovDelay[ax][ay]) /* wait some time before making new amoeba */
8521 if (MovDelay[ax][ay])
8525 if (can_drop) /* EL_AMOEBA_WET or EL_EMC_DRIPPER */
8528 int x = ax + xy[start][0];
8529 int y = ay + xy[start][1];
8531 if (!IN_LEV_FIELD(x, y))
8534 if (IS_FREE(x, y) ||
8535 CAN_GROW_INTO(Feld[x][y]) ||
8536 Feld[x][y] == EL_QUICKSAND_EMPTY ||
8537 Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
8543 if (newax == ax && neway == ay)
8546 else /* normal or "filled" (BD style) amoeba */
8549 boolean waiting_for_player = FALSE;
8551 for (i = 0; i < NUM_DIRECTIONS; i++)
8553 int j = (start + i) % 4;
8554 int x = ax + xy[j][0];
8555 int y = ay + xy[j][1];
8557 if (!IN_LEV_FIELD(x, y))
8560 if (IS_FREE(x, y) ||
8561 CAN_GROW_INTO(Feld[x][y]) ||
8562 Feld[x][y] == EL_QUICKSAND_EMPTY ||
8563 Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
8569 else if (IS_PLAYER(x, y))
8570 waiting_for_player = TRUE;
8573 if (newax == ax && neway == ay) /* amoeba cannot grow */
8575 if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
8577 Feld[ax][ay] = EL_AMOEBA_DEAD;
8578 TEST_DrawLevelField(ax, ay);
8579 AmoebaCnt[AmoebaNr[ax][ay]]--;
8581 if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0) /* amoeba is completely dead */
8583 if (element == EL_AMOEBA_FULL)
8584 AmoebeUmwandeln(ax, ay);
8585 else if (element == EL_BD_AMOEBA)
8586 AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
8591 else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
8593 /* amoeba gets larger by growing in some direction */
8595 int new_group_nr = AmoebaNr[ax][ay];
8598 if (new_group_nr == 0)
8600 printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
8601 printf("AmoebeAbleger(): This should never happen!\n");
8606 AmoebaNr[newax][neway] = new_group_nr;
8607 AmoebaCnt[new_group_nr]++;
8608 AmoebaCnt2[new_group_nr]++;
8610 /* if amoeba touches other amoeba(s) after growing, unify them */
8611 AmoebenVereinigen(newax, neway);
8613 if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
8615 AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
8621 if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
8622 (neway == lev_fieldy - 1 && newax != ax))
8624 Feld[newax][neway] = EL_AMOEBA_GROWING; /* creation of new amoeba */
8625 Store[newax][neway] = element;
8627 else if (neway == ay || element == EL_EMC_DRIPPER)
8629 Feld[newax][neway] = EL_AMOEBA_DROP; /* drop left/right of amoeba */
8631 PlayLevelSoundAction(newax, neway, ACTION_GROWING);
8635 InitMovingField(ax, ay, MV_DOWN); /* drop dripping from amoeba */
8636 Feld[ax][ay] = EL_AMOEBA_DROPPING;
8637 Store[ax][ay] = EL_AMOEBA_DROP;
8638 ContinueMoving(ax, ay);
8642 TEST_DrawLevelField(newax, neway);
8645 void Life(int ax, int ay)
8649 int element = Feld[ax][ay];
8650 int graphic = el2img(element);
8651 int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
8653 boolean changed = FALSE;
8655 if (IS_ANIMATED(graphic))
8656 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
8661 if (!MovDelay[ax][ay]) /* start new "game of life" cycle */
8662 MovDelay[ax][ay] = life_time;
8664 if (MovDelay[ax][ay]) /* wait some time before next cycle */
8667 if (MovDelay[ax][ay])
8671 for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
8673 int xx = ax+x1, yy = ay+y1;
8676 if (!IN_LEV_FIELD(xx, yy))
8679 for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
8681 int x = xx+x2, y = yy+y2;
8683 if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
8686 if (((Feld[x][y] == element ||
8687 (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
8689 (IS_FREE(x, y) && Stop[x][y]))
8693 if (xx == ax && yy == ay) /* field in the middle */
8695 if (nachbarn < life_parameter[0] ||
8696 nachbarn > life_parameter[1])
8698 Feld[xx][yy] = EL_EMPTY;
8700 TEST_DrawLevelField(xx, yy);
8701 Stop[xx][yy] = TRUE;
8705 else if (IS_FREE(xx, yy) || CAN_GROW_INTO(Feld[xx][yy]))
8706 { /* free border field */
8707 if (nachbarn >= life_parameter[2] &&
8708 nachbarn <= life_parameter[3])
8710 Feld[xx][yy] = element;
8711 MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
8713 TEST_DrawLevelField(xx, yy);
8714 Stop[xx][yy] = TRUE;
8721 PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
8722 SND_GAME_OF_LIFE_GROWING);
8725 static void InitRobotWheel(int x, int y)
8727 ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
8730 static void RunRobotWheel(int x, int y)
8732 PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
8735 static void StopRobotWheel(int x, int y)
8737 if (ZX == x && ZY == y)
8741 game.robot_wheel_active = FALSE;
8745 static void InitTimegateWheel(int x, int y)
8747 ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
8750 static void RunTimegateWheel(int x, int y)
8752 PlayLevelSound(x, y, SND_CLASS_TIMEGATE_SWITCH_ACTIVE);
8755 static void InitMagicBallDelay(int x, int y)
8757 ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
8760 static void ActivateMagicBall(int bx, int by)
8764 if (level.ball_random)
8766 int pos_border = RND(8); /* select one of the eight border elements */
8767 int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
8768 int xx = pos_content % 3;
8769 int yy = pos_content / 3;
8774 if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
8775 CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
8779 for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
8781 int xx = x - bx + 1;
8782 int yy = y - by + 1;
8784 if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
8785 CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
8789 game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
8792 void CheckExit(int x, int y)
8794 if (local_player->gems_still_needed > 0 ||
8795 local_player->sokobanfields_still_needed > 0 ||
8796 local_player->lights_still_needed > 0)
8798 int element = Feld[x][y];
8799 int graphic = el2img(element);
8801 if (IS_ANIMATED(graphic))
8802 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8807 if (AllPlayersGone) /* do not re-open exit door closed after last player */
8810 Feld[x][y] = EL_EXIT_OPENING;
8812 PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
8815 void CheckExitEM(int x, int y)
8817 if (local_player->gems_still_needed > 0 ||
8818 local_player->sokobanfields_still_needed > 0 ||
8819 local_player->lights_still_needed > 0)
8821 int element = Feld[x][y];
8822 int graphic = el2img(element);
8824 if (IS_ANIMATED(graphic))
8825 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8830 if (AllPlayersGone) /* do not re-open exit door closed after last player */
8833 Feld[x][y] = EL_EM_EXIT_OPENING;
8835 PlayLevelSoundNearest(x, y, SND_CLASS_EM_EXIT_OPENING);
8838 void CheckExitSteel(int x, int y)
8840 if (local_player->gems_still_needed > 0 ||
8841 local_player->sokobanfields_still_needed > 0 ||
8842 local_player->lights_still_needed > 0)
8844 int element = Feld[x][y];
8845 int graphic = el2img(element);
8847 if (IS_ANIMATED(graphic))
8848 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8853 if (AllPlayersGone) /* do not re-open exit door closed after last player */
8856 Feld[x][y] = EL_STEEL_EXIT_OPENING;
8858 PlayLevelSoundNearest(x, y, SND_CLASS_STEEL_EXIT_OPENING);
8861 void CheckExitSteelEM(int x, int y)
8863 if (local_player->gems_still_needed > 0 ||
8864 local_player->sokobanfields_still_needed > 0 ||
8865 local_player->lights_still_needed > 0)
8867 int element = Feld[x][y];
8868 int graphic = el2img(element);
8870 if (IS_ANIMATED(graphic))
8871 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8876 if (AllPlayersGone) /* do not re-open exit door closed after last player */
8879 Feld[x][y] = EL_EM_STEEL_EXIT_OPENING;
8881 PlayLevelSoundNearest(x, y, SND_CLASS_EM_STEEL_EXIT_OPENING);
8884 void CheckExitSP(int x, int y)
8886 if (local_player->gems_still_needed > 0)
8888 int element = Feld[x][y];
8889 int graphic = el2img(element);
8891 if (IS_ANIMATED(graphic))
8892 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8897 if (AllPlayersGone) /* do not re-open exit door closed after last player */
8900 Feld[x][y] = EL_SP_EXIT_OPENING;
8902 PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
8905 static void CloseAllOpenTimegates()
8909 SCAN_PLAYFIELD(x, y)
8911 int element = Feld[x][y];
8913 if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
8915 Feld[x][y] = EL_TIMEGATE_CLOSING;
8917 PlayLevelSoundAction(x, y, ACTION_CLOSING);
8922 void DrawTwinkleOnField(int x, int y)
8924 if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
8927 if (Feld[x][y] == EL_BD_DIAMOND)
8930 if (MovDelay[x][y] == 0) /* next animation frame */
8931 MovDelay[x][y] = 11 * !GetSimpleRandom(500);
8933 if (MovDelay[x][y] != 0) /* wait some time before next frame */
8937 DrawLevelElementAnimation(x, y, Feld[x][y]);
8939 if (MovDelay[x][y] != 0)
8941 int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
8942 10 - MovDelay[x][y]);
8944 DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
8949 void MauerWaechst(int x, int y)
8953 if (!MovDelay[x][y]) /* next animation frame */
8954 MovDelay[x][y] = 3 * delay;
8956 if (MovDelay[x][y]) /* wait some time before next frame */
8960 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8962 int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
8963 int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
8965 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
8968 if (!MovDelay[x][y])
8970 if (MovDir[x][y] == MV_LEFT)
8972 if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
8973 TEST_DrawLevelField(x - 1, y);
8975 else if (MovDir[x][y] == MV_RIGHT)
8977 if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
8978 TEST_DrawLevelField(x + 1, y);
8980 else if (MovDir[x][y] == MV_UP)
8982 if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
8983 TEST_DrawLevelField(x, y - 1);
8987 if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
8988 TEST_DrawLevelField(x, y + 1);
8991 Feld[x][y] = Store[x][y];
8993 GfxDir[x][y] = MovDir[x][y] = MV_NONE;
8994 TEST_DrawLevelField(x, y);
8999 void MauerAbleger(int ax, int ay)
9001 int element = Feld[ax][ay];
9002 int graphic = el2img(element);
9003 boolean oben_frei = FALSE, unten_frei = FALSE;
9004 boolean links_frei = FALSE, rechts_frei = FALSE;
9005 boolean oben_massiv = FALSE, unten_massiv = FALSE;
9006 boolean links_massiv = FALSE, rechts_massiv = FALSE;
9007 boolean new_wall = FALSE;
9009 if (IS_ANIMATED(graphic))
9010 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9012 if (!MovDelay[ax][ay]) /* start building new wall */
9013 MovDelay[ax][ay] = 6;
9015 if (MovDelay[ax][ay]) /* wait some time before building new wall */
9018 if (MovDelay[ax][ay])
9022 if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9024 if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9026 if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9028 if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9031 if (element == EL_EXPANDABLE_WALL_VERTICAL ||
9032 element == EL_EXPANDABLE_WALL_ANY)
9036 Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
9037 Store[ax][ay-1] = element;
9038 GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9039 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9040 DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9041 IMG_EXPANDABLE_WALL_GROWING_UP, 0);
9046 Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
9047 Store[ax][ay+1] = element;
9048 GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9049 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9050 DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9051 IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
9056 if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9057 element == EL_EXPANDABLE_WALL_ANY ||
9058 element == EL_EXPANDABLE_WALL ||
9059 element == EL_BD_EXPANDABLE_WALL)
9063 Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
9064 Store[ax-1][ay] = element;
9065 GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9066 if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9067 DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9068 IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
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_RIGHT;
9077 if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9078 DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9079 IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
9084 if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
9085 TEST_DrawLevelField(ax, ay);
9087 if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
9089 if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
9090 unten_massiv = TRUE;
9091 if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
9092 links_massiv = TRUE;
9093 if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
9094 rechts_massiv = TRUE;
9096 if (((oben_massiv && unten_massiv) ||
9097 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9098 element == EL_EXPANDABLE_WALL) &&
9099 ((links_massiv && rechts_massiv) ||
9100 element == EL_EXPANDABLE_WALL_VERTICAL))
9101 Feld[ax][ay] = EL_WALL;
9104 PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9107 void MauerAblegerStahl(int ax, int ay)
9109 int element = Feld[ax][ay];
9110 int graphic = el2img(element);
9111 boolean oben_frei = FALSE, unten_frei = FALSE;
9112 boolean links_frei = FALSE, rechts_frei = FALSE;
9113 boolean oben_massiv = FALSE, unten_massiv = FALSE;
9114 boolean links_massiv = FALSE, rechts_massiv = FALSE;
9115 boolean new_wall = FALSE;
9117 if (IS_ANIMATED(graphic))
9118 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9120 if (!MovDelay[ax][ay]) /* start building new wall */
9121 MovDelay[ax][ay] = 6;
9123 if (MovDelay[ax][ay]) /* wait some time before building new wall */
9126 if (MovDelay[ax][ay])
9130 if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9132 if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9134 if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9136 if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9139 if (element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
9140 element == EL_EXPANDABLE_STEELWALL_ANY)
9144 Feld[ax][ay-1] = EL_EXPANDABLE_STEELWALL_GROWING;
9145 Store[ax][ay-1] = element;
9146 GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9147 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9148 DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9149 IMG_EXPANDABLE_STEELWALL_GROWING_UP, 0);
9154 Feld[ax][ay+1] = EL_EXPANDABLE_STEELWALL_GROWING;
9155 Store[ax][ay+1] = element;
9156 GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9157 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9158 DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9159 IMG_EXPANDABLE_STEELWALL_GROWING_DOWN, 0);
9164 if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
9165 element == EL_EXPANDABLE_STEELWALL_ANY)
9169 Feld[ax-1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9170 Store[ax-1][ay] = element;
9171 GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9172 if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9173 DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9174 IMG_EXPANDABLE_STEELWALL_GROWING_LEFT, 0);
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_RIGHT;
9183 if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9184 DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9185 IMG_EXPANDABLE_STEELWALL_GROWING_RIGHT, 0);
9190 if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
9192 if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
9193 unten_massiv = TRUE;
9194 if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
9195 links_massiv = TRUE;
9196 if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
9197 rechts_massiv = TRUE;
9199 if (((oben_massiv && unten_massiv) ||
9200 element == EL_EXPANDABLE_STEELWALL_HORIZONTAL) &&
9201 ((links_massiv && rechts_massiv) ||
9202 element == EL_EXPANDABLE_STEELWALL_VERTICAL))
9203 Feld[ax][ay] = EL_STEELWALL;
9206 PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9209 void CheckForDragon(int x, int y)
9212 boolean dragon_found = FALSE;
9213 static int xy[4][2] =
9221 for (i = 0; i < NUM_DIRECTIONS; i++)
9223 for (j = 0; j < 4; j++)
9225 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9227 if (IN_LEV_FIELD(xx, yy) &&
9228 (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
9230 if (Feld[xx][yy] == EL_DRAGON)
9231 dragon_found = TRUE;
9240 for (i = 0; i < NUM_DIRECTIONS; i++)
9242 for (j = 0; j < 3; j++)
9244 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9246 if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
9248 Feld[xx][yy] = EL_EMPTY;
9249 TEST_DrawLevelField(xx, yy);
9258 static void InitBuggyBase(int x, int y)
9260 int element = Feld[x][y];
9261 int activating_delay = FRAMES_PER_SECOND / 4;
9264 (element == EL_SP_BUGGY_BASE ?
9265 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
9266 element == EL_SP_BUGGY_BASE_ACTIVATING ?
9268 element == EL_SP_BUGGY_BASE_ACTIVE ?
9269 1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
9272 static void WarnBuggyBase(int x, int y)
9275 static int xy[4][2] =
9283 for (i = 0; i < NUM_DIRECTIONS; i++)
9285 int xx = x + xy[i][0];
9286 int yy = y + xy[i][1];
9288 if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
9290 PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
9297 static void InitTrap(int x, int y)
9299 ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
9302 static void ActivateTrap(int x, int y)
9304 PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
9307 static void ChangeActiveTrap(int x, int y)
9309 int graphic = IMG_TRAP_ACTIVE;
9311 /* if new animation frame was drawn, correct crumbled sand border */
9312 if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
9313 TEST_DrawLevelFieldCrumbled(x, y);
9316 static int getSpecialActionElement(int element, int number, int base_element)
9318 return (element != EL_EMPTY ? element :
9319 number != -1 ? base_element + number - 1 :
9323 static int getModifiedActionNumber(int value_old, int operator, int operand,
9324 int value_min, int value_max)
9326 int value_new = (operator == CA_MODE_SET ? operand :
9327 operator == CA_MODE_ADD ? value_old + operand :
9328 operator == CA_MODE_SUBTRACT ? value_old - operand :
9329 operator == CA_MODE_MULTIPLY ? value_old * operand :
9330 operator == CA_MODE_DIVIDE ? value_old / MAX(1, operand) :
9331 operator == CA_MODE_MODULO ? value_old % MAX(1, operand) :
9334 return (value_new < value_min ? value_min :
9335 value_new > value_max ? value_max :
9339 static void ExecuteCustomElementAction(int x, int y, int element, int page)
9341 struct ElementInfo *ei = &element_info[element];
9342 struct ElementChangeInfo *change = &ei->change_page[page];
9343 int target_element = change->target_element;
9344 int action_type = change->action_type;
9345 int action_mode = change->action_mode;
9346 int action_arg = change->action_arg;
9347 int action_element = change->action_element;
9350 if (!change->has_action)
9353 /* ---------- determine action paramater values -------------------------- */
9355 int level_time_value =
9356 (level.time > 0 ? TimeLeft :
9359 int action_arg_element_raw =
9360 (action_arg == CA_ARG_PLAYER_TRIGGER ? change->actual_trigger_player :
9361 action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
9362 action_arg == CA_ARG_ELEMENT_TARGET ? change->target_element :
9363 action_arg == CA_ARG_ELEMENT_ACTION ? change->action_element :
9364 action_arg == CA_ARG_INVENTORY_RM_TRIGGER ? change->actual_trigger_element:
9365 action_arg == CA_ARG_INVENTORY_RM_TARGET ? change->target_element :
9366 action_arg == CA_ARG_INVENTORY_RM_ACTION ? change->action_element :
9368 int action_arg_element = GetElementFromGroupElement(action_arg_element_raw);
9370 int action_arg_direction =
9371 (action_arg >= CA_ARG_DIRECTION_LEFT &&
9372 action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
9373 action_arg == CA_ARG_DIRECTION_TRIGGER ?
9374 change->actual_trigger_side :
9375 action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
9376 MV_DIR_OPPOSITE(change->actual_trigger_side) :
9379 int action_arg_number_min =
9380 (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
9383 int action_arg_number_max =
9384 (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
9385 action_type == CA_SET_LEVEL_GEMS ? 999 :
9386 action_type == CA_SET_LEVEL_TIME ? 9999 :
9387 action_type == CA_SET_LEVEL_SCORE ? 99999 :
9388 action_type == CA_SET_CE_VALUE ? 9999 :
9389 action_type == CA_SET_CE_SCORE ? 9999 :
9392 int action_arg_number_reset =
9393 (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize[0] :
9394 action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
9395 action_type == CA_SET_LEVEL_TIME ? level.time :
9396 action_type == CA_SET_LEVEL_SCORE ? 0 :
9397 action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
9398 action_type == CA_SET_CE_SCORE ? 0 :
9401 int action_arg_number =
9402 (action_arg <= CA_ARG_MAX ? action_arg :
9403 action_arg >= CA_ARG_SPEED_NOT_MOVING &&
9404 action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
9405 action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
9406 action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
9407 action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
9408 action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
9409 action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
9410 action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
9411 action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
9412 action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
9413 action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? local_player->gems_still_needed :
9414 action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? local_player->score :
9415 action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
9416 action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
9417 action_arg == CA_ARG_ELEMENT_CV_ACTION ? GET_NEW_CE_VALUE(action_element):
9418 action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
9419 action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
9420 action_arg == CA_ARG_ELEMENT_CS_ACTION ? GET_CE_SCORE(action_element) :
9421 action_arg == CA_ARG_ELEMENT_NR_TARGET ? change->target_element :
9422 action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
9423 action_arg == CA_ARG_ELEMENT_NR_ACTION ? change->action_element :
9426 int action_arg_number_old =
9427 (action_type == CA_SET_LEVEL_GEMS ? local_player->gems_still_needed :
9428 action_type == CA_SET_LEVEL_TIME ? TimeLeft :
9429 action_type == CA_SET_LEVEL_SCORE ? local_player->score :
9430 action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
9431 action_type == CA_SET_CE_SCORE ? ei->collect_score :
9434 int action_arg_number_new =
9435 getModifiedActionNumber(action_arg_number_old,
9436 action_mode, action_arg_number,
9437 action_arg_number_min, action_arg_number_max);
9439 int trigger_player_bits =
9440 (change->actual_trigger_player_bits != CH_PLAYER_NONE ?
9441 change->actual_trigger_player_bits : change->trigger_player);
9443 int action_arg_player_bits =
9444 (action_arg >= CA_ARG_PLAYER_1 &&
9445 action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
9446 action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
9447 action_arg == CA_ARG_PLAYER_ACTION ? 1 << GET_PLAYER_NR(action_element) :
9450 /* ---------- execute action -------------------------------------------- */
9452 switch (action_type)
9459 /* ---------- level actions ------------------------------------------- */
9461 case CA_RESTART_LEVEL:
9463 game.restart_level = TRUE;
9468 case CA_SHOW_ENVELOPE:
9470 int element = getSpecialActionElement(action_arg_element,
9471 action_arg_number, EL_ENVELOPE_1);
9473 if (IS_ENVELOPE(element))
9474 local_player->show_envelope = element;
9479 case CA_SET_LEVEL_TIME:
9481 if (level.time > 0) /* only modify limited time value */
9483 TimeLeft = action_arg_number_new;
9485 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
9487 DisplayGameControlValues();
9489 if (!TimeLeft && setup.time_limit)
9490 for (i = 0; i < MAX_PLAYERS; i++)
9491 KillPlayer(&stored_player[i]);
9497 case CA_SET_LEVEL_SCORE:
9499 local_player->score = action_arg_number_new;
9501 game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
9503 DisplayGameControlValues();
9508 case CA_SET_LEVEL_GEMS:
9510 local_player->gems_still_needed = action_arg_number_new;
9512 game.snapshot.collected_item = TRUE;
9514 game_panel_controls[GAME_PANEL_GEMS].value =
9515 local_player->gems_still_needed;
9517 DisplayGameControlValues();
9522 case CA_SET_LEVEL_WIND:
9524 game.wind_direction = action_arg_direction;
9529 case CA_SET_LEVEL_RANDOM_SEED:
9531 /* ensure that setting a new random seed while playing is predictable */
9532 InitRND(action_arg_number_new ? action_arg_number_new : RND(1000000) + 1);
9537 /* ---------- player actions ------------------------------------------ */
9539 case CA_MOVE_PLAYER:
9541 /* automatically move to the next field in specified direction */
9542 for (i = 0; i < MAX_PLAYERS; i++)
9543 if (trigger_player_bits & (1 << i))
9544 stored_player[i].programmed_action = action_arg_direction;
9549 case CA_EXIT_PLAYER:
9551 for (i = 0; i < MAX_PLAYERS; i++)
9552 if (action_arg_player_bits & (1 << i))
9553 PlayerWins(&stored_player[i]);
9558 case CA_KILL_PLAYER:
9560 for (i = 0; i < MAX_PLAYERS; i++)
9561 if (action_arg_player_bits & (1 << i))
9562 KillPlayer(&stored_player[i]);
9567 case CA_SET_PLAYER_KEYS:
9569 int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
9570 int element = getSpecialActionElement(action_arg_element,
9571 action_arg_number, EL_KEY_1);
9573 if (IS_KEY(element))
9575 for (i = 0; i < MAX_PLAYERS; i++)
9577 if (trigger_player_bits & (1 << i))
9579 stored_player[i].key[KEY_NR(element)] = key_state;
9581 DrawGameDoorValues();
9589 case CA_SET_PLAYER_SPEED:
9591 for (i = 0; i < MAX_PLAYERS; i++)
9593 if (trigger_player_bits & (1 << i))
9595 int move_stepsize = TILEX / stored_player[i].move_delay_value;
9597 if (action_arg == CA_ARG_SPEED_FASTER &&
9598 stored_player[i].cannot_move)
9600 action_arg_number = STEPSIZE_VERY_SLOW;
9602 else if (action_arg == CA_ARG_SPEED_SLOWER ||
9603 action_arg == CA_ARG_SPEED_FASTER)
9605 action_arg_number = 2;
9606 action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
9609 else if (action_arg == CA_ARG_NUMBER_RESET)
9611 action_arg_number = level.initial_player_stepsize[i];
9615 getModifiedActionNumber(move_stepsize,
9618 action_arg_number_min,
9619 action_arg_number_max);
9621 SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
9628 case CA_SET_PLAYER_SHIELD:
9630 for (i = 0; i < MAX_PLAYERS; i++)
9632 if (trigger_player_bits & (1 << i))
9634 if (action_arg == CA_ARG_SHIELD_OFF)
9636 stored_player[i].shield_normal_time_left = 0;
9637 stored_player[i].shield_deadly_time_left = 0;
9639 else if (action_arg == CA_ARG_SHIELD_NORMAL)
9641 stored_player[i].shield_normal_time_left = 999999;
9643 else if (action_arg == CA_ARG_SHIELD_DEADLY)
9645 stored_player[i].shield_normal_time_left = 999999;
9646 stored_player[i].shield_deadly_time_left = 999999;
9654 case CA_SET_PLAYER_GRAVITY:
9656 for (i = 0; i < MAX_PLAYERS; i++)
9658 if (trigger_player_bits & (1 << i))
9660 stored_player[i].gravity =
9661 (action_arg == CA_ARG_GRAVITY_OFF ? FALSE :
9662 action_arg == CA_ARG_GRAVITY_ON ? TRUE :
9663 action_arg == CA_ARG_GRAVITY_TOGGLE ? !stored_player[i].gravity :
9664 stored_player[i].gravity);
9671 case CA_SET_PLAYER_ARTWORK:
9673 for (i = 0; i < MAX_PLAYERS; i++)
9675 if (trigger_player_bits & (1 << i))
9677 int artwork_element = action_arg_element;
9679 if (action_arg == CA_ARG_ELEMENT_RESET)
9681 (level.use_artwork_element[i] ? level.artwork_element[i] :
9682 stored_player[i].element_nr);
9684 if (stored_player[i].artwork_element != artwork_element)
9685 stored_player[i].Frame = 0;
9687 stored_player[i].artwork_element = artwork_element;
9689 SetPlayerWaiting(&stored_player[i], FALSE);
9691 /* set number of special actions for bored and sleeping animation */
9692 stored_player[i].num_special_action_bored =
9693 get_num_special_action(artwork_element,
9694 ACTION_BORING_1, ACTION_BORING_LAST);
9695 stored_player[i].num_special_action_sleeping =
9696 get_num_special_action(artwork_element,
9697 ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
9704 case CA_SET_PLAYER_INVENTORY:
9706 for (i = 0; i < MAX_PLAYERS; i++)
9708 struct PlayerInfo *player = &stored_player[i];
9711 if (trigger_player_bits & (1 << i))
9713 int inventory_element = action_arg_element;
9715 if (action_arg == CA_ARG_ELEMENT_TARGET ||
9716 action_arg == CA_ARG_ELEMENT_TRIGGER ||
9717 action_arg == CA_ARG_ELEMENT_ACTION)
9719 int element = inventory_element;
9720 int collect_count = element_info[element].collect_count_initial;
9722 if (!IS_CUSTOM_ELEMENT(element))
9725 if (collect_count == 0)
9726 player->inventory_infinite_element = element;
9728 for (k = 0; k < collect_count; k++)
9729 if (player->inventory_size < MAX_INVENTORY_SIZE)
9730 player->inventory_element[player->inventory_size++] =
9733 else if (action_arg == CA_ARG_INVENTORY_RM_TARGET ||
9734 action_arg == CA_ARG_INVENTORY_RM_TRIGGER ||
9735 action_arg == CA_ARG_INVENTORY_RM_ACTION)
9737 if (player->inventory_infinite_element != EL_UNDEFINED &&
9738 IS_EQUAL_OR_IN_GROUP(player->inventory_infinite_element,
9739 action_arg_element_raw))
9740 player->inventory_infinite_element = EL_UNDEFINED;
9742 for (k = 0, j = 0; j < player->inventory_size; j++)
9744 if (!IS_EQUAL_OR_IN_GROUP(player->inventory_element[j],
9745 action_arg_element_raw))
9746 player->inventory_element[k++] = player->inventory_element[j];
9749 player->inventory_size = k;
9751 else if (action_arg == CA_ARG_INVENTORY_RM_FIRST)
9753 if (player->inventory_size > 0)
9755 for (j = 0; j < player->inventory_size - 1; j++)
9756 player->inventory_element[j] = player->inventory_element[j + 1];
9758 player->inventory_size--;
9761 else if (action_arg == CA_ARG_INVENTORY_RM_LAST)
9763 if (player->inventory_size > 0)
9764 player->inventory_size--;
9766 else if (action_arg == CA_ARG_INVENTORY_RM_ALL)
9768 player->inventory_infinite_element = EL_UNDEFINED;
9769 player->inventory_size = 0;
9771 else if (action_arg == CA_ARG_INVENTORY_RESET)
9773 player->inventory_infinite_element = EL_UNDEFINED;
9774 player->inventory_size = 0;
9776 if (level.use_initial_inventory[i])
9778 for (j = 0; j < level.initial_inventory_size[i]; j++)
9780 int element = level.initial_inventory_content[i][j];
9781 int collect_count = element_info[element].collect_count_initial;
9783 if (!IS_CUSTOM_ELEMENT(element))
9786 if (collect_count == 0)
9787 player->inventory_infinite_element = element;
9789 for (k = 0; k < collect_count; k++)
9790 if (player->inventory_size < MAX_INVENTORY_SIZE)
9791 player->inventory_element[player->inventory_size++] =
9802 /* ---------- CE actions ---------------------------------------------- */
9804 case CA_SET_CE_VALUE:
9806 int last_ce_value = CustomValue[x][y];
9808 CustomValue[x][y] = action_arg_number_new;
9810 if (CustomValue[x][y] != last_ce_value)
9812 CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_CHANGES);
9813 CheckTriggeredElementChange(x, y, element, CE_VALUE_CHANGES_OF_X);
9815 if (CustomValue[x][y] == 0)
9817 CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
9818 CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
9825 case CA_SET_CE_SCORE:
9827 int last_ce_score = ei->collect_score;
9829 ei->collect_score = action_arg_number_new;
9831 if (ei->collect_score != last_ce_score)
9833 CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
9834 CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
9836 if (ei->collect_score == 0)
9840 CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
9841 CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
9844 This is a very special case that seems to be a mixture between
9845 CheckElementChange() and CheckTriggeredElementChange(): while
9846 the first one only affects single elements that are triggered
9847 directly, the second one affects multiple elements in the playfield
9848 that are triggered indirectly by another element. This is a third
9849 case: Changing the CE score always affects multiple identical CEs,
9850 so every affected CE must be checked, not only the single CE for
9851 which the CE score was changed in the first place (as every instance
9852 of that CE shares the same CE score, and therefore also can change)!
9854 SCAN_PLAYFIELD(xx, yy)
9856 if (Feld[xx][yy] == element)
9857 CheckElementChange(xx, yy, element, EL_UNDEFINED,
9858 CE_SCORE_GETS_ZERO);
9866 case CA_SET_CE_ARTWORK:
9868 int artwork_element = action_arg_element;
9869 boolean reset_frame = FALSE;
9872 if (action_arg == CA_ARG_ELEMENT_RESET)
9873 artwork_element = (ei->use_gfx_element ? ei->gfx_element_initial :
9876 if (ei->gfx_element != artwork_element)
9879 ei->gfx_element = artwork_element;
9881 SCAN_PLAYFIELD(xx, yy)
9883 if (Feld[xx][yy] == element)
9887 ResetGfxAnimation(xx, yy);
9888 ResetRandomAnimationValue(xx, yy);
9891 TEST_DrawLevelField(xx, yy);
9898 /* ---------- engine actions ------------------------------------------ */
9900 case CA_SET_ENGINE_SCAN_MODE:
9902 InitPlayfieldScanMode(action_arg);
9912 static void CreateFieldExt(int x, int y, int element, boolean is_change)
9914 int old_element = Feld[x][y];
9915 int new_element = GetElementFromGroupElement(element);
9916 int previous_move_direction = MovDir[x][y];
9917 int last_ce_value = CustomValue[x][y];
9918 boolean player_explosion_protected = PLAYER_EXPLOSION_PROTECTED(x, y);
9919 boolean new_element_is_player = ELEM_IS_PLAYER(new_element);
9920 boolean add_player_onto_element = (new_element_is_player &&
9921 new_element != EL_SOKOBAN_FIELD_PLAYER &&
9922 IS_WALKABLE(old_element));
9924 if (!add_player_onto_element)
9926 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
9927 RemoveMovingField(x, y);
9931 Feld[x][y] = new_element;
9933 if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
9934 MovDir[x][y] = previous_move_direction;
9936 if (element_info[new_element].use_last_ce_value)
9937 CustomValue[x][y] = last_ce_value;
9939 InitField_WithBug1(x, y, FALSE);
9941 new_element = Feld[x][y]; /* element may have changed */
9943 ResetGfxAnimation(x, y);
9944 ResetRandomAnimationValue(x, y);
9946 TEST_DrawLevelField(x, y);
9948 if (GFX_CRUMBLED(new_element))
9949 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
9952 /* check if element under the player changes from accessible to unaccessible
9953 (needed for special case of dropping element which then changes) */
9954 /* (must be checked after creating new element for walkable group elements) */
9955 if (IS_PLAYER(x, y) && !player_explosion_protected &&
9956 IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
9963 /* "ChangeCount" not set yet to allow "entered by player" change one time */
9964 if (new_element_is_player)
9965 RelocatePlayer(x, y, new_element);
9968 ChangeCount[x][y]++; /* count number of changes in the same frame */
9970 TestIfBadThingTouchesPlayer(x, y);
9971 TestIfPlayerTouchesCustomElement(x, y);
9972 TestIfElementTouchesCustomElement(x, y);
9975 static void CreateField(int x, int y, int element)
9977 CreateFieldExt(x, y, element, FALSE);
9980 static void CreateElementFromChange(int x, int y, int element)
9982 element = GET_VALID_RUNTIME_ELEMENT(element);
9984 if (game.engine_version >= VERSION_IDENT(3,2,0,7))
9986 int old_element = Feld[x][y];
9988 /* prevent changed element from moving in same engine frame
9989 unless both old and new element can either fall or move */
9990 if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
9991 (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
9995 CreateFieldExt(x, y, element, TRUE);
9998 static boolean ChangeElement(int x, int y, int element, int page)
10000 struct ElementInfo *ei = &element_info[element];
10001 struct ElementChangeInfo *change = &ei->change_page[page];
10002 int ce_value = CustomValue[x][y];
10003 int ce_score = ei->collect_score;
10004 int target_element;
10005 int old_element = Feld[x][y];
10007 /* always use default change event to prevent running into a loop */
10008 if (ChangeEvent[x][y] == -1)
10009 ChangeEvent[x][y] = CE_DELAY;
10011 if (ChangeEvent[x][y] == CE_DELAY)
10013 /* reset actual trigger element, trigger player and action element */
10014 change->actual_trigger_element = EL_EMPTY;
10015 change->actual_trigger_player = EL_EMPTY;
10016 change->actual_trigger_player_bits = CH_PLAYER_NONE;
10017 change->actual_trigger_side = CH_SIDE_NONE;
10018 change->actual_trigger_ce_value = 0;
10019 change->actual_trigger_ce_score = 0;
10022 /* do not change elements more than a specified maximum number of changes */
10023 if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
10026 ChangeCount[x][y]++; /* count number of changes in the same frame */
10028 if (change->explode)
10035 if (change->use_target_content)
10037 boolean complete_replace = TRUE;
10038 boolean can_replace[3][3];
10041 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10044 boolean is_walkable;
10045 boolean is_diggable;
10046 boolean is_collectible;
10047 boolean is_removable;
10048 boolean is_destructible;
10049 int ex = x + xx - 1;
10050 int ey = y + yy - 1;
10051 int content_element = change->target_content.e[xx][yy];
10054 can_replace[xx][yy] = TRUE;
10056 if (ex == x && ey == y) /* do not check changing element itself */
10059 if (content_element == EL_EMPTY_SPACE)
10061 can_replace[xx][yy] = FALSE; /* do not replace border with space */
10066 if (!IN_LEV_FIELD(ex, ey))
10068 can_replace[xx][yy] = FALSE;
10069 complete_replace = FALSE;
10076 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10077 e = MovingOrBlocked2Element(ex, ey);
10079 is_empty = (IS_FREE(ex, ey) ||
10080 (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
10082 is_walkable = (is_empty || IS_WALKABLE(e));
10083 is_diggable = (is_empty || IS_DIGGABLE(e));
10084 is_collectible = (is_empty || IS_COLLECTIBLE(e));
10085 is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
10086 is_removable = (is_diggable || is_collectible);
10088 can_replace[xx][yy] =
10089 (((change->replace_when == CP_WHEN_EMPTY && is_empty) ||
10090 (change->replace_when == CP_WHEN_WALKABLE && is_walkable) ||
10091 (change->replace_when == CP_WHEN_DIGGABLE && is_diggable) ||
10092 (change->replace_when == CP_WHEN_COLLECTIBLE && is_collectible) ||
10093 (change->replace_when == CP_WHEN_REMOVABLE && is_removable) ||
10094 (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
10095 !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
10097 if (!can_replace[xx][yy])
10098 complete_replace = FALSE;
10101 if (!change->only_if_complete || complete_replace)
10103 boolean something_has_changed = FALSE;
10105 if (change->only_if_complete && change->use_random_replace &&
10106 RND(100) < change->random_percentage)
10109 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10111 int ex = x + xx - 1;
10112 int ey = y + yy - 1;
10113 int content_element;
10115 if (can_replace[xx][yy] && (!change->use_random_replace ||
10116 RND(100) < change->random_percentage))
10118 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10119 RemoveMovingField(ex, ey);
10121 ChangeEvent[ex][ey] = ChangeEvent[x][y];
10123 content_element = change->target_content.e[xx][yy];
10124 target_element = GET_TARGET_ELEMENT(element, content_element, change,
10125 ce_value, ce_score);
10127 CreateElementFromChange(ex, ey, target_element);
10129 something_has_changed = TRUE;
10131 /* for symmetry reasons, freeze newly created border elements */
10132 if (ex != x || ey != y)
10133 Stop[ex][ey] = TRUE; /* no more moving in this frame */
10137 if (something_has_changed)
10139 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10140 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10146 target_element = GET_TARGET_ELEMENT(element, change->target_element, change,
10147 ce_value, ce_score);
10149 if (element == EL_DIAGONAL_GROWING ||
10150 element == EL_DIAGONAL_SHRINKING)
10152 target_element = Store[x][y];
10154 Store[x][y] = EL_EMPTY;
10157 CreateElementFromChange(x, y, target_element);
10159 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10160 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10163 /* this uses direct change before indirect change */
10164 CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
10169 static void HandleElementChange(int x, int y, int page)
10171 int element = MovingOrBlocked2Element(x, y);
10172 struct ElementInfo *ei = &element_info[element];
10173 struct ElementChangeInfo *change = &ei->change_page[page];
10174 boolean handle_action_before_change = FALSE;
10177 if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
10178 !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
10181 printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
10182 x, y, element, element_info[element].token_name);
10183 printf("HandleElementChange(): This should never happen!\n");
10188 /* this can happen with classic bombs on walkable, changing elements */
10189 if (!CAN_CHANGE_OR_HAS_ACTION(element))
10194 if (ChangeDelay[x][y] == 0) /* initialize element change */
10196 ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
10198 if (change->can_change)
10200 /* !!! not clear why graphic animation should be reset at all here !!! */
10201 /* !!! UPDATE: but is needed for correct Snake Bite tail animation !!! */
10202 /* !!! SOLUTION: do not reset if graphics engine set to 4 or above !!! */
10205 GRAPHICAL BUG ADDRESSED BY CHECKING GRAPHICS ENGINE VERSION:
10207 When using an animation frame delay of 1 (this only happens with
10208 "sp_zonk.moving.left/right" in the classic graphics), the default
10209 (non-moving) animation shows wrong animation frames (while the
10210 moving animation, like "sp_zonk.moving.left/right", is correct,
10211 so this graphical bug never shows up with the classic graphics).
10212 For an animation with 4 frames, this causes wrong frames 0,0,1,2
10213 be drawn instead of the correct frames 0,1,2,3. This is caused by
10214 "GfxFrame[][]" being reset *twice* (in two successive frames) after
10215 an element change: First when the change delay ("ChangeDelay[][]")
10216 counter has reached zero after decrementing, then a second time in
10217 the next frame (after "GfxFrame[][]" was already incremented) when
10218 "ChangeDelay[][]" is reset to the initial delay value again.
10220 This causes frame 0 to be drawn twice, while the last frame won't
10221 be drawn anymore, resulting in the wrong frame sequence 0,0,1,2.
10223 As some animations may already be cleverly designed around this bug
10224 (at least the "Snake Bite" snake tail animation does this), it cannot
10225 simply be fixed here without breaking such existing animations.
10226 Unfortunately, it cannot easily be detected if a graphics set was
10227 designed "before" or "after" the bug was fixed. As a workaround,
10228 a new graphics set option "game.graphics_engine_version" was added
10229 to be able to specify the game's major release version for which the
10230 graphics set was designed, which can then be used to decide if the
10231 bugfix should be used (version 4 and above) or not (version 3 or
10232 below, or if no version was specified at all, as with old sets).
10234 (The wrong/fixed animation frames can be tested with the test level set
10235 "test_gfxframe" and level "000", which contains a specially prepared
10236 custom element at level position (x/y) == (11/9) which uses the zonk
10237 animation mentioned above. Using "game.graphics_engine_version: 4"
10238 fixes the wrong animation frames, showing the correct frames 0,1,2,3.
10239 This can also be seen from the debug output for this test element.)
10242 /* when a custom element is about to change (for example by change delay),
10243 do not reset graphic animation when the custom element is moving */
10244 if (game.graphics_engine_version < 4 &&
10247 ResetGfxAnimation(x, y);
10248 ResetRandomAnimationValue(x, y);
10251 if (change->pre_change_function)
10252 change->pre_change_function(x, y);
10256 ChangeDelay[x][y]--;
10258 if (ChangeDelay[x][y] != 0) /* continue element change */
10260 if (change->can_change)
10262 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10264 if (IS_ANIMATED(graphic))
10265 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10267 if (change->change_function)
10268 change->change_function(x, y);
10271 else /* finish element change */
10273 if (ChangePage[x][y] != -1) /* remember page from delayed change */
10275 page = ChangePage[x][y];
10276 ChangePage[x][y] = -1;
10278 change = &ei->change_page[page];
10281 if (IS_MOVING(x, y)) /* never change a running system ;-) */
10283 ChangeDelay[x][y] = 1; /* try change after next move step */
10284 ChangePage[x][y] = page; /* remember page to use for change */
10289 /* special case: set new level random seed before changing element */
10290 if (change->has_action && change->action_type == CA_SET_LEVEL_RANDOM_SEED)
10291 handle_action_before_change = TRUE;
10293 if (change->has_action && handle_action_before_change)
10294 ExecuteCustomElementAction(x, y, element, page);
10296 if (change->can_change)
10298 if (ChangeElement(x, y, element, page))
10300 if (change->post_change_function)
10301 change->post_change_function(x, y);
10305 if (change->has_action && !handle_action_before_change)
10306 ExecuteCustomElementAction(x, y, element, page);
10310 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
10311 int trigger_element,
10313 int trigger_player,
10317 boolean change_done_any = FALSE;
10318 int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
10321 if (!(trigger_events[trigger_element][trigger_event]))
10324 RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
10326 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
10328 int element = EL_CUSTOM_START + i;
10329 boolean change_done = FALSE;
10332 if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
10333 !HAS_ANY_CHANGE_EVENT(element, trigger_event))
10336 for (p = 0; p < element_info[element].num_change_pages; p++)
10338 struct ElementChangeInfo *change = &element_info[element].change_page[p];
10340 if (change->can_change_or_has_action &&
10341 change->has_event[trigger_event] &&
10342 change->trigger_side & trigger_side &&
10343 change->trigger_player & trigger_player &&
10344 change->trigger_page & trigger_page_bits &&
10345 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
10347 change->actual_trigger_element = trigger_element;
10348 change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
10349 change->actual_trigger_player_bits = trigger_player;
10350 change->actual_trigger_side = trigger_side;
10351 change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
10352 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10354 if ((change->can_change && !change_done) || change->has_action)
10358 SCAN_PLAYFIELD(x, y)
10360 if (Feld[x][y] == element)
10362 if (change->can_change && !change_done)
10364 /* if element already changed in this frame, not only prevent
10365 another element change (checked in ChangeElement()), but
10366 also prevent additional element actions for this element */
10368 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
10369 !level.use_action_after_change_bug)
10372 ChangeDelay[x][y] = 1;
10373 ChangeEvent[x][y] = trigger_event;
10375 HandleElementChange(x, y, p);
10377 else if (change->has_action)
10379 /* if element already changed in this frame, not only prevent
10380 another element change (checked in ChangeElement()), but
10381 also prevent additional element actions for this element */
10383 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
10384 !level.use_action_after_change_bug)
10387 ExecuteCustomElementAction(x, y, element, p);
10388 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10393 if (change->can_change)
10395 change_done = TRUE;
10396 change_done_any = TRUE;
10403 RECURSION_LOOP_DETECTION_END();
10405 return change_done_any;
10408 static boolean CheckElementChangeExt(int x, int y,
10410 int trigger_element,
10412 int trigger_player,
10415 boolean change_done = FALSE;
10418 if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
10419 !HAS_ANY_CHANGE_EVENT(element, trigger_event))
10422 if (Feld[x][y] == EL_BLOCKED)
10424 Blocked2Moving(x, y, &x, &y);
10425 element = Feld[x][y];
10428 /* check if element has already changed or is about to change after moving */
10429 if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
10430 Feld[x][y] != element) ||
10432 (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
10433 (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
10434 ChangePage[x][y] != -1)))
10437 RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
10439 for (p = 0; p < element_info[element].num_change_pages; p++)
10441 struct ElementChangeInfo *change = &element_info[element].change_page[p];
10443 /* check trigger element for all events where the element that is checked
10444 for changing interacts with a directly adjacent element -- this is
10445 different to element changes that affect other elements to change on the
10446 whole playfield (which is handeld by CheckTriggeredElementChangeExt()) */
10447 boolean check_trigger_element =
10448 (trigger_event == CE_TOUCHING_X ||
10449 trigger_event == CE_HITTING_X ||
10450 trigger_event == CE_HIT_BY_X ||
10451 trigger_event == CE_DIGGING_X); /* this one was forgotten until 3.2.3 */
10453 if (change->can_change_or_has_action &&
10454 change->has_event[trigger_event] &&
10455 change->trigger_side & trigger_side &&
10456 change->trigger_player & trigger_player &&
10457 (!check_trigger_element ||
10458 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
10460 change->actual_trigger_element = trigger_element;
10461 change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
10462 change->actual_trigger_player_bits = trigger_player;
10463 change->actual_trigger_side = trigger_side;
10464 change->actual_trigger_ce_value = CustomValue[x][y];
10465 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10467 /* special case: trigger element not at (x,y) position for some events */
10468 if (check_trigger_element)
10480 { 0, 0 }, { 0, 0 }, { 0, 0 },
10484 int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
10485 int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
10487 change->actual_trigger_ce_value = CustomValue[xx][yy];
10488 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10491 if (change->can_change && !change_done)
10493 ChangeDelay[x][y] = 1;
10494 ChangeEvent[x][y] = trigger_event;
10496 HandleElementChange(x, y, p);
10498 change_done = TRUE;
10500 else if (change->has_action)
10502 ExecuteCustomElementAction(x, y, element, p);
10503 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10508 RECURSION_LOOP_DETECTION_END();
10510 return change_done;
10513 static void PlayPlayerSound(struct PlayerInfo *player)
10515 int jx = player->jx, jy = player->jy;
10516 int sound_element = player->artwork_element;
10517 int last_action = player->last_action_waiting;
10518 int action = player->action_waiting;
10520 if (player->is_waiting)
10522 if (action != last_action)
10523 PlayLevelSoundElementAction(jx, jy, sound_element, action);
10525 PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
10529 if (action != last_action)
10530 StopSound(element_info[sound_element].sound[last_action]);
10532 if (last_action == ACTION_SLEEPING)
10533 PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
10537 static void PlayAllPlayersSound()
10541 for (i = 0; i < MAX_PLAYERS; i++)
10542 if (stored_player[i].active)
10543 PlayPlayerSound(&stored_player[i]);
10546 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
10548 boolean last_waiting = player->is_waiting;
10549 int move_dir = player->MovDir;
10551 player->dir_waiting = move_dir;
10552 player->last_action_waiting = player->action_waiting;
10556 if (!last_waiting) /* not waiting -> waiting */
10558 player->is_waiting = TRUE;
10560 player->frame_counter_bored =
10562 game.player_boring_delay_fixed +
10563 GetSimpleRandom(game.player_boring_delay_random);
10564 player->frame_counter_sleeping =
10566 game.player_sleeping_delay_fixed +
10567 GetSimpleRandom(game.player_sleeping_delay_random);
10569 InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
10572 if (game.player_sleeping_delay_fixed +
10573 game.player_sleeping_delay_random > 0 &&
10574 player->anim_delay_counter == 0 &&
10575 player->post_delay_counter == 0 &&
10576 FrameCounter >= player->frame_counter_sleeping)
10577 player->is_sleeping = TRUE;
10578 else if (game.player_boring_delay_fixed +
10579 game.player_boring_delay_random > 0 &&
10580 FrameCounter >= player->frame_counter_bored)
10581 player->is_bored = TRUE;
10583 player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
10584 player->is_bored ? ACTION_BORING :
10587 if (player->is_sleeping && player->use_murphy)
10589 /* special case for sleeping Murphy when leaning against non-free tile */
10591 if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
10592 (Feld[player->jx - 1][player->jy] != EL_EMPTY &&
10593 !IS_MOVING(player->jx - 1, player->jy)))
10594 move_dir = MV_LEFT;
10595 else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
10596 (Feld[player->jx + 1][player->jy] != EL_EMPTY &&
10597 !IS_MOVING(player->jx + 1, player->jy)))
10598 move_dir = MV_RIGHT;
10600 player->is_sleeping = FALSE;
10602 player->dir_waiting = move_dir;
10605 if (player->is_sleeping)
10607 if (player->num_special_action_sleeping > 0)
10609 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
10611 int last_special_action = player->special_action_sleeping;
10612 int num_special_action = player->num_special_action_sleeping;
10613 int special_action =
10614 (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
10615 last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
10616 last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
10617 last_special_action + 1 : ACTION_SLEEPING);
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_sleeping = special_action;
10631 if (player->anim_delay_counter > 0)
10633 player->action_waiting = player->special_action_sleeping;
10634 player->anim_delay_counter--;
10636 else if (player->post_delay_counter > 0)
10638 player->post_delay_counter--;
10642 else if (player->is_bored)
10644 if (player->num_special_action_bored > 0)
10646 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
10648 int special_action =
10649 ACTION_BORING_1 + GetSimpleRandom(player->num_special_action_bored);
10650 int special_graphic =
10651 el_act_dir2img(player->artwork_element, special_action, move_dir);
10653 player->anim_delay_counter =
10654 graphic_info[special_graphic].anim_delay_fixed +
10655 GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
10656 player->post_delay_counter =
10657 graphic_info[special_graphic].post_delay_fixed +
10658 GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
10660 player->special_action_bored = special_action;
10663 if (player->anim_delay_counter > 0)
10665 player->action_waiting = player->special_action_bored;
10666 player->anim_delay_counter--;
10668 else if (player->post_delay_counter > 0)
10670 player->post_delay_counter--;
10675 else if (last_waiting) /* waiting -> not waiting */
10677 player->is_waiting = FALSE;
10678 player->is_bored = FALSE;
10679 player->is_sleeping = FALSE;
10681 player->frame_counter_bored = -1;
10682 player->frame_counter_sleeping = -1;
10684 player->anim_delay_counter = 0;
10685 player->post_delay_counter = 0;
10687 player->dir_waiting = player->MovDir;
10688 player->action_waiting = ACTION_DEFAULT;
10690 player->special_action_bored = ACTION_DEFAULT;
10691 player->special_action_sleeping = ACTION_DEFAULT;
10695 static void CheckSaveEngineSnapshot(struct PlayerInfo *player)
10697 if ((!player->is_moving && player->was_moving) ||
10698 (player->MovPos == 0 && player->was_moving) ||
10699 (player->is_snapping && !player->was_snapping) ||
10700 (player->is_dropping && !player->was_dropping))
10702 if (!CheckSaveEngineSnapshotToList())
10705 player->was_moving = FALSE;
10706 player->was_snapping = TRUE;
10707 player->was_dropping = TRUE;
10711 if (player->is_moving)
10712 player->was_moving = TRUE;
10714 if (!player->is_snapping)
10715 player->was_snapping = FALSE;
10717 if (!player->is_dropping)
10718 player->was_dropping = FALSE;
10722 static void CheckSingleStepMode(struct PlayerInfo *player)
10724 if (tape.single_step && tape.recording && !tape.pausing)
10726 /* as it is called "single step mode", just return to pause mode when the
10727 player stopped moving after one tile (or never starts moving at all) */
10728 if (!player->is_moving && !player->is_pushing)
10730 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
10731 SnapField(player, 0, 0); /* stop snapping */
10735 CheckSaveEngineSnapshot(player);
10738 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
10740 int left = player_action & JOY_LEFT;
10741 int right = player_action & JOY_RIGHT;
10742 int up = player_action & JOY_UP;
10743 int down = player_action & JOY_DOWN;
10744 int button1 = player_action & JOY_BUTTON_1;
10745 int button2 = player_action & JOY_BUTTON_2;
10746 int dx = (left ? -1 : right ? 1 : 0);
10747 int dy = (up ? -1 : down ? 1 : 0);
10749 if (!player->active || tape.pausing)
10755 SnapField(player, dx, dy);
10759 DropElement(player);
10761 MovePlayer(player, dx, dy);
10764 CheckSingleStepMode(player);
10766 SetPlayerWaiting(player, FALSE);
10768 return player_action;
10772 /* no actions for this player (no input at player's configured device) */
10774 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
10775 SnapField(player, 0, 0);
10776 CheckGravityMovementWhenNotMoving(player);
10778 if (player->MovPos == 0)
10779 SetPlayerWaiting(player, TRUE);
10781 if (player->MovPos == 0) /* needed for tape.playing */
10782 player->is_moving = FALSE;
10784 player->is_dropping = FALSE;
10785 player->is_dropping_pressed = FALSE;
10786 player->drop_pressed_delay = 0;
10788 CheckSingleStepMode(player);
10794 static void CheckLevelTime()
10798 /* !!! SAME CODE AS IN "GameActions()" -- FIX THIS !!! */
10799 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
10801 if (level.native_em_level->lev->home == 0) /* all players at home */
10803 PlayerWins(local_player);
10805 AllPlayersGone = TRUE;
10807 level.native_em_level->lev->home = -1;
10810 if (level.native_em_level->ply[0]->alive == 0 &&
10811 level.native_em_level->ply[1]->alive == 0 &&
10812 level.native_em_level->ply[2]->alive == 0 &&
10813 level.native_em_level->ply[3]->alive == 0) /* all dead */
10814 AllPlayersGone = TRUE;
10816 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
10818 if (game_sp.LevelSolved &&
10819 !game_sp.GameOver) /* game won */
10821 PlayerWins(local_player);
10823 game_sp.GameOver = TRUE;
10825 AllPlayersGone = TRUE;
10828 if (game_sp.GameOver) /* game lost */
10829 AllPlayersGone = TRUE;
10832 if (TimeFrames >= FRAMES_PER_SECOND)
10837 for (i = 0; i < MAX_PLAYERS; i++)
10839 struct PlayerInfo *player = &stored_player[i];
10841 if (SHIELD_ON(player))
10843 player->shield_normal_time_left--;
10845 if (player->shield_deadly_time_left > 0)
10846 player->shield_deadly_time_left--;
10850 if (!local_player->LevelSolved && !level.use_step_counter)
10858 if (TimeLeft <= 10 && setup.time_limit)
10859 PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
10861 /* this does not make sense: game_panel_controls[GAME_PANEL_TIME].value
10862 is reset from other values in UpdateGameDoorValues() -- FIX THIS */
10864 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
10866 if (!TimeLeft && setup.time_limit)
10868 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
10869 level.native_em_level->lev->killed_out_of_time = TRUE;
10871 for (i = 0; i < MAX_PLAYERS; i++)
10872 KillPlayer(&stored_player[i]);
10875 else if (game.no_time_limit && !AllPlayersGone) /* level w/o time limit */
10877 game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
10880 level.native_em_level->lev->time =
10881 (game.no_time_limit ? TimePlayed : TimeLeft);
10884 if (tape.recording || tape.playing)
10885 DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
10888 if (tape.recording || tape.playing)
10889 DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
10891 UpdateAndDisplayGameControlValues();
10894 void AdvanceFrameAndPlayerCounters(int player_nr)
10898 /* advance frame counters (global frame counter and time frame counter) */
10902 /* advance player counters (counters for move delay, move animation etc.) */
10903 for (i = 0; i < MAX_PLAYERS; i++)
10905 boolean advance_player_counters = (player_nr == -1 || player_nr == i);
10906 int move_delay_value = stored_player[i].move_delay_value;
10907 int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
10909 if (!advance_player_counters) /* not all players may be affected */
10912 if (move_frames == 0) /* less than one move per game frame */
10914 int stepsize = TILEX / move_delay_value;
10915 int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
10916 int count = (stored_player[i].is_moving ?
10917 ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
10919 if (count % delay == 0)
10923 stored_player[i].Frame += move_frames;
10925 if (stored_player[i].MovPos != 0)
10926 stored_player[i].StepFrame += move_frames;
10928 if (stored_player[i].move_delay > 0)
10929 stored_player[i].move_delay--;
10931 /* due to bugs in previous versions, counter must count up, not down */
10932 if (stored_player[i].push_delay != -1)
10933 stored_player[i].push_delay++;
10935 if (stored_player[i].drop_delay > 0)
10936 stored_player[i].drop_delay--;
10938 if (stored_player[i].is_dropping_pressed)
10939 stored_player[i].drop_pressed_delay++;
10943 void StartGameActions(boolean init_network_game, boolean record_tape,
10946 unsigned int new_random_seed = InitRND(random_seed);
10949 TapeStartRecording(new_random_seed);
10951 #if defined(NETWORK_AVALIABLE)
10952 if (init_network_game)
10954 SendToServer_StartPlaying();
10963 void GameActionsExt()
10966 static unsigned int game_frame_delay = 0;
10968 unsigned int game_frame_delay_value;
10969 byte *recorded_player_action;
10970 byte summarized_player_action = 0;
10971 byte tape_action[MAX_PLAYERS];
10974 /* detect endless loops, caused by custom element programming */
10975 if (recursion_loop_detected && recursion_loop_depth == 0)
10977 char *message = getStringCat3("Internal Error! Element ",
10978 EL_NAME(recursion_loop_element),
10979 " caused endless loop! Quit the game?");
10981 Error(ERR_WARN, "element '%s' caused endless loop in game engine",
10982 EL_NAME(recursion_loop_element));
10984 RequestQuitGameExt(FALSE, level_editor_test_game, message);
10986 recursion_loop_detected = FALSE; /* if game should be continued */
10993 if (game.restart_level)
10994 StartGameActions(options.network, setup.autorecord, level.random_seed);
10996 /* !!! SAME CODE AS IN "CheckLevelTime()" -- FIX THIS !!! */
10997 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
10999 if (level.native_em_level->lev->home == 0) /* all players at home */
11001 PlayerWins(local_player);
11003 AllPlayersGone = TRUE;
11005 level.native_em_level->lev->home = -1;
11008 if (level.native_em_level->ply[0]->alive == 0 &&
11009 level.native_em_level->ply[1]->alive == 0 &&
11010 level.native_em_level->ply[2]->alive == 0 &&
11011 level.native_em_level->ply[3]->alive == 0) /* all dead */
11012 AllPlayersGone = TRUE;
11014 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11016 if (game_sp.LevelSolved &&
11017 !game_sp.GameOver) /* game won */
11019 PlayerWins(local_player);
11021 game_sp.GameOver = TRUE;
11023 AllPlayersGone = TRUE;
11026 if (game_sp.GameOver) /* game lost */
11027 AllPlayersGone = TRUE;
11030 if (local_player->LevelSolved && !local_player->LevelSolved_GameEnd)
11033 if (AllPlayersGone && !TAPE_IS_STOPPED(tape))
11036 if (game_status != GAME_MODE_PLAYING) /* status might have changed */
11039 game_frame_delay_value =
11040 (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
11042 if (tape.playing && tape.warp_forward && !tape.pausing)
11043 game_frame_delay_value = 0;
11045 SetVideoFrameDelay(game_frame_delay_value);
11049 /* ---------- main game synchronization point ---------- */
11051 int skip = WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11053 printf("::: skip == %d\n", skip);
11056 /* ---------- main game synchronization point ---------- */
11058 WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11062 if (network_playing && !network_player_action_received)
11064 /* try to get network player actions in time */
11066 #if defined(NETWORK_AVALIABLE)
11067 /* last chance to get network player actions without main loop delay */
11068 HandleNetworking();
11071 /* game was quit by network peer */
11072 if (game_status != GAME_MODE_PLAYING)
11075 if (!network_player_action_received)
11076 return; /* failed to get network player actions in time */
11078 /* do not yet reset "network_player_action_received" (for tape.pausing) */
11084 /* at this point we know that we really continue executing the game */
11086 network_player_action_received = FALSE;
11088 /* when playing tape, read previously recorded player input from tape data */
11089 recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
11091 /* TapePlayAction() may return NULL when toggling to "pause before death" */
11095 if (tape.set_centered_player)
11097 game.centered_player_nr_next = tape.centered_player_nr_next;
11098 game.set_centered_player = TRUE;
11101 for (i = 0; i < MAX_PLAYERS; i++)
11103 summarized_player_action |= stored_player[i].action;
11105 if (!network_playing && (game.team_mode || tape.playing))
11106 stored_player[i].effective_action = stored_player[i].action;
11109 #if defined(NETWORK_AVALIABLE)
11110 if (network_playing)
11111 SendToServer_MovePlayer(summarized_player_action);
11114 // summarize all actions at local players mapped input device position
11115 // (this allows using different input devices in single player mode)
11116 if (!options.network && !game.team_mode)
11117 stored_player[map_player_action[local_player->index_nr]].effective_action =
11118 summarized_player_action;
11120 if (tape.recording &&
11122 setup.input_on_focus &&
11123 game.centered_player_nr != -1)
11125 for (i = 0; i < MAX_PLAYERS; i++)
11126 stored_player[i].effective_action =
11127 (i == game.centered_player_nr ? summarized_player_action : 0);
11130 if (recorded_player_action != NULL)
11131 for (i = 0; i < MAX_PLAYERS; i++)
11132 stored_player[i].effective_action = recorded_player_action[i];
11134 for (i = 0; i < MAX_PLAYERS; i++)
11136 tape_action[i] = stored_player[i].effective_action;
11138 /* (this may happen in the RND game engine if a player was not present on
11139 the playfield on level start, but appeared later from a custom element */
11140 if (setup.team_mode &&
11143 !tape.player_participates[i])
11144 tape.player_participates[i] = TRUE;
11147 /* only record actions from input devices, but not programmed actions */
11148 if (tape.recording)
11149 TapeRecordAction(tape_action);
11151 #if USE_NEW_PLAYER_ASSIGNMENTS
11152 // !!! also map player actions in single player mode !!!
11153 // if (game.team_mode)
11156 byte mapped_action[MAX_PLAYERS];
11158 #if DEBUG_PLAYER_ACTIONS
11160 for (i = 0; i < MAX_PLAYERS; i++)
11161 printf(" %d, ", stored_player[i].effective_action);
11164 for (i = 0; i < MAX_PLAYERS; i++)
11165 mapped_action[i] = stored_player[map_player_action[i]].effective_action;
11167 for (i = 0; i < MAX_PLAYERS; i++)
11168 stored_player[i].effective_action = mapped_action[i];
11170 #if DEBUG_PLAYER_ACTIONS
11172 for (i = 0; i < MAX_PLAYERS; i++)
11173 printf(" %d, ", stored_player[i].effective_action);
11177 #if DEBUG_PLAYER_ACTIONS
11181 for (i = 0; i < MAX_PLAYERS; i++)
11182 printf(" %d, ", stored_player[i].effective_action);
11188 for (i = 0; i < MAX_PLAYERS; i++)
11190 // allow engine snapshot in case of changed movement attempt
11191 if ((game.snapshot.last_action[i] & KEY_MOTION) !=
11192 (stored_player[i].effective_action & KEY_MOTION))
11193 game.snapshot.changed_action = TRUE;
11195 // allow engine snapshot in case of snapping/dropping attempt
11196 if ((game.snapshot.last_action[i] & KEY_BUTTON) == 0 &&
11197 (stored_player[i].effective_action & KEY_BUTTON) != 0)
11198 game.snapshot.changed_action = TRUE;
11200 game.snapshot.last_action[i] = stored_player[i].effective_action;
11203 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11205 GameActions_EM_Main();
11207 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11209 GameActions_SP_Main();
11213 GameActions_RND_Main();
11216 BlitScreenToBitmap(backbuffer);
11220 AdvanceFrameAndPlayerCounters(-1); /* advance counters for all players */
11222 if (options.debug) /* calculate frames per second */
11224 static unsigned int fps_counter = 0;
11225 static int fps_frames = 0;
11226 unsigned int fps_delay_ms = Counter() - fps_counter;
11230 if (fps_delay_ms >= 500) /* calculate fps every 0.5 seconds */
11232 global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
11235 fps_counter = Counter();
11238 redraw_mask |= REDRAW_FPS;
11242 static void GameActions_CheckSaveEngineSnapshot()
11244 if (!game.snapshot.save_snapshot)
11247 // clear flag for saving snapshot _before_ saving snapshot
11248 game.snapshot.save_snapshot = FALSE;
11250 SaveEngineSnapshotToList();
11257 GameActions_CheckSaveEngineSnapshot();
11260 void GameActions_EM_Main()
11262 byte effective_action[MAX_PLAYERS];
11263 boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11266 for (i = 0; i < MAX_PLAYERS; i++)
11267 effective_action[i] = stored_player[i].effective_action;
11269 GameActions_EM(effective_action, warp_mode);
11272 void GameActions_SP_Main()
11274 byte effective_action[MAX_PLAYERS];
11275 boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11278 for (i = 0; i < MAX_PLAYERS; i++)
11279 effective_action[i] = stored_player[i].effective_action;
11281 GameActions_SP(effective_action, warp_mode);
11284 void GameActions_RND_Main()
11289 void GameActions_RND()
11291 int magic_wall_x = 0, magic_wall_y = 0;
11292 int i, x, y, element, graphic;
11294 InitPlayfieldScanModeVars();
11296 if (game.engine_version >= VERSION_IDENT(3,2,0,7))
11298 SCAN_PLAYFIELD(x, y)
11300 ChangeCount[x][y] = 0;
11301 ChangeEvent[x][y] = -1;
11305 if (game.set_centered_player)
11307 boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
11309 /* switching to "all players" only possible if all players fit to screen */
11310 if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
11312 game.centered_player_nr_next = game.centered_player_nr;
11313 game.set_centered_player = FALSE;
11316 /* do not switch focus to non-existing (or non-active) player */
11317 if (game.centered_player_nr_next >= 0 &&
11318 !stored_player[game.centered_player_nr_next].active)
11320 game.centered_player_nr_next = game.centered_player_nr;
11321 game.set_centered_player = FALSE;
11325 if (game.set_centered_player &&
11326 ScreenMovPos == 0) /* screen currently aligned at tile position */
11330 if (game.centered_player_nr_next == -1)
11332 setScreenCenteredToAllPlayers(&sx, &sy);
11336 sx = stored_player[game.centered_player_nr_next].jx;
11337 sy = stored_player[game.centered_player_nr_next].jy;
11340 game.centered_player_nr = game.centered_player_nr_next;
11341 game.set_centered_player = FALSE;
11343 DrawRelocateScreen(0, 0, sx, sy, MV_NONE, TRUE, setup.quick_switch);
11344 DrawGameDoorValues();
11347 for (i = 0; i < MAX_PLAYERS; i++)
11349 int actual_player_action = stored_player[i].effective_action;
11352 /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
11353 - rnd_equinox_tetrachloride 048
11354 - rnd_equinox_tetrachloride_ii 096
11355 - rnd_emanuel_schmieg 002
11356 - doctor_sloan_ww 001, 020
11358 if (stored_player[i].MovPos == 0)
11359 CheckGravityMovement(&stored_player[i]);
11362 /* overwrite programmed action with tape action */
11363 if (stored_player[i].programmed_action)
11364 actual_player_action = stored_player[i].programmed_action;
11366 PlayerActions(&stored_player[i], actual_player_action);
11368 ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
11371 ScrollScreen(NULL, SCROLL_GO_ON);
11373 /* for backwards compatibility, the following code emulates a fixed bug that
11374 occured when pushing elements (causing elements that just made their last
11375 pushing step to already (if possible) make their first falling step in the
11376 same game frame, which is bad); this code is also needed to use the famous
11377 "spring push bug" which is used in older levels and might be wanted to be
11378 used also in newer levels, but in this case the buggy pushing code is only
11379 affecting the "spring" element and no other elements */
11381 if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
11383 for (i = 0; i < MAX_PLAYERS; i++)
11385 struct PlayerInfo *player = &stored_player[i];
11386 int x = player->jx;
11387 int y = player->jy;
11389 if (player->active && player->is_pushing && player->is_moving &&
11391 (game.engine_version < VERSION_IDENT(2,2,0,7) ||
11392 Feld[x][y] == EL_SPRING))
11394 ContinueMoving(x, y);
11396 /* continue moving after pushing (this is actually a bug) */
11397 if (!IS_MOVING(x, y))
11398 Stop[x][y] = FALSE;
11403 SCAN_PLAYFIELD(x, y)
11405 ChangeCount[x][y] = 0;
11406 ChangeEvent[x][y] = -1;
11408 /* this must be handled before main playfield loop */
11409 if (Feld[x][y] == EL_PLAYER_IS_LEAVING)
11412 if (MovDelay[x][y] <= 0)
11416 if (Feld[x][y] == EL_ELEMENT_SNAPPING)
11419 if (MovDelay[x][y] <= 0)
11422 TEST_DrawLevelField(x, y);
11424 TestIfElementTouchesCustomElement(x, y); /* for empty space */
11429 if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
11431 printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
11432 printf("GameActions(): This should never happen!\n");
11434 ChangePage[x][y] = -1;
11438 Stop[x][y] = FALSE;
11439 if (WasJustMoving[x][y] > 0)
11440 WasJustMoving[x][y]--;
11441 if (WasJustFalling[x][y] > 0)
11442 WasJustFalling[x][y]--;
11443 if (CheckCollision[x][y] > 0)
11444 CheckCollision[x][y]--;
11445 if (CheckImpact[x][y] > 0)
11446 CheckImpact[x][y]--;
11450 /* reset finished pushing action (not done in ContinueMoving() to allow
11451 continuous pushing animation for elements with zero push delay) */
11452 if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
11454 ResetGfxAnimation(x, y);
11455 TEST_DrawLevelField(x, y);
11459 if (IS_BLOCKED(x, y))
11463 Blocked2Moving(x, y, &oldx, &oldy);
11464 if (!IS_MOVING(oldx, oldy))
11466 printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
11467 printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
11468 printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
11469 printf("GameActions(): This should never happen!\n");
11475 SCAN_PLAYFIELD(x, y)
11477 element = Feld[x][y];
11478 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11480 ResetGfxFrame(x, y, TRUE);
11482 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
11483 IS_NEXT_FRAME(GfxFrame[x][y], graphic))
11484 ResetRandomAnimationValue(x, y);
11486 SetRandomAnimationValue(x, y);
11488 PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
11490 if (IS_INACTIVE(element))
11492 if (IS_ANIMATED(graphic))
11493 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11498 /* this may take place after moving, so 'element' may have changed */
11499 if (IS_CHANGING(x, y) &&
11500 (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
11502 int page = element_info[element].event_page_nr[CE_DELAY];
11504 HandleElementChange(x, y, page);
11506 element = Feld[x][y];
11507 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11510 if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
11514 element = Feld[x][y];
11515 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11517 if (IS_ANIMATED(graphic) &&
11518 !IS_MOVING(x, y) &&
11520 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11522 if (IS_GEM(element) || element == EL_SP_INFOTRON)
11523 TEST_DrawTwinkleOnField(x, y);
11525 else if ((element == EL_ACID ||
11526 element == EL_EXIT_OPEN ||
11527 element == EL_EM_EXIT_OPEN ||
11528 element == EL_SP_EXIT_OPEN ||
11529 element == EL_STEEL_EXIT_OPEN ||
11530 element == EL_EM_STEEL_EXIT_OPEN ||
11531 element == EL_SP_TERMINAL ||
11532 element == EL_SP_TERMINAL_ACTIVE ||
11533 element == EL_EXTRA_TIME ||
11534 element == EL_SHIELD_NORMAL ||
11535 element == EL_SHIELD_DEADLY) &&
11536 IS_ANIMATED(graphic))
11537 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11538 else if (IS_MOVING(x, y))
11539 ContinueMoving(x, y);
11540 else if (IS_ACTIVE_BOMB(element))
11541 CheckDynamite(x, y);
11542 else if (element == EL_AMOEBA_GROWING)
11543 AmoebeWaechst(x, y);
11544 else if (element == EL_AMOEBA_SHRINKING)
11545 AmoebaDisappearing(x, y);
11547 #if !USE_NEW_AMOEBA_CODE
11548 else if (IS_AMOEBALIVE(element))
11549 AmoebeAbleger(x, y);
11552 else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
11554 else if (element == EL_EXIT_CLOSED)
11556 else if (element == EL_EM_EXIT_CLOSED)
11558 else if (element == EL_STEEL_EXIT_CLOSED)
11559 CheckExitSteel(x, y);
11560 else if (element == EL_EM_STEEL_EXIT_CLOSED)
11561 CheckExitSteelEM(x, y);
11562 else if (element == EL_SP_EXIT_CLOSED)
11564 else if (element == EL_EXPANDABLE_WALL_GROWING ||
11565 element == EL_EXPANDABLE_STEELWALL_GROWING)
11566 MauerWaechst(x, y);
11567 else if (element == EL_EXPANDABLE_WALL ||
11568 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
11569 element == EL_EXPANDABLE_WALL_VERTICAL ||
11570 element == EL_EXPANDABLE_WALL_ANY ||
11571 element == EL_BD_EXPANDABLE_WALL)
11572 MauerAbleger(x, y);
11573 else if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
11574 element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
11575 element == EL_EXPANDABLE_STEELWALL_ANY)
11576 MauerAblegerStahl(x, y);
11577 else if (element == EL_FLAMES)
11578 CheckForDragon(x, y);
11579 else if (element == EL_EXPLOSION)
11580 ; /* drawing of correct explosion animation is handled separately */
11581 else if (element == EL_ELEMENT_SNAPPING ||
11582 element == EL_DIAGONAL_SHRINKING ||
11583 element == EL_DIAGONAL_GROWING)
11585 graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
11587 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11589 else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
11590 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11592 if (IS_BELT_ACTIVE(element))
11593 PlayLevelSoundAction(x, y, ACTION_ACTIVE);
11595 if (game.magic_wall_active)
11597 int jx = local_player->jx, jy = local_player->jy;
11599 /* play the element sound at the position nearest to the player */
11600 if ((element == EL_MAGIC_WALL_FULL ||
11601 element == EL_MAGIC_WALL_ACTIVE ||
11602 element == EL_MAGIC_WALL_EMPTYING ||
11603 element == EL_BD_MAGIC_WALL_FULL ||
11604 element == EL_BD_MAGIC_WALL_ACTIVE ||
11605 element == EL_BD_MAGIC_WALL_EMPTYING ||
11606 element == EL_DC_MAGIC_WALL_FULL ||
11607 element == EL_DC_MAGIC_WALL_ACTIVE ||
11608 element == EL_DC_MAGIC_WALL_EMPTYING) &&
11609 ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
11617 #if USE_NEW_AMOEBA_CODE
11618 /* new experimental amoeba growth stuff */
11619 if (!(FrameCounter % 8))
11621 static unsigned int random = 1684108901;
11623 for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
11625 x = RND(lev_fieldx);
11626 y = RND(lev_fieldy);
11627 element = Feld[x][y];
11629 if (!IS_PLAYER(x,y) &&
11630 (element == EL_EMPTY ||
11631 CAN_GROW_INTO(element) ||
11632 element == EL_QUICKSAND_EMPTY ||
11633 element == EL_QUICKSAND_FAST_EMPTY ||
11634 element == EL_ACID_SPLASH_LEFT ||
11635 element == EL_ACID_SPLASH_RIGHT))
11637 if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
11638 (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
11639 (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
11640 (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
11641 Feld[x][y] = EL_AMOEBA_DROP;
11644 random = random * 129 + 1;
11649 game.explosions_delayed = FALSE;
11651 SCAN_PLAYFIELD(x, y)
11653 element = Feld[x][y];
11655 if (ExplodeField[x][y])
11656 Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
11657 else if (element == EL_EXPLOSION)
11658 Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
11660 ExplodeField[x][y] = EX_TYPE_NONE;
11663 game.explosions_delayed = TRUE;
11665 if (game.magic_wall_active)
11667 if (!(game.magic_wall_time_left % 4))
11669 int element = Feld[magic_wall_x][magic_wall_y];
11671 if (element == EL_BD_MAGIC_WALL_FULL ||
11672 element == EL_BD_MAGIC_WALL_ACTIVE ||
11673 element == EL_BD_MAGIC_WALL_EMPTYING)
11674 PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
11675 else if (element == EL_DC_MAGIC_WALL_FULL ||
11676 element == EL_DC_MAGIC_WALL_ACTIVE ||
11677 element == EL_DC_MAGIC_WALL_EMPTYING)
11678 PlayLevelSound(magic_wall_x, magic_wall_y, SND_DC_MAGIC_WALL_ACTIVE);
11680 PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
11683 if (game.magic_wall_time_left > 0)
11685 game.magic_wall_time_left--;
11687 if (!game.magic_wall_time_left)
11689 SCAN_PLAYFIELD(x, y)
11691 element = Feld[x][y];
11693 if (element == EL_MAGIC_WALL_ACTIVE ||
11694 element == EL_MAGIC_WALL_FULL)
11696 Feld[x][y] = EL_MAGIC_WALL_DEAD;
11697 TEST_DrawLevelField(x, y);
11699 else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
11700 element == EL_BD_MAGIC_WALL_FULL)
11702 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
11703 TEST_DrawLevelField(x, y);
11705 else if (element == EL_DC_MAGIC_WALL_ACTIVE ||
11706 element == EL_DC_MAGIC_WALL_FULL)
11708 Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
11709 TEST_DrawLevelField(x, y);
11713 game.magic_wall_active = FALSE;
11718 if (game.light_time_left > 0)
11720 game.light_time_left--;
11722 if (game.light_time_left == 0)
11723 RedrawAllLightSwitchesAndInvisibleElements();
11726 if (game.timegate_time_left > 0)
11728 game.timegate_time_left--;
11730 if (game.timegate_time_left == 0)
11731 CloseAllOpenTimegates();
11734 if (game.lenses_time_left > 0)
11736 game.lenses_time_left--;
11738 if (game.lenses_time_left == 0)
11739 RedrawAllInvisibleElementsForLenses();
11742 if (game.magnify_time_left > 0)
11744 game.magnify_time_left--;
11746 if (game.magnify_time_left == 0)
11747 RedrawAllInvisibleElementsForMagnifier();
11750 for (i = 0; i < MAX_PLAYERS; i++)
11752 struct PlayerInfo *player = &stored_player[i];
11754 if (SHIELD_ON(player))
11756 if (player->shield_deadly_time_left)
11757 PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
11758 else if (player->shield_normal_time_left)
11759 PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
11763 #if USE_DELAYED_GFX_REDRAW
11764 SCAN_PLAYFIELD(x, y)
11766 if (GfxRedraw[x][y] != GFX_REDRAW_NONE)
11768 /* !!! PROBLEM: THIS REDRAWS THE PLAYFIELD _AFTER_ THE SCAN, BUT TILES
11769 !!! MAY HAVE CHANGED AFTER BEING DRAWN DURING PLAYFIELD SCAN !!! */
11771 if (GfxRedraw[x][y] & GFX_REDRAW_TILE)
11772 DrawLevelField(x, y);
11774 if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED)
11775 DrawLevelFieldCrumbled(x, y);
11777 if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS)
11778 DrawLevelFieldCrumbledNeighbours(x, y);
11780 if (GfxRedraw[x][y] & GFX_REDRAW_TILE_TWINKLED)
11781 DrawTwinkleOnField(x, y);
11784 GfxRedraw[x][y] = GFX_REDRAW_NONE;
11789 PlayAllPlayersSound();
11791 if (local_player->show_envelope != 0 && local_player->MovPos == 0)
11793 ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
11795 local_player->show_envelope = 0;
11798 /* use random number generator in every frame to make it less predictable */
11799 if (game.engine_version >= VERSION_IDENT(3,1,1,0))
11803 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
11805 int min_x = x, min_y = y, max_x = x, max_y = y;
11808 for (i = 0; i < MAX_PLAYERS; i++)
11810 int jx = stored_player[i].jx, jy = stored_player[i].jy;
11812 if (!stored_player[i].active || &stored_player[i] == player)
11815 min_x = MIN(min_x, jx);
11816 min_y = MIN(min_y, jy);
11817 max_x = MAX(max_x, jx);
11818 max_y = MAX(max_y, jy);
11821 return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
11824 static boolean AllPlayersInVisibleScreen()
11828 for (i = 0; i < MAX_PLAYERS; i++)
11830 int jx = stored_player[i].jx, jy = stored_player[i].jy;
11832 if (!stored_player[i].active)
11835 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
11842 void ScrollLevel(int dx, int dy)
11844 int scroll_offset = 2 * TILEX_VAR;
11847 BlitBitmap(drawto_field, drawto_field,
11848 FX + TILEX_VAR * (dx == -1) - scroll_offset,
11849 FY + TILEY_VAR * (dy == -1) - scroll_offset,
11850 SXSIZE - TILEX_VAR * (dx != 0) + 2 * scroll_offset,
11851 SYSIZE - TILEY_VAR * (dy != 0) + 2 * scroll_offset,
11852 FX + TILEX_VAR * (dx == 1) - scroll_offset,
11853 FY + TILEY_VAR * (dy == 1) - scroll_offset);
11857 x = (dx == 1 ? BX1 : BX2);
11858 for (y = BY1; y <= BY2; y++)
11859 DrawScreenField(x, y);
11864 y = (dy == 1 ? BY1 : BY2);
11865 for (x = BX1; x <= BX2; x++)
11866 DrawScreenField(x, y);
11869 redraw_mask |= REDRAW_FIELD;
11872 static boolean canFallDown(struct PlayerInfo *player)
11874 int jx = player->jx, jy = player->jy;
11876 return (IN_LEV_FIELD(jx, jy + 1) &&
11877 (IS_FREE(jx, jy + 1) ||
11878 (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
11879 IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
11880 !IS_WALKABLE_INSIDE(Feld[jx][jy]));
11883 static boolean canPassField(int x, int y, int move_dir)
11885 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
11886 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
11887 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
11888 int nextx = x + dx;
11889 int nexty = y + dy;
11890 int element = Feld[x][y];
11892 return (IS_PASSABLE_FROM(element, opposite_dir) &&
11893 !CAN_MOVE(element) &&
11894 IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
11895 IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
11896 (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
11899 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
11901 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
11902 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
11903 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
11907 return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
11908 IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
11909 (IS_DIGGABLE(Feld[newx][newy]) ||
11910 IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
11911 canPassField(newx, newy, move_dir)));
11914 static void CheckGravityMovement(struct PlayerInfo *player)
11916 if (player->gravity && !player->programmed_action)
11918 int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
11919 int move_dir_vertical = player->effective_action & MV_VERTICAL;
11920 boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
11921 int jx = player->jx, jy = player->jy;
11922 boolean player_is_moving_to_valid_field =
11923 (!player_is_snapping &&
11924 (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
11925 canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
11926 boolean player_can_fall_down = canFallDown(player);
11928 if (player_can_fall_down &&
11929 !player_is_moving_to_valid_field)
11930 player->programmed_action = MV_DOWN;
11934 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
11936 return CheckGravityMovement(player);
11938 if (player->gravity && !player->programmed_action)
11940 int jx = player->jx, jy = player->jy;
11941 boolean field_under_player_is_free =
11942 (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
11943 boolean player_is_standing_on_valid_field =
11944 (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
11945 (IS_WALKABLE(Feld[jx][jy]) &&
11946 !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
11948 if (field_under_player_is_free && !player_is_standing_on_valid_field)
11949 player->programmed_action = MV_DOWN;
11954 MovePlayerOneStep()
11955 -----------------------------------------------------------------------------
11956 dx, dy: direction (non-diagonal) to try to move the player to
11957 real_dx, real_dy: direction as read from input device (can be diagonal)
11960 boolean MovePlayerOneStep(struct PlayerInfo *player,
11961 int dx, int dy, int real_dx, int real_dy)
11963 int jx = player->jx, jy = player->jy;
11964 int new_jx = jx + dx, new_jy = jy + dy;
11966 boolean player_can_move = !player->cannot_move;
11968 if (!player->active || (!dx && !dy))
11969 return MP_NO_ACTION;
11971 player->MovDir = (dx < 0 ? MV_LEFT :
11972 dx > 0 ? MV_RIGHT :
11974 dy > 0 ? MV_DOWN : MV_NONE);
11976 if (!IN_LEV_FIELD(new_jx, new_jy))
11977 return MP_NO_ACTION;
11979 if (!player_can_move)
11981 if (player->MovPos == 0)
11983 player->is_moving = FALSE;
11984 player->is_digging = FALSE;
11985 player->is_collecting = FALSE;
11986 player->is_snapping = FALSE;
11987 player->is_pushing = FALSE;
11991 if (!options.network && game.centered_player_nr == -1 &&
11992 !AllPlayersInSight(player, new_jx, new_jy))
11993 return MP_NO_ACTION;
11995 can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
11996 if (can_move != MP_MOVING)
11999 /* check if DigField() has caused relocation of the player */
12000 if (player->jx != jx || player->jy != jy)
12001 return MP_NO_ACTION; /* <-- !!! CHECK THIS [-> MP_ACTION ?] !!! */
12003 StorePlayer[jx][jy] = 0;
12004 player->last_jx = jx;
12005 player->last_jy = jy;
12006 player->jx = new_jx;
12007 player->jy = new_jy;
12008 StorePlayer[new_jx][new_jy] = player->element_nr;
12010 if (player->move_delay_value_next != -1)
12012 player->move_delay_value = player->move_delay_value_next;
12013 player->move_delay_value_next = -1;
12017 (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
12019 player->step_counter++;
12021 PlayerVisit[jx][jy] = FrameCounter;
12023 player->is_moving = TRUE;
12026 /* should better be called in MovePlayer(), but this breaks some tapes */
12027 ScrollPlayer(player, SCROLL_INIT);
12033 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
12035 int jx = player->jx, jy = player->jy;
12036 int old_jx = jx, old_jy = jy;
12037 int moved = MP_NO_ACTION;
12039 if (!player->active)
12044 if (player->MovPos == 0)
12046 player->is_moving = FALSE;
12047 player->is_digging = FALSE;
12048 player->is_collecting = FALSE;
12049 player->is_snapping = FALSE;
12050 player->is_pushing = FALSE;
12056 if (player->move_delay > 0)
12059 player->move_delay = -1; /* set to "uninitialized" value */
12061 /* store if player is automatically moved to next field */
12062 player->is_auto_moving = (player->programmed_action != MV_NONE);
12064 /* remove the last programmed player action */
12065 player->programmed_action = 0;
12067 if (player->MovPos)
12069 /* should only happen if pre-1.2 tape recordings are played */
12070 /* this is only for backward compatibility */
12072 int original_move_delay_value = player->move_delay_value;
12075 printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%d]\n",
12079 /* scroll remaining steps with finest movement resolution */
12080 player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
12082 while (player->MovPos)
12084 ScrollPlayer(player, SCROLL_GO_ON);
12085 ScrollScreen(NULL, SCROLL_GO_ON);
12087 AdvanceFrameAndPlayerCounters(player->index_nr);
12090 BackToFront_WithFrameDelay(0);
12093 player->move_delay_value = original_move_delay_value;
12096 player->is_active = FALSE;
12098 if (player->last_move_dir & MV_HORIZONTAL)
12100 if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
12101 moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
12105 if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
12106 moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
12109 if (!moved && !player->is_active)
12111 player->is_moving = FALSE;
12112 player->is_digging = FALSE;
12113 player->is_collecting = FALSE;
12114 player->is_snapping = FALSE;
12115 player->is_pushing = FALSE;
12121 if (moved & MP_MOVING && !ScreenMovPos &&
12122 (player->index_nr == game.centered_player_nr ||
12123 game.centered_player_nr == -1))
12125 int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
12126 int offset = game.scroll_delay_value;
12128 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12130 /* actual player has left the screen -- scroll in that direction */
12131 if (jx != old_jx) /* player has moved horizontally */
12132 scroll_x += (jx - old_jx);
12133 else /* player has moved vertically */
12134 scroll_y += (jy - old_jy);
12138 if (jx != old_jx) /* player has moved horizontally */
12140 if ((player->MovDir == MV_LEFT && scroll_x > jx - MIDPOSX + offset) ||
12141 (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
12142 scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
12144 /* don't scroll over playfield boundaries */
12145 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
12146 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
12148 /* don't scroll more than one field at a time */
12149 scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
12151 /* don't scroll against the player's moving direction */
12152 if ((player->MovDir == MV_LEFT && scroll_x > old_scroll_x) ||
12153 (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
12154 scroll_x = old_scroll_x;
12156 else /* player has moved vertically */
12158 if ((player->MovDir == MV_UP && scroll_y > jy - MIDPOSY + offset) ||
12159 (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
12160 scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
12162 /* don't scroll over playfield boundaries */
12163 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
12164 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
12166 /* don't scroll more than one field at a time */
12167 scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
12169 /* don't scroll against the player's moving direction */
12170 if ((player->MovDir == MV_UP && scroll_y > old_scroll_y) ||
12171 (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
12172 scroll_y = old_scroll_y;
12176 if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
12178 if (!options.network && game.centered_player_nr == -1 &&
12179 !AllPlayersInVisibleScreen())
12181 scroll_x = old_scroll_x;
12182 scroll_y = old_scroll_y;
12186 ScrollScreen(player, SCROLL_INIT);
12187 ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
12192 player->StepFrame = 0;
12194 if (moved & MP_MOVING)
12196 if (old_jx != jx && old_jy == jy)
12197 player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
12198 else if (old_jx == jx && old_jy != jy)
12199 player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
12201 TEST_DrawLevelField(jx, jy); /* for "crumbled sand" */
12203 player->last_move_dir = player->MovDir;
12204 player->is_moving = TRUE;
12205 player->is_snapping = FALSE;
12206 player->is_switching = FALSE;
12207 player->is_dropping = FALSE;
12208 player->is_dropping_pressed = FALSE;
12209 player->drop_pressed_delay = 0;
12212 /* should better be called here than above, but this breaks some tapes */
12213 ScrollPlayer(player, SCROLL_INIT);
12218 CheckGravityMovementWhenNotMoving(player);
12220 player->is_moving = FALSE;
12222 /* at this point, the player is allowed to move, but cannot move right now
12223 (e.g. because of something blocking the way) -- ensure that the player
12224 is also allowed to move in the next frame (in old versions before 3.1.1,
12225 the player was forced to wait again for eight frames before next try) */
12227 if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12228 player->move_delay = 0; /* allow direct movement in the next frame */
12231 if (player->move_delay == -1) /* not yet initialized by DigField() */
12232 player->move_delay = player->move_delay_value;
12234 if (game.engine_version < VERSION_IDENT(3,0,7,0))
12236 TestIfPlayerTouchesBadThing(jx, jy);
12237 TestIfPlayerTouchesCustomElement(jx, jy);
12240 if (!player->active)
12241 RemovePlayer(player);
12246 void ScrollPlayer(struct PlayerInfo *player, int mode)
12248 int jx = player->jx, jy = player->jy;
12249 int last_jx = player->last_jx, last_jy = player->last_jy;
12250 int move_stepsize = TILEX / player->move_delay_value;
12252 if (!player->active)
12255 if (player->MovPos == 0 && mode == SCROLL_GO_ON) /* player not moving */
12258 if (mode == SCROLL_INIT)
12260 player->actual_frame_counter = FrameCounter;
12261 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
12263 if ((player->block_last_field || player->block_delay_adjustment > 0) &&
12264 Feld[last_jx][last_jy] == EL_EMPTY)
12266 int last_field_block_delay = 0; /* start with no blocking at all */
12267 int block_delay_adjustment = player->block_delay_adjustment;
12269 /* if player blocks last field, add delay for exactly one move */
12270 if (player->block_last_field)
12272 last_field_block_delay += player->move_delay_value;
12274 /* when blocking enabled, prevent moving up despite gravity */
12275 if (player->gravity && player->MovDir == MV_UP)
12276 block_delay_adjustment = -1;
12279 /* add block delay adjustment (also possible when not blocking) */
12280 last_field_block_delay += block_delay_adjustment;
12282 Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
12283 MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
12286 if (player->MovPos != 0) /* player has not yet reached destination */
12289 else if (!FrameReached(&player->actual_frame_counter, 1))
12292 if (player->MovPos != 0)
12294 player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
12295 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
12297 /* before DrawPlayer() to draw correct player graphic for this case */
12298 if (player->MovPos == 0)
12299 CheckGravityMovement(player);
12302 if (player->MovPos == 0) /* player reached destination field */
12304 if (player->move_delay_reset_counter > 0)
12306 player->move_delay_reset_counter--;
12308 if (player->move_delay_reset_counter == 0)
12310 /* continue with normal speed after quickly moving through gate */
12311 HALVE_PLAYER_SPEED(player);
12313 /* be able to make the next move without delay */
12314 player->move_delay = 0;
12318 player->last_jx = jx;
12319 player->last_jy = jy;
12321 if (Feld[jx][jy] == EL_EXIT_OPEN ||
12322 Feld[jx][jy] == EL_EM_EXIT_OPEN ||
12323 Feld[jx][jy] == EL_EM_EXIT_OPENING ||
12324 Feld[jx][jy] == EL_STEEL_EXIT_OPEN ||
12325 Feld[jx][jy] == EL_EM_STEEL_EXIT_OPEN ||
12326 Feld[jx][jy] == EL_EM_STEEL_EXIT_OPENING ||
12327 Feld[jx][jy] == EL_SP_EXIT_OPEN ||
12328 Feld[jx][jy] == EL_SP_EXIT_OPENING) /* <-- special case */
12330 DrawPlayer(player); /* needed here only to cleanup last field */
12331 RemovePlayer(player);
12333 if (local_player->friends_still_needed == 0 ||
12334 IS_SP_ELEMENT(Feld[jx][jy]))
12335 PlayerWins(player);
12338 /* this breaks one level: "machine", level 000 */
12340 int move_direction = player->MovDir;
12341 int enter_side = MV_DIR_OPPOSITE(move_direction);
12342 int leave_side = move_direction;
12343 int old_jx = last_jx;
12344 int old_jy = last_jy;
12345 int old_element = Feld[old_jx][old_jy];
12346 int new_element = Feld[jx][jy];
12348 if (IS_CUSTOM_ELEMENT(old_element))
12349 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
12351 player->index_bit, leave_side);
12353 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
12354 CE_PLAYER_LEAVES_X,
12355 player->index_bit, leave_side);
12357 if (IS_CUSTOM_ELEMENT(new_element))
12358 CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
12359 player->index_bit, enter_side);
12361 CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
12362 CE_PLAYER_ENTERS_X,
12363 player->index_bit, enter_side);
12365 CheckTriggeredElementChangeBySide(jx, jy, player->initial_element,
12366 CE_MOVE_OF_X, move_direction);
12369 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
12371 TestIfPlayerTouchesBadThing(jx, jy);
12372 TestIfPlayerTouchesCustomElement(jx, jy);
12374 /* needed because pushed element has not yet reached its destination,
12375 so it would trigger a change event at its previous field location */
12376 if (!player->is_pushing)
12377 TestIfElementTouchesCustomElement(jx, jy); /* for empty space */
12379 if (!player->active)
12380 RemovePlayer(player);
12383 if (!local_player->LevelSolved && level.use_step_counter)
12393 if (TimeLeft <= 10 && setup.time_limit)
12394 PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
12396 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
12398 DisplayGameControlValues();
12400 if (!TimeLeft && setup.time_limit)
12401 for (i = 0; i < MAX_PLAYERS; i++)
12402 KillPlayer(&stored_player[i]);
12404 else if (game.no_time_limit && !AllPlayersGone) /* level w/o time limit */
12406 game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
12408 DisplayGameControlValues();
12412 if (tape.single_step && tape.recording && !tape.pausing &&
12413 !player->programmed_action)
12414 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
12416 if (!player->programmed_action)
12417 CheckSaveEngineSnapshot(player);
12421 void ScrollScreen(struct PlayerInfo *player, int mode)
12423 static unsigned int screen_frame_counter = 0;
12425 if (mode == SCROLL_INIT)
12427 /* set scrolling step size according to actual player's moving speed */
12428 ScrollStepSize = TILEX / player->move_delay_value;
12430 screen_frame_counter = FrameCounter;
12431 ScreenMovDir = player->MovDir;
12432 ScreenMovPos = player->MovPos;
12433 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
12436 else if (!FrameReached(&screen_frame_counter, 1))
12441 ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
12442 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
12443 redraw_mask |= REDRAW_FIELD;
12446 ScreenMovDir = MV_NONE;
12449 void TestIfPlayerTouchesCustomElement(int x, int y)
12451 static int xy[4][2] =
12458 static int trigger_sides[4][2] =
12460 /* center side border side */
12461 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
12462 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
12463 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
12464 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
12466 static int touch_dir[4] =
12468 MV_LEFT | MV_RIGHT,
12473 int center_element = Feld[x][y]; /* should always be non-moving! */
12476 for (i = 0; i < NUM_DIRECTIONS; i++)
12478 int xx = x + xy[i][0];
12479 int yy = y + xy[i][1];
12480 int center_side = trigger_sides[i][0];
12481 int border_side = trigger_sides[i][1];
12482 int border_element;
12484 if (!IN_LEV_FIELD(xx, yy))
12487 if (IS_PLAYER(x, y)) /* player found at center element */
12489 struct PlayerInfo *player = PLAYERINFO(x, y);
12491 if (game.engine_version < VERSION_IDENT(3,0,7,0))
12492 border_element = Feld[xx][yy]; /* may be moving! */
12493 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
12494 border_element = Feld[xx][yy];
12495 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
12496 border_element = MovingOrBlocked2Element(xx, yy);
12498 continue; /* center and border element do not touch */
12500 CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
12501 player->index_bit, border_side);
12502 CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
12503 CE_PLAYER_TOUCHES_X,
12504 player->index_bit, border_side);
12507 /* use player element that is initially defined in the level playfield,
12508 not the player element that corresponds to the runtime player number
12509 (example: a level that contains EL_PLAYER_3 as the only player would
12510 incorrectly give EL_PLAYER_1 for "player->element_nr") */
12511 int player_element = PLAYERINFO(x, y)->initial_element;
12513 CheckElementChangeBySide(xx, yy, border_element, player_element,
12514 CE_TOUCHING_X, border_side);
12517 else if (IS_PLAYER(xx, yy)) /* player found at border element */
12519 struct PlayerInfo *player = PLAYERINFO(xx, yy);
12521 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
12523 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
12524 continue; /* center and border element do not touch */
12527 CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
12528 player->index_bit, center_side);
12529 CheckTriggeredElementChangeByPlayer(x, y, center_element,
12530 CE_PLAYER_TOUCHES_X,
12531 player->index_bit, center_side);
12534 /* use player element that is initially defined in the level playfield,
12535 not the player element that corresponds to the runtime player number
12536 (example: a level that contains EL_PLAYER_3 as the only player would
12537 incorrectly give EL_PLAYER_1 for "player->element_nr") */
12538 int player_element = PLAYERINFO(xx, yy)->initial_element;
12540 CheckElementChangeBySide(x, y, center_element, player_element,
12541 CE_TOUCHING_X, center_side);
12549 void TestIfElementTouchesCustomElement(int x, int y)
12551 static int xy[4][2] =
12558 static int trigger_sides[4][2] =
12560 /* center side border side */
12561 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
12562 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
12563 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
12564 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
12566 static int touch_dir[4] =
12568 MV_LEFT | MV_RIGHT,
12573 boolean change_center_element = FALSE;
12574 int center_element = Feld[x][y]; /* should always be non-moving! */
12575 int border_element_old[NUM_DIRECTIONS];
12578 for (i = 0; i < NUM_DIRECTIONS; i++)
12580 int xx = x + xy[i][0];
12581 int yy = y + xy[i][1];
12582 int border_element;
12584 border_element_old[i] = -1;
12586 if (!IN_LEV_FIELD(xx, yy))
12589 if (game.engine_version < VERSION_IDENT(3,0,7,0))
12590 border_element = Feld[xx][yy]; /* may be moving! */
12591 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
12592 border_element = Feld[xx][yy];
12593 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
12594 border_element = MovingOrBlocked2Element(xx, yy);
12596 continue; /* center and border element do not touch */
12598 border_element_old[i] = border_element;
12601 for (i = 0; i < NUM_DIRECTIONS; i++)
12603 int xx = x + xy[i][0];
12604 int yy = y + xy[i][1];
12605 int center_side = trigger_sides[i][0];
12606 int border_element = border_element_old[i];
12608 if (border_element == -1)
12611 /* check for change of border element */
12612 CheckElementChangeBySide(xx, yy, border_element, center_element,
12613 CE_TOUCHING_X, center_side);
12615 /* (center element cannot be player, so we dont have to check this here) */
12618 for (i = 0; i < NUM_DIRECTIONS; i++)
12620 int xx = x + xy[i][0];
12621 int yy = y + xy[i][1];
12622 int border_side = trigger_sides[i][1];
12623 int border_element = border_element_old[i];
12625 if (border_element == -1)
12628 /* check for change of center element (but change it only once) */
12629 if (!change_center_element)
12630 change_center_element =
12631 CheckElementChangeBySide(x, y, center_element, border_element,
12632 CE_TOUCHING_X, border_side);
12634 if (IS_PLAYER(xx, yy))
12636 /* use player element that is initially defined in the level playfield,
12637 not the player element that corresponds to the runtime player number
12638 (example: a level that contains EL_PLAYER_3 as the only player would
12639 incorrectly give EL_PLAYER_1 for "player->element_nr") */
12640 int player_element = PLAYERINFO(xx, yy)->initial_element;
12642 CheckElementChangeBySide(x, y, center_element, player_element,
12643 CE_TOUCHING_X, border_side);
12648 void TestIfElementHitsCustomElement(int x, int y, int direction)
12650 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
12651 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
12652 int hitx = x + dx, hity = y + dy;
12653 int hitting_element = Feld[x][y];
12654 int touched_element;
12656 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
12659 touched_element = (IN_LEV_FIELD(hitx, hity) ?
12660 MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
12662 if (IN_LEV_FIELD(hitx, hity))
12664 int opposite_direction = MV_DIR_OPPOSITE(direction);
12665 int hitting_side = direction;
12666 int touched_side = opposite_direction;
12667 boolean object_hit = (!IS_MOVING(hitx, hity) ||
12668 MovDir[hitx][hity] != direction ||
12669 ABS(MovPos[hitx][hity]) <= TILEY / 2);
12675 CheckElementChangeBySide(x, y, hitting_element, touched_element,
12676 CE_HITTING_X, touched_side);
12678 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
12679 CE_HIT_BY_X, hitting_side);
12681 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
12682 CE_HIT_BY_SOMETHING, opposite_direction);
12684 if (IS_PLAYER(hitx, hity))
12686 /* use player element that is initially defined in the level playfield,
12687 not the player element that corresponds to the runtime player number
12688 (example: a level that contains EL_PLAYER_3 as the only player would
12689 incorrectly give EL_PLAYER_1 for "player->element_nr") */
12690 int player_element = PLAYERINFO(hitx, hity)->initial_element;
12692 CheckElementChangeBySide(x, y, hitting_element, player_element,
12693 CE_HITTING_X, touched_side);
12698 /* "hitting something" is also true when hitting the playfield border */
12699 CheckElementChangeBySide(x, y, hitting_element, touched_element,
12700 CE_HITTING_SOMETHING, direction);
12703 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
12705 int i, kill_x = -1, kill_y = -1;
12707 int bad_element = -1;
12708 static int test_xy[4][2] =
12715 static int test_dir[4] =
12723 for (i = 0; i < NUM_DIRECTIONS; i++)
12725 int test_x, test_y, test_move_dir, test_element;
12727 test_x = good_x + test_xy[i][0];
12728 test_y = good_y + test_xy[i][1];
12730 if (!IN_LEV_FIELD(test_x, test_y))
12734 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
12736 test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
12738 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
12739 2nd case: DONT_TOUCH style bad thing does not move away from good thing
12741 if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
12742 (DONT_TOUCH(test_element) && test_move_dir != test_dir[i]))
12746 bad_element = test_element;
12752 if (kill_x != -1 || kill_y != -1)
12754 if (IS_PLAYER(good_x, good_y))
12756 struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
12758 if (player->shield_deadly_time_left > 0 &&
12759 !IS_INDESTRUCTIBLE(bad_element))
12760 Bang(kill_x, kill_y);
12761 else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
12762 KillPlayer(player);
12765 Bang(good_x, good_y);
12769 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
12771 int i, kill_x = -1, kill_y = -1;
12772 int bad_element = Feld[bad_x][bad_y];
12773 static int test_xy[4][2] =
12780 static int touch_dir[4] =
12782 MV_LEFT | MV_RIGHT,
12787 static int test_dir[4] =
12795 if (bad_element == EL_EXPLOSION) /* skip just exploding bad things */
12798 for (i = 0; i < NUM_DIRECTIONS; i++)
12800 int test_x, test_y, test_move_dir, test_element;
12802 test_x = bad_x + test_xy[i][0];
12803 test_y = bad_y + test_xy[i][1];
12805 if (!IN_LEV_FIELD(test_x, test_y))
12809 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
12811 test_element = Feld[test_x][test_y];
12813 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
12814 2nd case: DONT_TOUCH style bad thing does not move away from good thing
12816 if ((DONT_RUN_INTO(bad_element) && bad_move_dir == test_dir[i]) ||
12817 (DONT_TOUCH(bad_element) && test_move_dir != test_dir[i]))
12819 /* good thing is player or penguin that does not move away */
12820 if (IS_PLAYER(test_x, test_y))
12822 struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
12824 if (bad_element == EL_ROBOT && player->is_moving)
12825 continue; /* robot does not kill player if he is moving */
12827 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
12829 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
12830 continue; /* center and border element do not touch */
12838 else if (test_element == EL_PENGUIN)
12848 if (kill_x != -1 || kill_y != -1)
12850 if (IS_PLAYER(kill_x, kill_y))
12852 struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
12854 if (player->shield_deadly_time_left > 0 &&
12855 !IS_INDESTRUCTIBLE(bad_element))
12856 Bang(bad_x, bad_y);
12857 else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
12858 KillPlayer(player);
12861 Bang(kill_x, kill_y);
12865 void TestIfGoodThingGetsHitByBadThing(int bad_x, int bad_y, int bad_move_dir)
12867 int bad_element = Feld[bad_x][bad_y];
12868 int dx = (bad_move_dir == MV_LEFT ? -1 : bad_move_dir == MV_RIGHT ? +1 : 0);
12869 int dy = (bad_move_dir == MV_UP ? -1 : bad_move_dir == MV_DOWN ? +1 : 0);
12870 int test_x = bad_x + dx, test_y = bad_y + dy;
12871 int test_move_dir, test_element;
12872 int kill_x = -1, kill_y = -1;
12874 if (!IN_LEV_FIELD(test_x, test_y))
12878 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
12880 test_element = Feld[test_x][test_y];
12882 if (test_move_dir != bad_move_dir)
12884 /* good thing can be player or penguin that does not move away */
12885 if (IS_PLAYER(test_x, test_y))
12887 struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
12889 /* (note: in comparison to DONT_RUN_TO and DONT_TOUCH, also handle the
12890 player as being hit when he is moving towards the bad thing, because
12891 the "get hit by" condition would be lost after the player stops) */
12892 if (player->MovPos != 0 && player->MovDir == bad_move_dir)
12893 return; /* player moves away from bad thing */
12898 else if (test_element == EL_PENGUIN)
12905 if (kill_x != -1 || kill_y != -1)
12907 if (IS_PLAYER(kill_x, kill_y))
12909 struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
12911 if (player->shield_deadly_time_left > 0 &&
12912 !IS_INDESTRUCTIBLE(bad_element))
12913 Bang(bad_x, bad_y);
12914 else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
12915 KillPlayer(player);
12918 Bang(kill_x, kill_y);
12922 void TestIfPlayerTouchesBadThing(int x, int y)
12924 TestIfGoodThingHitsBadThing(x, y, MV_NONE);
12927 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
12929 TestIfGoodThingHitsBadThing(x, y, move_dir);
12932 void TestIfBadThingTouchesPlayer(int x, int y)
12934 TestIfBadThingHitsGoodThing(x, y, MV_NONE);
12937 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
12939 TestIfBadThingHitsGoodThing(x, y, move_dir);
12942 void TestIfFriendTouchesBadThing(int x, int y)
12944 TestIfGoodThingHitsBadThing(x, y, MV_NONE);
12947 void TestIfBadThingTouchesFriend(int x, int y)
12949 TestIfBadThingHitsGoodThing(x, y, MV_NONE);
12952 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
12954 int i, kill_x = bad_x, kill_y = bad_y;
12955 static int xy[4][2] =
12963 for (i = 0; i < NUM_DIRECTIONS; i++)
12967 x = bad_x + xy[i][0];
12968 y = bad_y + xy[i][1];
12969 if (!IN_LEV_FIELD(x, y))
12972 element = Feld[x][y];
12973 if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
12974 element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
12982 if (kill_x != bad_x || kill_y != bad_y)
12983 Bang(bad_x, bad_y);
12986 void KillPlayer(struct PlayerInfo *player)
12988 int jx = player->jx, jy = player->jy;
12990 if (!player->active)
12994 printf("::: 0: killed == %d, active == %d, reanimated == %d\n",
12995 player->killed, player->active, player->reanimated);
12998 /* the following code was introduced to prevent an infinite loop when calling
13000 -> CheckTriggeredElementChangeExt()
13001 -> ExecuteCustomElementAction()
13003 -> (infinitely repeating the above sequence of function calls)
13004 which occurs when killing the player while having a CE with the setting
13005 "kill player X when explosion of <player X>"; the solution using a new
13006 field "player->killed" was chosen for backwards compatibility, although
13007 clever use of the fields "player->active" etc. would probably also work */
13009 if (player->killed)
13013 player->killed = TRUE;
13015 /* remove accessible field at the player's position */
13016 Feld[jx][jy] = EL_EMPTY;
13018 /* deactivate shield (else Bang()/Explode() would not work right) */
13019 player->shield_normal_time_left = 0;
13020 player->shield_deadly_time_left = 0;
13023 printf("::: 1: killed == %d, active == %d, reanimated == %d\n",
13024 player->killed, player->active, player->reanimated);
13030 printf("::: 2: killed == %d, active == %d, reanimated == %d\n",
13031 player->killed, player->active, player->reanimated);
13034 if (player->reanimated) /* killed player may have been reanimated */
13035 player->killed = player->reanimated = FALSE;
13037 BuryPlayer(player);
13040 static void KillPlayerUnlessEnemyProtected(int x, int y)
13042 if (!PLAYER_ENEMY_PROTECTED(x, y))
13043 KillPlayer(PLAYERINFO(x, y));
13046 static void KillPlayerUnlessExplosionProtected(int x, int y)
13048 if (!PLAYER_EXPLOSION_PROTECTED(x, y))
13049 KillPlayer(PLAYERINFO(x, y));
13052 void BuryPlayer(struct PlayerInfo *player)
13054 int jx = player->jx, jy = player->jy;
13056 if (!player->active)
13059 PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
13060 PlayLevelSound(jx, jy, SND_GAME_LOSING);
13062 player->GameOver = TRUE;
13063 RemovePlayer(player);
13066 void RemovePlayer(struct PlayerInfo *player)
13068 int jx = player->jx, jy = player->jy;
13069 int i, found = FALSE;
13071 player->present = FALSE;
13072 player->active = FALSE;
13074 if (!ExplodeField[jx][jy])
13075 StorePlayer[jx][jy] = 0;
13077 if (player->is_moving)
13078 TEST_DrawLevelField(player->last_jx, player->last_jy);
13080 for (i = 0; i < MAX_PLAYERS; i++)
13081 if (stored_player[i].active)
13085 AllPlayersGone = TRUE;
13091 static void setFieldForSnapping(int x, int y, int element, int direction)
13093 struct ElementInfo *ei = &element_info[element];
13094 int direction_bit = MV_DIR_TO_BIT(direction);
13095 int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
13096 int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
13097 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
13099 Feld[x][y] = EL_ELEMENT_SNAPPING;
13100 MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
13102 ResetGfxAnimation(x, y);
13104 GfxElement[x][y] = element;
13105 GfxAction[x][y] = action;
13106 GfxDir[x][y] = direction;
13107 GfxFrame[x][y] = -1;
13111 =============================================================================
13112 checkDiagonalPushing()
13113 -----------------------------------------------------------------------------
13114 check if diagonal input device direction results in pushing of object
13115 (by checking if the alternative direction is walkable, diggable, ...)
13116 =============================================================================
13119 static boolean checkDiagonalPushing(struct PlayerInfo *player,
13120 int x, int y, int real_dx, int real_dy)
13122 int jx, jy, dx, dy, xx, yy;
13124 if (real_dx == 0 || real_dy == 0) /* no diagonal direction => push */
13127 /* diagonal direction: check alternative direction */
13132 xx = jx + (dx == 0 ? real_dx : 0);
13133 yy = jy + (dy == 0 ? real_dy : 0);
13135 return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
13139 =============================================================================
13141 -----------------------------------------------------------------------------
13142 x, y: field next to player (non-diagonal) to try to dig to
13143 real_dx, real_dy: direction as read from input device (can be diagonal)
13144 =============================================================================
13147 static int DigField(struct PlayerInfo *player,
13148 int oldx, int oldy, int x, int y,
13149 int real_dx, int real_dy, int mode)
13151 boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
13152 boolean player_was_pushing = player->is_pushing;
13153 boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
13154 boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
13155 int jx = oldx, jy = oldy;
13156 int dx = x - jx, dy = y - jy;
13157 int nextx = x + dx, nexty = y + dy;
13158 int move_direction = (dx == -1 ? MV_LEFT :
13159 dx == +1 ? MV_RIGHT :
13161 dy == +1 ? MV_DOWN : MV_NONE);
13162 int opposite_direction = MV_DIR_OPPOSITE(move_direction);
13163 int dig_side = MV_DIR_OPPOSITE(move_direction);
13164 int old_element = Feld[jx][jy];
13165 int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
13168 if (is_player) /* function can also be called by EL_PENGUIN */
13170 if (player->MovPos == 0)
13172 player->is_digging = FALSE;
13173 player->is_collecting = FALSE;
13176 if (player->MovPos == 0) /* last pushing move finished */
13177 player->is_pushing = FALSE;
13179 if (mode == DF_NO_PUSH) /* player just stopped pushing */
13181 player->is_switching = FALSE;
13182 player->push_delay = -1;
13184 return MP_NO_ACTION;
13188 if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
13189 old_element = Back[jx][jy];
13191 /* in case of element dropped at player position, check background */
13192 else if (Back[jx][jy] != EL_EMPTY &&
13193 game.engine_version >= VERSION_IDENT(2,2,0,0))
13194 old_element = Back[jx][jy];
13196 if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
13197 return MP_NO_ACTION; /* field has no opening in this direction */
13199 if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
13200 return MP_NO_ACTION; /* field has no opening in this direction */
13202 if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
13206 Feld[jx][jy] = player->artwork_element;
13207 InitMovingField(jx, jy, MV_DOWN);
13208 Store[jx][jy] = EL_ACID;
13209 ContinueMoving(jx, jy);
13210 BuryPlayer(player);
13212 return MP_DONT_RUN_INTO;
13215 if (player_can_move && DONT_RUN_INTO(element))
13217 TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
13219 return MP_DONT_RUN_INTO;
13222 if (IS_MOVING(x, y) || IS_PLAYER(x, y))
13223 return MP_NO_ACTION;
13225 collect_count = element_info[element].collect_count_initial;
13227 if (!is_player && !IS_COLLECTIBLE(element)) /* penguin cannot collect it */
13228 return MP_NO_ACTION;
13230 if (game.engine_version < VERSION_IDENT(2,2,0,0))
13231 player_can_move = player_can_move_or_snap;
13233 if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
13234 game.engine_version >= VERSION_IDENT(2,2,0,0))
13236 CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
13237 player->index_bit, dig_side);
13238 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13239 player->index_bit, dig_side);
13241 if (element == EL_DC_LANDMINE)
13244 if (Feld[x][y] != element) /* field changed by snapping */
13247 return MP_NO_ACTION;
13250 if (player->gravity && is_player && !player->is_auto_moving &&
13251 canFallDown(player) && move_direction != MV_DOWN &&
13252 !canMoveToValidFieldWithGravity(jx, jy, move_direction))
13253 return MP_NO_ACTION; /* player cannot walk here due to gravity */
13255 if (player_can_move &&
13256 IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
13258 int sound_element = SND_ELEMENT(element);
13259 int sound_action = ACTION_WALKING;
13261 if (IS_RND_GATE(element))
13263 if (!player->key[RND_GATE_NR(element)])
13264 return MP_NO_ACTION;
13266 else if (IS_RND_GATE_GRAY(element))
13268 if (!player->key[RND_GATE_GRAY_NR(element)])
13269 return MP_NO_ACTION;
13271 else if (IS_RND_GATE_GRAY_ACTIVE(element))
13273 if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
13274 return MP_NO_ACTION;
13276 else if (element == EL_EXIT_OPEN ||
13277 element == EL_EM_EXIT_OPEN ||
13278 element == EL_EM_EXIT_OPENING ||
13279 element == EL_STEEL_EXIT_OPEN ||
13280 element == EL_EM_STEEL_EXIT_OPEN ||
13281 element == EL_EM_STEEL_EXIT_OPENING ||
13282 element == EL_SP_EXIT_OPEN ||
13283 element == EL_SP_EXIT_OPENING)
13285 sound_action = ACTION_PASSING; /* player is passing exit */
13287 else if (element == EL_EMPTY)
13289 sound_action = ACTION_MOVING; /* nothing to walk on */
13292 /* play sound from background or player, whatever is available */
13293 if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
13294 PlayLevelSoundElementAction(x, y, sound_element, sound_action);
13296 PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
13298 else if (player_can_move &&
13299 IS_PASSABLE(element) && canPassField(x, y, move_direction))
13301 if (!ACCESS_FROM(element, opposite_direction))
13302 return MP_NO_ACTION; /* field not accessible from this direction */
13304 if (CAN_MOVE(element)) /* only fixed elements can be passed! */
13305 return MP_NO_ACTION;
13307 if (IS_EM_GATE(element))
13309 if (!player->key[EM_GATE_NR(element)])
13310 return MP_NO_ACTION;
13312 else if (IS_EM_GATE_GRAY(element))
13314 if (!player->key[EM_GATE_GRAY_NR(element)])
13315 return MP_NO_ACTION;
13317 else if (IS_EM_GATE_GRAY_ACTIVE(element))
13319 if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
13320 return MP_NO_ACTION;
13322 else if (IS_EMC_GATE(element))
13324 if (!player->key[EMC_GATE_NR(element)])
13325 return MP_NO_ACTION;
13327 else if (IS_EMC_GATE_GRAY(element))
13329 if (!player->key[EMC_GATE_GRAY_NR(element)])
13330 return MP_NO_ACTION;
13332 else if (IS_EMC_GATE_GRAY_ACTIVE(element))
13334 if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
13335 return MP_NO_ACTION;
13337 else if (element == EL_DC_GATE_WHITE ||
13338 element == EL_DC_GATE_WHITE_GRAY ||
13339 element == EL_DC_GATE_WHITE_GRAY_ACTIVE)
13341 if (player->num_white_keys == 0)
13342 return MP_NO_ACTION;
13344 player->num_white_keys--;
13346 else if (IS_SP_PORT(element))
13348 if (element == EL_SP_GRAVITY_PORT_LEFT ||
13349 element == EL_SP_GRAVITY_PORT_RIGHT ||
13350 element == EL_SP_GRAVITY_PORT_UP ||
13351 element == EL_SP_GRAVITY_PORT_DOWN)
13352 player->gravity = !player->gravity;
13353 else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
13354 element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
13355 element == EL_SP_GRAVITY_ON_PORT_UP ||
13356 element == EL_SP_GRAVITY_ON_PORT_DOWN)
13357 player->gravity = TRUE;
13358 else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
13359 element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
13360 element == EL_SP_GRAVITY_OFF_PORT_UP ||
13361 element == EL_SP_GRAVITY_OFF_PORT_DOWN)
13362 player->gravity = FALSE;
13365 /* automatically move to the next field with double speed */
13366 player->programmed_action = move_direction;
13368 if (player->move_delay_reset_counter == 0)
13370 player->move_delay_reset_counter = 2; /* two double speed steps */
13372 DOUBLE_PLAYER_SPEED(player);
13375 PlayLevelSoundAction(x, y, ACTION_PASSING);
13377 else if (player_can_move_or_snap && IS_DIGGABLE(element))
13381 if (mode != DF_SNAP)
13383 GfxElement[x][y] = GFX_ELEMENT(element);
13384 player->is_digging = TRUE;
13387 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
13389 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
13390 player->index_bit, dig_side);
13392 if (mode == DF_SNAP)
13394 if (level.block_snap_field)
13395 setFieldForSnapping(x, y, element, move_direction);
13397 TestIfElementTouchesCustomElement(x, y); /* for empty space */
13399 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13400 player->index_bit, dig_side);
13403 else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
13407 if (is_player && mode != DF_SNAP)
13409 GfxElement[x][y] = element;
13410 player->is_collecting = TRUE;
13413 if (element == EL_SPEED_PILL)
13415 player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
13417 else if (element == EL_EXTRA_TIME && level.time > 0)
13419 TimeLeft += level.extra_time;
13421 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
13423 DisplayGameControlValues();
13425 else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
13427 player->shield_normal_time_left += level.shield_normal_time;
13428 if (element == EL_SHIELD_DEADLY)
13429 player->shield_deadly_time_left += level.shield_deadly_time;
13431 else if (element == EL_DYNAMITE ||
13432 element == EL_EM_DYNAMITE ||
13433 element == EL_SP_DISK_RED)
13435 if (player->inventory_size < MAX_INVENTORY_SIZE)
13436 player->inventory_element[player->inventory_size++] = element;
13438 DrawGameDoorValues();
13440 else if (element == EL_DYNABOMB_INCREASE_NUMBER)
13442 player->dynabomb_count++;
13443 player->dynabombs_left++;
13445 else if (element == EL_DYNABOMB_INCREASE_SIZE)
13447 player->dynabomb_size++;
13449 else if (element == EL_DYNABOMB_INCREASE_POWER)
13451 player->dynabomb_xl = TRUE;
13453 else if (IS_KEY(element))
13455 player->key[KEY_NR(element)] = TRUE;
13457 DrawGameDoorValues();
13459 else if (element == EL_DC_KEY_WHITE)
13461 player->num_white_keys++;
13463 /* display white keys? */
13464 /* DrawGameDoorValues(); */
13466 else if (IS_ENVELOPE(element))
13468 player->show_envelope = element;
13470 else if (element == EL_EMC_LENSES)
13472 game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
13474 RedrawAllInvisibleElementsForLenses();
13476 else if (element == EL_EMC_MAGNIFIER)
13478 game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
13480 RedrawAllInvisibleElementsForMagnifier();
13482 else if (IS_DROPPABLE(element) ||
13483 IS_THROWABLE(element)) /* can be collected and dropped */
13487 if (collect_count == 0)
13488 player->inventory_infinite_element = element;
13490 for (i = 0; i < collect_count; i++)
13491 if (player->inventory_size < MAX_INVENTORY_SIZE)
13492 player->inventory_element[player->inventory_size++] = element;
13494 DrawGameDoorValues();
13496 else if (collect_count > 0)
13498 local_player->gems_still_needed -= collect_count;
13499 if (local_player->gems_still_needed < 0)
13500 local_player->gems_still_needed = 0;
13502 game.snapshot.collected_item = TRUE;
13504 game_panel_controls[GAME_PANEL_GEMS].value = local_player->gems_still_needed;
13506 DisplayGameControlValues();
13509 RaiseScoreElement(element);
13510 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
13513 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
13514 player->index_bit, dig_side);
13516 if (mode == DF_SNAP)
13518 if (level.block_snap_field)
13519 setFieldForSnapping(x, y, element, move_direction);
13521 TestIfElementTouchesCustomElement(x, y); /* for empty space */
13523 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13524 player->index_bit, dig_side);
13527 else if (player_can_move_or_snap && IS_PUSHABLE(element))
13529 if (mode == DF_SNAP && element != EL_BD_ROCK)
13530 return MP_NO_ACTION;
13532 if (CAN_FALL(element) && dy)
13533 return MP_NO_ACTION;
13535 if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
13536 !(element == EL_SPRING && level.use_spring_bug))
13537 return MP_NO_ACTION;
13539 if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
13540 ((move_direction & MV_VERTICAL &&
13541 ((element_info[element].move_pattern & MV_LEFT &&
13542 IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
13543 (element_info[element].move_pattern & MV_RIGHT &&
13544 IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
13545 (move_direction & MV_HORIZONTAL &&
13546 ((element_info[element].move_pattern & MV_UP &&
13547 IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
13548 (element_info[element].move_pattern & MV_DOWN &&
13549 IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
13550 return MP_NO_ACTION;
13552 /* do not push elements already moving away faster than player */
13553 if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
13554 ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
13555 return MP_NO_ACTION;
13557 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
13559 if (player->push_delay_value == -1 || !player_was_pushing)
13560 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13562 else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
13564 if (player->push_delay_value == -1)
13565 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13567 else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
13569 if (!player->is_pushing)
13570 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13573 player->is_pushing = TRUE;
13574 player->is_active = TRUE;
13576 if (!(IN_LEV_FIELD(nextx, nexty) &&
13577 (IS_FREE(nextx, nexty) ||
13578 (IS_SB_ELEMENT(element) &&
13579 Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY) ||
13580 (IS_CUSTOM_ELEMENT(element) &&
13581 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty)))))
13582 return MP_NO_ACTION;
13584 if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
13585 return MP_NO_ACTION;
13587 if (player->push_delay == -1) /* new pushing; restart delay */
13588 player->push_delay = 0;
13590 if (player->push_delay < player->push_delay_value &&
13591 !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
13592 element != EL_SPRING && element != EL_BALLOON)
13594 /* make sure that there is no move delay before next try to push */
13595 if (game.engine_version >= VERSION_IDENT(3,0,7,1))
13596 player->move_delay = 0;
13598 return MP_NO_ACTION;
13601 if (IS_CUSTOM_ELEMENT(element) &&
13602 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty))
13604 if (!DigFieldByCE(nextx, nexty, element))
13605 return MP_NO_ACTION;
13608 if (IS_SB_ELEMENT(element))
13610 if (element == EL_SOKOBAN_FIELD_FULL)
13612 Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
13613 local_player->sokobanfields_still_needed++;
13616 if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
13618 Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
13619 local_player->sokobanfields_still_needed--;
13622 Feld[x][y] = EL_SOKOBAN_OBJECT;
13624 if (Back[x][y] == Back[nextx][nexty])
13625 PlayLevelSoundAction(x, y, ACTION_PUSHING);
13626 else if (Back[x][y] != 0)
13627 PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
13630 PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
13633 if (local_player->sokobanfields_still_needed == 0 &&
13634 (game.emulation == EMU_SOKOBAN || level.auto_exit_sokoban))
13636 PlayerWins(player);
13638 PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
13642 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
13644 InitMovingField(x, y, move_direction);
13645 GfxAction[x][y] = ACTION_PUSHING;
13647 if (mode == DF_SNAP)
13648 ContinueMoving(x, y);
13650 MovPos[x][y] = (dx != 0 ? dx : dy);
13652 Pushed[x][y] = TRUE;
13653 Pushed[nextx][nexty] = TRUE;
13655 if (game.engine_version < VERSION_IDENT(2,2,0,7))
13656 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13658 player->push_delay_value = -1; /* get new value later */
13660 /* check for element change _after_ element has been pushed */
13661 if (game.use_change_when_pushing_bug)
13663 CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
13664 player->index_bit, dig_side);
13665 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
13666 player->index_bit, dig_side);
13669 else if (IS_SWITCHABLE(element))
13671 if (PLAYER_SWITCHING(player, x, y))
13673 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
13674 player->index_bit, dig_side);
13679 player->is_switching = TRUE;
13680 player->switch_x = x;
13681 player->switch_y = y;
13683 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
13685 if (element == EL_ROBOT_WHEEL)
13687 Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
13691 game.robot_wheel_active = TRUE;
13693 TEST_DrawLevelField(x, y);
13695 else if (element == EL_SP_TERMINAL)
13699 SCAN_PLAYFIELD(xx, yy)
13701 if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
13705 else if (Feld[xx][yy] == EL_SP_TERMINAL)
13707 Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
13709 ResetGfxAnimation(xx, yy);
13710 TEST_DrawLevelField(xx, yy);
13714 else if (IS_BELT_SWITCH(element))
13716 ToggleBeltSwitch(x, y);
13718 else if (element == EL_SWITCHGATE_SWITCH_UP ||
13719 element == EL_SWITCHGATE_SWITCH_DOWN ||
13720 element == EL_DC_SWITCHGATE_SWITCH_UP ||
13721 element == EL_DC_SWITCHGATE_SWITCH_DOWN)
13723 ToggleSwitchgateSwitch(x, y);
13725 else if (element == EL_LIGHT_SWITCH ||
13726 element == EL_LIGHT_SWITCH_ACTIVE)
13728 ToggleLightSwitch(x, y);
13730 else if (element == EL_TIMEGATE_SWITCH ||
13731 element == EL_DC_TIMEGATE_SWITCH)
13733 ActivateTimegateSwitch(x, y);
13735 else if (element == EL_BALLOON_SWITCH_LEFT ||
13736 element == EL_BALLOON_SWITCH_RIGHT ||
13737 element == EL_BALLOON_SWITCH_UP ||
13738 element == EL_BALLOON_SWITCH_DOWN ||
13739 element == EL_BALLOON_SWITCH_NONE ||
13740 element == EL_BALLOON_SWITCH_ANY)
13742 game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT ? MV_LEFT :
13743 element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
13744 element == EL_BALLOON_SWITCH_UP ? MV_UP :
13745 element == EL_BALLOON_SWITCH_DOWN ? MV_DOWN :
13746 element == EL_BALLOON_SWITCH_NONE ? MV_NONE :
13749 else if (element == EL_LAMP)
13751 Feld[x][y] = EL_LAMP_ACTIVE;
13752 local_player->lights_still_needed--;
13754 ResetGfxAnimation(x, y);
13755 TEST_DrawLevelField(x, y);
13757 else if (element == EL_TIME_ORB_FULL)
13759 Feld[x][y] = EL_TIME_ORB_EMPTY;
13761 if (level.time > 0 || level.use_time_orb_bug)
13763 TimeLeft += level.time_orb_time;
13764 game.no_time_limit = FALSE;
13766 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
13768 DisplayGameControlValues();
13771 ResetGfxAnimation(x, y);
13772 TEST_DrawLevelField(x, y);
13774 else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
13775 element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
13779 game.ball_state = !game.ball_state;
13781 SCAN_PLAYFIELD(xx, yy)
13783 int e = Feld[xx][yy];
13785 if (game.ball_state)
13787 if (e == EL_EMC_MAGIC_BALL)
13788 CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
13789 else if (e == EL_EMC_MAGIC_BALL_SWITCH)
13790 CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
13794 if (e == EL_EMC_MAGIC_BALL_ACTIVE)
13795 CreateField(xx, yy, EL_EMC_MAGIC_BALL);
13796 else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
13797 CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
13802 CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
13803 player->index_bit, dig_side);
13805 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
13806 player->index_bit, dig_side);
13808 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
13809 player->index_bit, dig_side);
13815 if (!PLAYER_SWITCHING(player, x, y))
13817 player->is_switching = TRUE;
13818 player->switch_x = x;
13819 player->switch_y = y;
13821 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
13822 player->index_bit, dig_side);
13823 CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
13824 player->index_bit, dig_side);
13826 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
13827 player->index_bit, dig_side);
13828 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
13829 player->index_bit, dig_side);
13832 CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
13833 player->index_bit, dig_side);
13834 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
13835 player->index_bit, dig_side);
13837 return MP_NO_ACTION;
13840 player->push_delay = -1;
13842 if (is_player) /* function can also be called by EL_PENGUIN */
13844 if (Feld[x][y] != element) /* really digged/collected something */
13846 player->is_collecting = !player->is_digging;
13847 player->is_active = TRUE;
13854 static boolean DigFieldByCE(int x, int y, int digging_element)
13856 int element = Feld[x][y];
13858 if (!IS_FREE(x, y))
13860 int action = (IS_DIGGABLE(element) ? ACTION_DIGGING :
13861 IS_COLLECTIBLE(element) ? ACTION_COLLECTING :
13864 /* no element can dig solid indestructible elements */
13865 if (IS_INDESTRUCTIBLE(element) &&
13866 !IS_DIGGABLE(element) &&
13867 !IS_COLLECTIBLE(element))
13870 if (AmoebaNr[x][y] &&
13871 (element == EL_AMOEBA_FULL ||
13872 element == EL_BD_AMOEBA ||
13873 element == EL_AMOEBA_GROWING))
13875 AmoebaCnt[AmoebaNr[x][y]]--;
13876 AmoebaCnt2[AmoebaNr[x][y]]--;
13879 if (IS_MOVING(x, y))
13880 RemoveMovingField(x, y);
13884 TEST_DrawLevelField(x, y);
13887 /* if digged element was about to explode, prevent the explosion */
13888 ExplodeField[x][y] = EX_TYPE_NONE;
13890 PlayLevelSoundAction(x, y, action);
13893 Store[x][y] = EL_EMPTY;
13895 /* this makes it possible to leave the removed element again */
13896 if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element)))
13897 Store[x][y] = element;
13902 static boolean SnapField(struct PlayerInfo *player, int dx, int dy)
13904 int jx = player->jx, jy = player->jy;
13905 int x = jx + dx, y = jy + dy;
13906 int snap_direction = (dx == -1 ? MV_LEFT :
13907 dx == +1 ? MV_RIGHT :
13909 dy == +1 ? MV_DOWN : MV_NONE);
13910 boolean can_continue_snapping = (level.continuous_snapping &&
13911 WasJustFalling[x][y] < CHECK_DELAY_FALLING);
13913 if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
13916 if (!player->active || !IN_LEV_FIELD(x, y))
13924 if (player->MovPos == 0)
13925 player->is_pushing = FALSE;
13927 player->is_snapping = FALSE;
13929 if (player->MovPos == 0)
13931 player->is_moving = FALSE;
13932 player->is_digging = FALSE;
13933 player->is_collecting = FALSE;
13939 /* prevent snapping with already pressed snap key when not allowed */
13940 if (player->is_snapping && !can_continue_snapping)
13943 player->MovDir = snap_direction;
13945 if (player->MovPos == 0)
13947 player->is_moving = FALSE;
13948 player->is_digging = FALSE;
13949 player->is_collecting = FALSE;
13952 player->is_dropping = FALSE;
13953 player->is_dropping_pressed = FALSE;
13954 player->drop_pressed_delay = 0;
13956 if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
13959 player->is_snapping = TRUE;
13960 player->is_active = TRUE;
13962 if (player->MovPos == 0)
13964 player->is_moving = FALSE;
13965 player->is_digging = FALSE;
13966 player->is_collecting = FALSE;
13969 if (player->MovPos != 0) /* prevent graphic bugs in versions < 2.2.0 */
13970 TEST_DrawLevelField(player->last_jx, player->last_jy);
13972 TEST_DrawLevelField(x, y);
13977 static boolean DropElement(struct PlayerInfo *player)
13979 int old_element, new_element;
13980 int dropx = player->jx, dropy = player->jy;
13981 int drop_direction = player->MovDir;
13982 int drop_side = drop_direction;
13983 int drop_element = get_next_dropped_element(player);
13985 player->is_dropping_pressed = TRUE;
13987 /* do not drop an element on top of another element; when holding drop key
13988 pressed without moving, dropped element must move away before the next
13989 element can be dropped (this is especially important if the next element
13990 is dynamite, which can be placed on background for historical reasons) */
13991 if (PLAYER_DROPPING(player, dropx, dropy) && Feld[dropx][dropy] != EL_EMPTY)
13994 if (IS_THROWABLE(drop_element))
13996 dropx += GET_DX_FROM_DIR(drop_direction);
13997 dropy += GET_DY_FROM_DIR(drop_direction);
13999 if (!IN_LEV_FIELD(dropx, dropy))
14003 old_element = Feld[dropx][dropy]; /* old element at dropping position */
14004 new_element = drop_element; /* default: no change when dropping */
14006 /* check if player is active, not moving and ready to drop */
14007 if (!player->active || player->MovPos || player->drop_delay > 0)
14010 /* check if player has anything that can be dropped */
14011 if (new_element == EL_UNDEFINED)
14014 /* check if drop key was pressed long enough for EM style dynamite */
14015 if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
14018 /* check if anything can be dropped at the current position */
14019 if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
14022 /* collected custom elements can only be dropped on empty fields */
14023 if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
14026 if (old_element != EL_EMPTY)
14027 Back[dropx][dropy] = old_element; /* store old element on this field */
14029 ResetGfxAnimation(dropx, dropy);
14030 ResetRandomAnimationValue(dropx, dropy);
14032 if (player->inventory_size > 0 ||
14033 player->inventory_infinite_element != EL_UNDEFINED)
14035 if (player->inventory_size > 0)
14037 player->inventory_size--;
14039 DrawGameDoorValues();
14041 if (new_element == EL_DYNAMITE)
14042 new_element = EL_DYNAMITE_ACTIVE;
14043 else if (new_element == EL_EM_DYNAMITE)
14044 new_element = EL_EM_DYNAMITE_ACTIVE;
14045 else if (new_element == EL_SP_DISK_RED)
14046 new_element = EL_SP_DISK_RED_ACTIVE;
14049 Feld[dropx][dropy] = new_element;
14051 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
14052 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
14053 el2img(Feld[dropx][dropy]), 0);
14055 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
14057 /* needed if previous element just changed to "empty" in the last frame */
14058 ChangeCount[dropx][dropy] = 0; /* allow at least one more change */
14060 CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
14061 player->index_bit, drop_side);
14062 CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
14064 player->index_bit, drop_side);
14066 TestIfElementTouchesCustomElement(dropx, dropy);
14068 else /* player is dropping a dyna bomb */
14070 player->dynabombs_left--;
14072 Feld[dropx][dropy] = new_element;
14074 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
14075 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
14076 el2img(Feld[dropx][dropy]), 0);
14078 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
14081 if (Feld[dropx][dropy] == new_element) /* uninitialized unless CE change */
14082 InitField_WithBug1(dropx, dropy, FALSE);
14084 new_element = Feld[dropx][dropy]; /* element might have changed */
14086 if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
14087 element_info[new_element].move_pattern == MV_WHEN_DROPPED)
14089 if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
14090 MovDir[dropx][dropy] = drop_direction;
14092 ChangeCount[dropx][dropy] = 0; /* allow at least one more change */
14094 /* do not cause impact style collision by dropping elements that can fall */
14095 CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
14098 player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
14099 player->is_dropping = TRUE;
14101 player->drop_pressed_delay = 0;
14102 player->is_dropping_pressed = FALSE;
14104 player->drop_x = dropx;
14105 player->drop_y = dropy;
14110 /* ------------------------------------------------------------------------- */
14111 /* game sound playing functions */
14112 /* ------------------------------------------------------------------------- */
14114 static int *loop_sound_frame = NULL;
14115 static int *loop_sound_volume = NULL;
14117 void InitPlayLevelSound()
14119 int num_sounds = getSoundListSize();
14121 checked_free(loop_sound_frame);
14122 checked_free(loop_sound_volume);
14124 loop_sound_frame = checked_calloc(num_sounds * sizeof(int));
14125 loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
14128 static void PlayLevelSound(int x, int y, int nr)
14130 int sx = SCREENX(x), sy = SCREENY(y);
14131 int volume, stereo_position;
14132 int max_distance = 8;
14133 int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
14135 if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
14136 (!setup.sound_loops && IS_LOOP_SOUND(nr)))
14139 if (!IN_LEV_FIELD(x, y) ||
14140 sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
14141 sy < -max_distance || sy >= SCR_FIELDY + max_distance)
14144 volume = SOUND_MAX_VOLUME;
14146 if (!IN_SCR_FIELD(sx, sy))
14148 int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
14149 int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
14151 volume -= volume * (dx > dy ? dx : dy) / max_distance;
14154 stereo_position = (SOUND_MAX_LEFT +
14155 (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
14156 (SCR_FIELDX + 2 * max_distance));
14158 if (IS_LOOP_SOUND(nr))
14160 /* This assures that quieter loop sounds do not overwrite louder ones,
14161 while restarting sound volume comparison with each new game frame. */
14163 if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
14166 loop_sound_volume[nr] = volume;
14167 loop_sound_frame[nr] = FrameCounter;
14170 PlaySoundExt(nr, volume, stereo_position, type);
14173 static void PlayLevelSoundNearest(int x, int y, int sound_action)
14175 PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
14176 x > LEVELX(BX2) ? LEVELX(BX2) : x,
14177 y < LEVELY(BY1) ? LEVELY(BY1) :
14178 y > LEVELY(BY2) ? LEVELY(BY2) : y,
14182 static void PlayLevelSoundAction(int x, int y, int action)
14184 PlayLevelSoundElementAction(x, y, Feld[x][y], action);
14187 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
14189 int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
14191 if (sound_effect != SND_UNDEFINED)
14192 PlayLevelSound(x, y, sound_effect);
14195 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
14198 int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
14200 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14201 PlayLevelSound(x, y, sound_effect);
14204 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
14206 int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
14208 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14209 PlayLevelSound(x, y, sound_effect);
14212 static void StopLevelSoundActionIfLoop(int x, int y, int action)
14214 int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
14216 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14217 StopSound(sound_effect);
14220 static void PlayLevelMusic()
14222 if (levelset.music[level_nr] != MUS_UNDEFINED)
14223 PlayMusic(levelset.music[level_nr]); /* from config file */
14225 PlayMusic(MAP_NOCONF_MUSIC(level_nr)); /* from music dir */
14228 void PlayLevelSound_EM(int xx, int yy, int element_em, int sample)
14230 int element = (element_em > -1 ? map_element_EM_to_RND(element_em) : 0);
14231 int offset = (BorderElement == EL_STEELWALL ? 1 : 0);
14232 int x = xx - 1 - offset;
14233 int y = yy - 1 - offset;
14238 PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
14242 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14246 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14250 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14254 PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
14258 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14262 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14265 case SAMPLE_android_clone:
14266 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14269 case SAMPLE_android_move:
14270 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14273 case SAMPLE_spring:
14274 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14278 PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
14282 PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
14285 case SAMPLE_eater_eat:
14286 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14290 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14293 case SAMPLE_collect:
14294 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
14297 case SAMPLE_diamond:
14298 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14301 case SAMPLE_squash:
14302 /* !!! CHECK THIS !!! */
14304 PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
14306 PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
14310 case SAMPLE_wonderfall:
14311 PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
14315 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14319 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14323 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14327 PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
14331 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14335 PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
14338 case SAMPLE_wonder:
14339 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14343 PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
14346 case SAMPLE_exit_open:
14347 PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
14350 case SAMPLE_exit_leave:
14351 PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
14354 case SAMPLE_dynamite:
14355 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14359 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14363 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
14367 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14371 PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
14375 PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
14379 PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
14383 PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
14388 void PlayLevelSound_SP(int xx, int yy, int element_sp, int action_sp)
14390 int element = map_element_SP_to_RND(element_sp);
14391 int action = map_action_SP_to_RND(action_sp);
14392 int offset = (setup.sp_show_border_elements ? 0 : 1);
14393 int x = xx - offset;
14394 int y = yy - offset;
14396 PlayLevelSoundElementAction(x, y, element, action);
14399 void RaiseScore(int value)
14401 local_player->score += value;
14403 game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
14405 DisplayGameControlValues();
14408 void RaiseScoreElement(int element)
14413 case EL_BD_DIAMOND:
14414 case EL_EMERALD_YELLOW:
14415 case EL_EMERALD_RED:
14416 case EL_EMERALD_PURPLE:
14417 case EL_SP_INFOTRON:
14418 RaiseScore(level.score[SC_EMERALD]);
14421 RaiseScore(level.score[SC_DIAMOND]);
14424 RaiseScore(level.score[SC_CRYSTAL]);
14427 RaiseScore(level.score[SC_PEARL]);
14430 case EL_BD_BUTTERFLY:
14431 case EL_SP_ELECTRON:
14432 RaiseScore(level.score[SC_BUG]);
14435 case EL_BD_FIREFLY:
14436 case EL_SP_SNIKSNAK:
14437 RaiseScore(level.score[SC_SPACESHIP]);
14440 case EL_DARK_YAMYAM:
14441 RaiseScore(level.score[SC_YAMYAM]);
14444 RaiseScore(level.score[SC_ROBOT]);
14447 RaiseScore(level.score[SC_PACMAN]);
14450 RaiseScore(level.score[SC_NUT]);
14453 case EL_EM_DYNAMITE:
14454 case EL_SP_DISK_RED:
14455 case EL_DYNABOMB_INCREASE_NUMBER:
14456 case EL_DYNABOMB_INCREASE_SIZE:
14457 case EL_DYNABOMB_INCREASE_POWER:
14458 RaiseScore(level.score[SC_DYNAMITE]);
14460 case EL_SHIELD_NORMAL:
14461 case EL_SHIELD_DEADLY:
14462 RaiseScore(level.score[SC_SHIELD]);
14464 case EL_EXTRA_TIME:
14465 RaiseScore(level.extra_time_score);
14479 case EL_DC_KEY_WHITE:
14480 RaiseScore(level.score[SC_KEY]);
14483 RaiseScore(element_info[element].collect_score);
14488 void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message)
14490 if (skip_request || Request(message, REQ_ASK | REQ_STAY_CLOSED))
14492 /* closing door required in case of envelope style request dialogs */
14494 CloseDoor(DOOR_CLOSE_1);
14496 #if defined(NETWORK_AVALIABLE)
14497 if (options.network)
14498 SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
14503 FadeSkipNextFadeIn();
14505 SetGameStatus(GAME_MODE_MAIN);
14510 else /* continue playing the game */
14512 if (tape.playing && tape.deactivate_display)
14513 TapeDeactivateDisplayOff(TRUE);
14515 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
14517 if (tape.playing && tape.deactivate_display)
14518 TapeDeactivateDisplayOn();
14522 void RequestQuitGame(boolean ask_if_really_quit)
14524 boolean quick_quit = (!ask_if_really_quit || level_editor_test_game);
14525 boolean skip_request = AllPlayersGone || quick_quit;
14527 RequestQuitGameExt(skip_request, quick_quit,
14528 "Do you really want to quit the game?");
14532 /* ------------------------------------------------------------------------- */
14533 /* random generator functions */
14534 /* ------------------------------------------------------------------------- */
14536 unsigned int InitEngineRandom_RND(int seed)
14538 game.num_random_calls = 0;
14540 return InitEngineRandom(seed);
14543 unsigned int RND(int max)
14547 game.num_random_calls++;
14549 return GetEngineRandom(max);
14556 /* ------------------------------------------------------------------------- */
14557 /* game engine snapshot handling functions */
14558 /* ------------------------------------------------------------------------- */
14560 struct EngineSnapshotInfo
14562 /* runtime values for custom element collect score */
14563 int collect_score[NUM_CUSTOM_ELEMENTS];
14565 /* runtime values for group element choice position */
14566 int choice_pos[NUM_GROUP_ELEMENTS];
14568 /* runtime values for belt position animations */
14569 int belt_graphic[4][NUM_BELT_PARTS];
14570 int belt_anim_mode[4][NUM_BELT_PARTS];
14573 static struct EngineSnapshotInfo engine_snapshot_rnd;
14574 static char *snapshot_level_identifier = NULL;
14575 static int snapshot_level_nr = -1;
14577 static void SaveEngineSnapshotValues_RND()
14579 static int belt_base_active_element[4] =
14581 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
14582 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
14583 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
14584 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
14588 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
14590 int element = EL_CUSTOM_START + i;
14592 engine_snapshot_rnd.collect_score[i] = element_info[element].collect_score;
14595 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
14597 int element = EL_GROUP_START + i;
14599 engine_snapshot_rnd.choice_pos[i] = element_info[element].group->choice_pos;
14602 for (i = 0; i < 4; i++)
14604 for (j = 0; j < NUM_BELT_PARTS; j++)
14606 int element = belt_base_active_element[i] + j;
14607 int graphic = el2img(element);
14608 int anim_mode = graphic_info[graphic].anim_mode;
14610 engine_snapshot_rnd.belt_graphic[i][j] = graphic;
14611 engine_snapshot_rnd.belt_anim_mode[i][j] = anim_mode;
14616 static void LoadEngineSnapshotValues_RND()
14618 unsigned int num_random_calls = game.num_random_calls;
14621 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
14623 int element = EL_CUSTOM_START + i;
14625 element_info[element].collect_score = engine_snapshot_rnd.collect_score[i];
14628 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
14630 int element = EL_GROUP_START + i;
14632 element_info[element].group->choice_pos = engine_snapshot_rnd.choice_pos[i];
14635 for (i = 0; i < 4; i++)
14637 for (j = 0; j < NUM_BELT_PARTS; j++)
14639 int graphic = engine_snapshot_rnd.belt_graphic[i][j];
14640 int anim_mode = engine_snapshot_rnd.belt_anim_mode[i][j];
14642 graphic_info[graphic].anim_mode = anim_mode;
14646 if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
14648 InitRND(tape.random_seed);
14649 for (i = 0; i < num_random_calls; i++)
14653 if (game.num_random_calls != num_random_calls)
14655 Error(ERR_INFO, "number of random calls out of sync");
14656 Error(ERR_INFO, "number of random calls should be %d", num_random_calls);
14657 Error(ERR_INFO, "number of random calls is %d", game.num_random_calls);
14658 Error(ERR_EXIT, "this should not happen -- please debug");
14662 void FreeEngineSnapshotSingle()
14664 FreeSnapshotSingle();
14666 setString(&snapshot_level_identifier, NULL);
14667 snapshot_level_nr = -1;
14670 void FreeEngineSnapshotList()
14672 FreeSnapshotList();
14675 ListNode *SaveEngineSnapshotBuffers()
14677 ListNode *buffers = NULL;
14679 /* copy some special values to a structure better suited for the snapshot */
14681 if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
14682 SaveEngineSnapshotValues_RND();
14683 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
14684 SaveEngineSnapshotValues_EM();
14685 if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
14686 SaveEngineSnapshotValues_SP(&buffers);
14688 /* save values stored in special snapshot structure */
14690 if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
14691 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
14692 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
14693 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
14694 if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
14695 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_sp));
14697 /* save further RND engine values */
14699 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(stored_player));
14700 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(game));
14701 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(tape));
14703 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ZX));
14704 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ZY));
14705 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExitX));
14706 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExitY));
14708 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(FrameCounter));
14709 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeFrames));
14710 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimePlayed));
14711 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeLeft));
14712 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TapeTime));
14714 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovDir));
14715 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovPos));
14716 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenGfxPos));
14718 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScrollStepSize));
14720 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AllPlayersGone));
14722 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt));
14723 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2));
14725 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Feld));
14726 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovPos));
14727 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDir));
14728 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDelay));
14729 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeDelay));
14730 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangePage));
14731 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CustomValue));
14732 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store));
14733 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store2));
14734 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(StorePlayer));
14735 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Back));
14736 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaNr));
14737 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustMoving));
14738 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustFalling));
14739 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckCollision));
14740 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckImpact));
14741 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Stop));
14742 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Pushed));
14744 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeCount));
14745 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeEvent));
14747 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodePhase));
14748 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeDelay));
14749 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeField));
14751 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(RunnerVisit));
14752 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(PlayerVisit));
14754 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxFrame));
14755 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxRandom));
14756 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxElement));
14757 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxAction));
14758 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxDir));
14760 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_x));
14761 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_y));
14764 ListNode *node = engine_snapshot_list_rnd;
14767 while (node != NULL)
14769 num_bytes += ((struct EngineSnapshotNodeInfo *)node->content)->size;
14774 printf("::: size of engine snapshot: %d bytes\n", num_bytes);
14780 void SaveEngineSnapshotSingle()
14782 ListNode *buffers = SaveEngineSnapshotBuffers();
14784 /* finally save all snapshot buffers to single snapshot */
14785 SaveSnapshotSingle(buffers);
14787 /* save level identification information */
14788 setString(&snapshot_level_identifier, leveldir_current->identifier);
14789 snapshot_level_nr = level_nr;
14792 boolean CheckSaveEngineSnapshotToList()
14794 boolean save_snapshot =
14795 ((game.snapshot.mode == SNAPSHOT_MODE_EVERY_STEP) ||
14796 (game.snapshot.mode == SNAPSHOT_MODE_EVERY_MOVE &&
14797 game.snapshot.changed_action) ||
14798 (game.snapshot.mode == SNAPSHOT_MODE_EVERY_COLLECT &&
14799 game.snapshot.collected_item));
14801 game.snapshot.changed_action = FALSE;
14802 game.snapshot.collected_item = FALSE;
14803 game.snapshot.save_snapshot = save_snapshot;
14805 return save_snapshot;
14808 void SaveEngineSnapshotToList()
14810 if (game.snapshot.mode == SNAPSHOT_MODE_OFF ||
14814 ListNode *buffers = SaveEngineSnapshotBuffers();
14816 /* finally save all snapshot buffers to snapshot list */
14817 SaveSnapshotToList(buffers);
14820 void SaveEngineSnapshotToListInitial()
14822 FreeEngineSnapshotList();
14824 SaveEngineSnapshotToList();
14827 void LoadEngineSnapshotValues()
14829 /* restore special values from snapshot structure */
14831 if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
14832 LoadEngineSnapshotValues_RND();
14833 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
14834 LoadEngineSnapshotValues_EM();
14835 if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
14836 LoadEngineSnapshotValues_SP();
14839 void LoadEngineSnapshotSingle()
14841 LoadSnapshotSingle();
14843 LoadEngineSnapshotValues();
14846 void LoadEngineSnapshot_Undo(int steps)
14848 LoadSnapshotFromList_Older(steps);
14850 LoadEngineSnapshotValues();
14853 void LoadEngineSnapshot_Redo(int steps)
14855 LoadSnapshotFromList_Newer(steps);
14857 LoadEngineSnapshotValues();
14860 boolean CheckEngineSnapshotSingle()
14862 return (strEqual(snapshot_level_identifier, leveldir_current->identifier) &&
14863 snapshot_level_nr == level_nr);
14866 boolean CheckEngineSnapshotList()
14868 return CheckSnapshotList();
14872 /* ---------- new game button stuff ---------------------------------------- */
14880 } gamebutton_info[NUM_GAME_BUTTONS] =
14883 IMG_GFX_GAME_BUTTON_STOP, &game.button.stop,
14884 GAME_CTRL_ID_STOP, "stop game"
14887 IMG_GFX_GAME_BUTTON_PAUSE, &game.button.pause,
14888 GAME_CTRL_ID_PAUSE, "pause game"
14891 IMG_GFX_GAME_BUTTON_PLAY, &game.button.play,
14892 GAME_CTRL_ID_PLAY, "play game"
14895 IMG_GFX_GAME_BUTTON_UNDO, &game.button.undo,
14896 GAME_CTRL_ID_UNDO, "undo step"
14899 IMG_GFX_GAME_BUTTON_REDO, &game.button.redo,
14900 GAME_CTRL_ID_REDO, "redo step"
14903 IMG_GFX_GAME_BUTTON_SAVE, &game.button.save,
14904 GAME_CTRL_ID_SAVE, "save game"
14907 IMG_GFX_GAME_BUTTON_PAUSE2, &game.button.pause2,
14908 GAME_CTRL_ID_PAUSE2, "pause game"
14911 IMG_GFX_GAME_BUTTON_LOAD, &game.button.load,
14912 GAME_CTRL_ID_LOAD, "load game"
14915 IMG_GFX_GAME_BUTTON_SOUND_MUSIC, &game.button.sound_music,
14916 SOUND_CTRL_ID_MUSIC, "background music on/off"
14919 IMG_GFX_GAME_BUTTON_SOUND_LOOPS, &game.button.sound_loops,
14920 SOUND_CTRL_ID_LOOPS, "sound loops on/off"
14923 IMG_GFX_GAME_BUTTON_SOUND_SIMPLE, &game.button.sound_simple,
14924 SOUND_CTRL_ID_SIMPLE, "normal sounds on/off"
14928 void CreateGameButtons()
14932 for (i = 0; i < NUM_GAME_BUTTONS; i++)
14934 struct GraphicInfo *gfx = &graphic_info[gamebutton_info[i].graphic];
14935 struct XY *pos = gamebutton_info[i].pos;
14936 struct GadgetInfo *gi;
14939 unsigned int event_mask;
14940 int base_x = (tape.show_game_buttons ? VX : DX);
14941 int base_y = (tape.show_game_buttons ? VY : DY);
14942 int gd_x = gfx->src_x;
14943 int gd_y = gfx->src_y;
14944 int gd_xp = gfx->src_x + gfx->pressed_xoffset;
14945 int gd_yp = gfx->src_y + gfx->pressed_yoffset;
14946 int gd_xa = gfx->src_x + gfx->active_xoffset;
14947 int gd_ya = gfx->src_y + gfx->active_yoffset;
14948 int gd_xap = gfx->src_x + gfx->active_xoffset + gfx->pressed_xoffset;
14949 int gd_yap = gfx->src_y + gfx->active_yoffset + gfx->pressed_yoffset;
14952 if (gfx->bitmap == NULL)
14954 game_gadget[id] = NULL;
14959 if (id == GAME_CTRL_ID_STOP ||
14960 id == GAME_CTRL_ID_PLAY ||
14961 id == GAME_CTRL_ID_SAVE ||
14962 id == GAME_CTRL_ID_LOAD)
14964 button_type = GD_TYPE_NORMAL_BUTTON;
14966 event_mask = GD_EVENT_RELEASED;
14968 else if (id == GAME_CTRL_ID_UNDO ||
14969 id == GAME_CTRL_ID_REDO)
14971 button_type = GD_TYPE_NORMAL_BUTTON;
14973 event_mask = GD_EVENT_PRESSED | GD_EVENT_REPEATED;
14977 button_type = GD_TYPE_CHECK_BUTTON;
14979 ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
14980 (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
14981 (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
14982 event_mask = GD_EVENT_PRESSED;
14985 gi = CreateGadget(GDI_CUSTOM_ID, id,
14986 GDI_INFO_TEXT, gamebutton_info[i].infotext,
14987 GDI_X, base_x + GDI_ACTIVE_POS(pos->x),
14988 GDI_Y, base_y + GDI_ACTIVE_POS(pos->y),
14989 GDI_WIDTH, gfx->width,
14990 GDI_HEIGHT, gfx->height,
14991 GDI_TYPE, button_type,
14992 GDI_STATE, GD_BUTTON_UNPRESSED,
14993 GDI_CHECKED, checked,
14994 GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
14995 GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
14996 GDI_ALT_DESIGN_UNPRESSED, gfx->bitmap, gd_xa, gd_ya,
14997 GDI_ALT_DESIGN_PRESSED, gfx->bitmap, gd_xap, gd_yap,
14998 GDI_DIRECT_DRAW, FALSE,
14999 GDI_EVENT_MASK, event_mask,
15000 GDI_CALLBACK_ACTION, HandleGameButtons,
15004 Error(ERR_EXIT, "cannot create gadget");
15006 game_gadget[id] = gi;
15010 void FreeGameButtons()
15014 for (i = 0; i < NUM_GAME_BUTTONS; i++)
15015 FreeGadget(game_gadget[i]);
15018 static void UnmapGameButtonsAtSamePosition(int id)
15022 for (i = 0; i < NUM_GAME_BUTTONS; i++)
15024 gamebutton_info[i].pos->x == gamebutton_info[id].pos->x &&
15025 gamebutton_info[i].pos->y == gamebutton_info[id].pos->y)
15026 UnmapGadget(game_gadget[i]);
15029 static void UnmapGameButtonsAtSamePosition_All()
15031 if (setup.show_snapshot_buttons)
15033 UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_SAVE);
15034 UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE2);
15035 UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_LOAD);
15039 UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_STOP);
15040 UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE);
15041 UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PLAY);
15045 static void MapGameButtonsAtSamePosition(int id)
15049 for (i = 0; i < NUM_GAME_BUTTONS; i++)
15051 gamebutton_info[i].pos->x == gamebutton_info[id].pos->x &&
15052 gamebutton_info[i].pos->y == gamebutton_info[id].pos->y)
15053 MapGadget(game_gadget[i]);
15055 UnmapGameButtonsAtSamePosition_All();
15058 void MapUndoRedoButtons()
15060 UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
15061 UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
15063 MapGadget(game_gadget[GAME_CTRL_ID_UNDO]);
15064 MapGadget(game_gadget[GAME_CTRL_ID_REDO]);
15066 ModifyGadget(game_gadget[GAME_CTRL_ID_PAUSE2], GDI_CHECKED, TRUE, GDI_END);
15069 void UnmapUndoRedoButtons()
15071 UnmapGadget(game_gadget[GAME_CTRL_ID_UNDO]);
15072 UnmapGadget(game_gadget[GAME_CTRL_ID_REDO]);
15074 MapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
15075 MapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
15077 ModifyGadget(game_gadget[GAME_CTRL_ID_PAUSE2], GDI_CHECKED, FALSE, GDI_END);
15080 void MapGameButtons()
15084 for (i = 0; i < NUM_GAME_BUTTONS; i++)
15085 if (i != GAME_CTRL_ID_UNDO &&
15086 i != GAME_CTRL_ID_REDO)
15087 MapGadget(game_gadget[i]);
15089 UnmapGameButtonsAtSamePosition_All();
15091 RedrawGameButtons();
15094 void UnmapGameButtons()
15098 for (i = 0; i < NUM_GAME_BUTTONS; i++)
15099 UnmapGadget(game_gadget[i]);
15102 void RedrawGameButtons()
15106 for (i = 0; i < NUM_GAME_BUTTONS; i++)
15107 RedrawGadget(game_gadget[i]);
15109 // RedrawGadget() may have set REDRAW_ALL if buttons are defined off-area
15110 redraw_mask &= ~REDRAW_ALL;
15113 void GameUndoRedoExt()
15115 ClearPlayerAction();
15117 tape.pausing = TRUE;
15120 UpdateAndDisplayGameControlValues();
15122 DrawCompleteVideoDisplay();
15123 DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
15124 DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
15125 DrawVideoDisplay(VIDEO_STATE_1STEP(tape.single_step), 0);
15130 void GameUndo(int steps)
15132 if (!CheckEngineSnapshotList())
15135 LoadEngineSnapshot_Undo(steps);
15140 void GameRedo(int steps)
15142 if (!CheckEngineSnapshotList())
15145 LoadEngineSnapshot_Redo(steps);
15150 static void HandleGameButtonsExt(int id, int button)
15152 static boolean game_undo_executed = FALSE;
15153 int steps = BUTTON_STEPSIZE(button);
15154 boolean handle_game_buttons =
15155 (game_status == GAME_MODE_PLAYING ||
15156 (game_status == GAME_MODE_MAIN && tape.show_game_buttons));
15158 if (!handle_game_buttons)
15163 case GAME_CTRL_ID_STOP:
15164 if (game_status == GAME_MODE_MAIN)
15170 RequestQuitGame(TRUE);
15174 case GAME_CTRL_ID_PAUSE:
15175 case GAME_CTRL_ID_PAUSE2:
15176 if (options.network && game_status == GAME_MODE_PLAYING)
15178 #if defined(NETWORK_AVALIABLE)
15180 SendToServer_ContinuePlaying();
15182 SendToServer_PausePlaying();
15186 TapeTogglePause(TAPE_TOGGLE_MANUAL);
15188 game_undo_executed = FALSE;
15192 case GAME_CTRL_ID_PLAY:
15193 if (game_status == GAME_MODE_MAIN)
15195 StartGameActions(options.network, setup.autorecord, level.random_seed);
15197 else if (tape.pausing)
15199 #if defined(NETWORK_AVALIABLE)
15200 if (options.network)
15201 SendToServer_ContinuePlaying();
15204 TapeTogglePause(TAPE_TOGGLE_MANUAL | TAPE_TOGGLE_PLAY_PAUSE);
15208 case GAME_CTRL_ID_UNDO:
15209 // Important: When using "save snapshot when collecting an item" mode,
15210 // load last (current) snapshot for first "undo" after pressing "pause"
15211 // (else the last-but-one snapshot would be loaded, because the snapshot
15212 // pointer already points to the last snapshot when pressing "pause",
15213 // which is fine for "every step/move" mode, but not for "every collect")
15214 if (game.snapshot.mode == SNAPSHOT_MODE_EVERY_COLLECT &&
15215 !game_undo_executed)
15218 game_undo_executed = TRUE;
15223 case GAME_CTRL_ID_REDO:
15227 case GAME_CTRL_ID_SAVE:
15231 case GAME_CTRL_ID_LOAD:
15235 case SOUND_CTRL_ID_MUSIC:
15236 if (setup.sound_music)
15238 setup.sound_music = FALSE;
15242 else if (audio.music_available)
15244 setup.sound = setup.sound_music = TRUE;
15246 SetAudioMode(setup.sound);
15252 case SOUND_CTRL_ID_LOOPS:
15253 if (setup.sound_loops)
15254 setup.sound_loops = FALSE;
15255 else if (audio.loops_available)
15257 setup.sound = setup.sound_loops = TRUE;
15259 SetAudioMode(setup.sound);
15263 case SOUND_CTRL_ID_SIMPLE:
15264 if (setup.sound_simple)
15265 setup.sound_simple = FALSE;
15266 else if (audio.sound_available)
15268 setup.sound = setup.sound_simple = TRUE;
15270 SetAudioMode(setup.sound);
15279 static void HandleGameButtons(struct GadgetInfo *gi)
15281 HandleGameButtonsExt(gi->custom_id, gi->event.button);
15284 void HandleSoundButtonKeys(Key key)
15287 if (key == setup.shortcut.sound_simple)
15288 ClickOnGadget(game_gadget[SOUND_CTRL_ID_SIMPLE], MB_LEFTBUTTON);
15289 else if (key == setup.shortcut.sound_loops)
15290 ClickOnGadget(game_gadget[SOUND_CTRL_ID_LOOPS], MB_LEFTBUTTON);
15291 else if (key == setup.shortcut.sound_music)
15292 ClickOnGadget(game_gadget[SOUND_CTRL_ID_MUSIC], MB_LEFTBUTTON);