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 scroll positions */
831 #define SCROLL_POSITION_X(x) ((x) < SBX_Left + MIDPOSX ? SBX_Left : \
832 (x) > SBX_Right + MIDPOSX ? SBX_Right :\
834 #define SCROLL_POSITION_Y(y) ((y) < SBY_Upper + MIDPOSY ? SBY_Upper :\
835 (y) > SBY_Lower + MIDPOSY ? SBY_Lower :\
838 /* values for other actions */
839 #define MOVE_STEPSIZE_NORMAL (TILEX / MOVE_DELAY_NORMAL_SPEED)
840 #define MOVE_STEPSIZE_MIN (1)
841 #define MOVE_STEPSIZE_MAX (TILEX)
843 #define GET_DX_FROM_DIR(d) ((d) == MV_LEFT ? -1 : (d) == MV_RIGHT ? 1 : 0)
844 #define GET_DY_FROM_DIR(d) ((d) == MV_UP ? -1 : (d) == MV_DOWN ? 1 : 0)
846 #define INIT_GFX_RANDOM() (GetSimpleRandom(1000000))
848 #define GET_NEW_PUSH_DELAY(e) ( (element_info[e].push_delay_fixed) + \
849 RND(element_info[e].push_delay_random))
850 #define GET_NEW_DROP_DELAY(e) ( (element_info[e].drop_delay_fixed) + \
851 RND(element_info[e].drop_delay_random))
852 #define GET_NEW_MOVE_DELAY(e) ( (element_info[e].move_delay_fixed) + \
853 RND(element_info[e].move_delay_random))
854 #define GET_MAX_MOVE_DELAY(e) ( (element_info[e].move_delay_fixed) + \
855 (element_info[e].move_delay_random))
856 #define GET_NEW_CE_VALUE(e) ( (element_info[e].ce_value_fixed_initial) +\
857 RND(element_info[e].ce_value_random_initial))
858 #define GET_CE_SCORE(e) ( (element_info[e].collect_score))
859 #define GET_CHANGE_DELAY(c) ( ((c)->delay_fixed * (c)->delay_frames) + \
860 RND((c)->delay_random * (c)->delay_frames))
861 #define GET_CE_DELAY_VALUE(c) ( ((c)->delay_fixed) + \
862 RND((c)->delay_random))
865 #define GET_VALID_RUNTIME_ELEMENT(e) \
866 ((e) >= NUM_RUNTIME_ELEMENTS ? EL_UNKNOWN : (e))
868 #define RESOLVED_REFERENCE_ELEMENT(be, e) \
869 ((be) + (e) - EL_SELF < EL_CUSTOM_START ? EL_CUSTOM_START : \
870 (be) + (e) - EL_SELF > EL_CUSTOM_END ? EL_CUSTOM_END : \
871 (be) + (e) - EL_SELF)
873 #define GET_PLAYER_FROM_BITS(p) \
874 (EL_PLAYER_1 + ((p) != PLAYER_BITS_ANY ? log_2(p) : 0))
876 #define GET_TARGET_ELEMENT(be, e, ch, cv, cs) \
877 ((e) == EL_TRIGGER_PLAYER ? (ch)->actual_trigger_player : \
878 (e) == EL_TRIGGER_ELEMENT ? (ch)->actual_trigger_element : \
879 (e) == EL_TRIGGER_CE_VALUE ? (ch)->actual_trigger_ce_value : \
880 (e) == EL_TRIGGER_CE_SCORE ? (ch)->actual_trigger_ce_score : \
881 (e) == EL_CURRENT_CE_VALUE ? (cv) : \
882 (e) == EL_CURRENT_CE_SCORE ? (cs) : \
883 (e) >= EL_PREV_CE_8 && (e) <= EL_NEXT_CE_8 ? \
884 RESOLVED_REFERENCE_ELEMENT(be, e) : \
887 #define CAN_GROW_INTO(e) \
888 ((e) == EL_SAND || (IS_DIGGABLE(e) && level.grow_into_diggable))
890 #define ELEMENT_CAN_ENTER_FIELD_BASE_X(x, y, condition) \
891 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
894 #define ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, condition) \
895 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
896 (CAN_MOVE_INTO_ACID(e) && \
897 Feld[x][y] == EL_ACID) || \
900 #define ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, condition) \
901 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) || \
902 (CAN_MOVE_INTO_ACID(e) && \
903 Feld[x][y] == EL_ACID) || \
906 #define ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, condition) \
907 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
909 (CAN_MOVE_INTO_ACID(e) && \
910 Feld[x][y] == EL_ACID) || \
911 (DONT_COLLIDE_WITH(e) && \
913 !PLAYER_ENEMY_PROTECTED(x, y))))
915 #define ELEMENT_CAN_ENTER_FIELD(e, x, y) \
916 ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, 0)
918 #define SATELLITE_CAN_ENTER_FIELD(x, y) \
919 ELEMENT_CAN_ENTER_FIELD_BASE_2(EL_SATELLITE, x, y, 0)
921 #define ANDROID_CAN_ENTER_FIELD(e, x, y) \
922 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, Feld[x][y] == EL_EMC_PLANT)
924 #define ANDROID_CAN_CLONE_FIELD(x, y) \
925 (IN_LEV_FIELD(x, y) && (CAN_BE_CLONED_BY_ANDROID(Feld[x][y]) || \
926 CAN_BE_CLONED_BY_ANDROID(EL_TRIGGER_ELEMENT)))
928 #define ENEMY_CAN_ENTER_FIELD(e, x, y) \
929 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
931 #define YAMYAM_CAN_ENTER_FIELD(e, x, y) \
932 ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, Feld[x][y] == EL_DIAMOND)
934 #define DARK_YAMYAM_CAN_ENTER_FIELD(e, x, y) \
935 ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x,y, IS_FOOD_DARK_YAMYAM(Feld[x][y]))
937 #define PACMAN_CAN_ENTER_FIELD(e, x, y) \
938 ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, IS_AMOEBOID(Feld[x][y]))
940 #define PIG_CAN_ENTER_FIELD(e, x, y) \
941 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, IS_FOOD_PIG(Feld[x][y]))
943 #define PENGUIN_CAN_ENTER_FIELD(e, x, y) \
944 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (Feld[x][y] == EL_EXIT_OPEN || \
945 Feld[x][y] == EL_EM_EXIT_OPEN || \
946 Feld[x][y] == EL_STEEL_EXIT_OPEN || \
947 Feld[x][y] == EL_EM_STEEL_EXIT_OPEN || \
948 IS_FOOD_PENGUIN(Feld[x][y])))
949 #define DRAGON_CAN_ENTER_FIELD(e, x, y) \
950 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
952 #define MOLE_CAN_ENTER_FIELD(e, x, y, condition) \
953 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (condition))
955 #define SPRING_CAN_ENTER_FIELD(e, x, y) \
956 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
958 #define SPRING_CAN_BUMP_FROM_FIELD(x, y) \
959 (IN_LEV_FIELD(x, y) && (Feld[x][y] == EL_EMC_SPRING_BUMPER || \
960 Feld[x][y] == EL_EMC_SPRING_BUMPER_ACTIVE))
962 #define MOVE_ENTER_EL(e) (element_info[e].move_enter_element)
964 #define CE_ENTER_FIELD_COND(e, x, y) \
965 (!IS_PLAYER(x, y) && \
966 IS_EQUAL_OR_IN_GROUP(Feld[x][y], MOVE_ENTER_EL(e)))
968 #define CUSTOM_ELEMENT_CAN_ENTER_FIELD(e, x, y) \
969 ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, CE_ENTER_FIELD_COND(e, x, y))
971 #define IN_LEV_FIELD_AND_IS_FREE(x, y) (IN_LEV_FIELD(x, y) && IS_FREE(x, y))
972 #define IN_LEV_FIELD_AND_NOT_FREE(x, y) (IN_LEV_FIELD(x, y) && !IS_FREE(x, y))
974 #define ACCESS_FROM(e, d) (element_info[e].access_direction &(d))
975 #define IS_WALKABLE_FROM(e, d) (IS_WALKABLE(e) && ACCESS_FROM(e, d))
976 #define IS_PASSABLE_FROM(e, d) (IS_PASSABLE(e) && ACCESS_FROM(e, d))
977 #define IS_ACCESSIBLE_FROM(e, d) (IS_ACCESSIBLE(e) && ACCESS_FROM(e, d))
979 /* game button identifiers */
980 #define GAME_CTRL_ID_STOP 0
981 #define GAME_CTRL_ID_PAUSE 1
982 #define GAME_CTRL_ID_PLAY 2
983 #define GAME_CTRL_ID_UNDO 3
984 #define GAME_CTRL_ID_REDO 4
985 #define GAME_CTRL_ID_SAVE 5
986 #define GAME_CTRL_ID_PAUSE2 6
987 #define GAME_CTRL_ID_LOAD 7
988 #define SOUND_CTRL_ID_MUSIC 8
989 #define SOUND_CTRL_ID_LOOPS 9
990 #define SOUND_CTRL_ID_SIMPLE 10
992 #define NUM_GAME_BUTTONS 11
995 /* forward declaration for internal use */
997 static void CreateField(int, int, int);
999 static void ResetGfxAnimation(int, int);
1001 static void SetPlayerWaiting(struct PlayerInfo *, boolean);
1002 static void AdvanceFrameAndPlayerCounters(int);
1004 static boolean MovePlayerOneStep(struct PlayerInfo *, int, int, int, int);
1005 static boolean MovePlayer(struct PlayerInfo *, int, int);
1006 static void ScrollPlayer(struct PlayerInfo *, int);
1007 static void ScrollScreen(struct PlayerInfo *, int);
1009 static int DigField(struct PlayerInfo *, int, int, int, int, int, int, int);
1010 static boolean DigFieldByCE(int, int, int);
1011 static boolean SnapField(struct PlayerInfo *, int, int);
1012 static boolean DropElement(struct PlayerInfo *);
1014 static void InitBeltMovement(void);
1015 static void CloseAllOpenTimegates(void);
1016 static void CheckGravityMovement(struct PlayerInfo *);
1017 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *);
1018 static void KillPlayerUnlessEnemyProtected(int, int);
1019 static void KillPlayerUnlessExplosionProtected(int, int);
1021 static void TestIfPlayerTouchesCustomElement(int, int);
1022 static void TestIfElementTouchesCustomElement(int, int);
1023 static void TestIfElementHitsCustomElement(int, int, int);
1025 static void HandleElementChange(int, int, int);
1026 static void ExecuteCustomElementAction(int, int, int, int);
1027 static boolean ChangeElement(int, int, int, int);
1029 static boolean CheckTriggeredElementChangeExt(int, int, int, int, int,int,int);
1030 #define CheckTriggeredElementChange(x, y, e, ev) \
1031 CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY,CH_SIDE_ANY, -1)
1032 #define CheckTriggeredElementChangeByPlayer(x, y, e, ev, p, s) \
1033 CheckTriggeredElementChangeExt(x, y, e, ev, p, s, -1)
1034 #define CheckTriggeredElementChangeBySide(x, y, e, ev, s) \
1035 CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, s, -1)
1036 #define CheckTriggeredElementChangeByPage(x, y, e, ev, p) \
1037 CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY, CH_SIDE_ANY, p)
1039 static boolean CheckElementChangeExt(int, int, int, int, int, int, int);
1040 #define CheckElementChange(x, y, e, te, ev) \
1041 CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, CH_SIDE_ANY)
1042 #define CheckElementChangeByPlayer(x, y, e, ev, p, s) \
1043 CheckElementChangeExt(x, y, e, EL_EMPTY, ev, p, s)
1044 #define CheckElementChangeBySide(x, y, e, te, ev, s) \
1045 CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, s)
1047 static void PlayLevelSound(int, int, int);
1048 static void PlayLevelSoundNearest(int, int, int);
1049 static void PlayLevelSoundAction(int, int, int);
1050 static void PlayLevelSoundElementAction(int, int, int, int);
1051 static void PlayLevelSoundElementActionIfLoop(int, int, int, int);
1052 static void PlayLevelSoundActionIfLoop(int, int, int);
1053 static void StopLevelSoundActionIfLoop(int, int, int);
1054 static void PlayLevelMusic();
1055 static void FadeLevelSoundsAndMusic();
1057 static void HandleGameButtons(struct GadgetInfo *);
1059 int AmoebeNachbarNr(int, int);
1060 void AmoebeUmwandeln(int, int);
1061 void ContinueMoving(int, int);
1062 void Bang(int, int);
1063 void InitMovDir(int, int);
1064 void InitAmoebaNr(int, int);
1065 int NewHiScore(void);
1067 void TestIfGoodThingHitsBadThing(int, int, int);
1068 void TestIfBadThingHitsGoodThing(int, int, int);
1069 void TestIfPlayerTouchesBadThing(int, int);
1070 void TestIfPlayerRunsIntoBadThing(int, int, int);
1071 void TestIfBadThingTouchesPlayer(int, int);
1072 void TestIfBadThingRunsIntoPlayer(int, int, int);
1073 void TestIfFriendTouchesBadThing(int, int);
1074 void TestIfBadThingTouchesFriend(int, int);
1075 void TestIfBadThingTouchesOtherBadThing(int, int);
1076 void TestIfGoodThingGetsHitByBadThing(int, int, int);
1078 void KillPlayer(struct PlayerInfo *);
1079 void BuryPlayer(struct PlayerInfo *);
1080 void RemovePlayer(struct PlayerInfo *);
1082 static int getInvisibleActiveFromInvisibleElement(int);
1083 static int getInvisibleFromInvisibleActiveElement(int);
1085 static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
1087 /* for detection of endless loops, caused by custom element programming */
1088 /* (using maximal playfield width x 10 is just a rough approximation) */
1089 #define MAX_ELEMENT_CHANGE_RECURSION_DEPTH (MAX_PLAYFIELD_WIDTH * 10)
1091 #define RECURSION_LOOP_DETECTION_START(e, rc) \
1093 if (recursion_loop_detected) \
1096 if (recursion_loop_depth > MAX_ELEMENT_CHANGE_RECURSION_DEPTH) \
1098 recursion_loop_detected = TRUE; \
1099 recursion_loop_element = (e); \
1102 recursion_loop_depth++; \
1105 #define RECURSION_LOOP_DETECTION_END() \
1107 recursion_loop_depth--; \
1110 static int recursion_loop_depth;
1111 static boolean recursion_loop_detected;
1112 static boolean recursion_loop_element;
1114 static int map_player_action[MAX_PLAYERS];
1117 /* ------------------------------------------------------------------------- */
1118 /* definition of elements that automatically change to other elements after */
1119 /* a specified time, eventually calling a function when changing */
1120 /* ------------------------------------------------------------------------- */
1122 /* forward declaration for changer functions */
1123 static void InitBuggyBase(int, int);
1124 static void WarnBuggyBase(int, int);
1126 static void InitTrap(int, int);
1127 static void ActivateTrap(int, int);
1128 static void ChangeActiveTrap(int, int);
1130 static void InitRobotWheel(int, int);
1131 static void RunRobotWheel(int, int);
1132 static void StopRobotWheel(int, int);
1134 static void InitTimegateWheel(int, int);
1135 static void RunTimegateWheel(int, int);
1137 static void InitMagicBallDelay(int, int);
1138 static void ActivateMagicBall(int, int);
1140 struct ChangingElementInfo
1145 void (*pre_change_function)(int x, int y);
1146 void (*change_function)(int x, int y);
1147 void (*post_change_function)(int x, int y);
1150 static struct ChangingElementInfo change_delay_list[] =
1185 EL_STEEL_EXIT_OPENING,
1193 EL_STEEL_EXIT_CLOSING,
1194 EL_STEEL_EXIT_CLOSED,
1217 EL_EM_STEEL_EXIT_OPENING,
1218 EL_EM_STEEL_EXIT_OPEN,
1225 EL_EM_STEEL_EXIT_CLOSING,
1249 EL_SWITCHGATE_OPENING,
1257 EL_SWITCHGATE_CLOSING,
1258 EL_SWITCHGATE_CLOSED,
1265 EL_TIMEGATE_OPENING,
1273 EL_TIMEGATE_CLOSING,
1282 EL_ACID_SPLASH_LEFT,
1290 EL_ACID_SPLASH_RIGHT,
1299 EL_SP_BUGGY_BASE_ACTIVATING,
1306 EL_SP_BUGGY_BASE_ACTIVATING,
1307 EL_SP_BUGGY_BASE_ACTIVE,
1314 EL_SP_BUGGY_BASE_ACTIVE,
1338 EL_ROBOT_WHEEL_ACTIVE,
1346 EL_TIMEGATE_SWITCH_ACTIVE,
1354 EL_DC_TIMEGATE_SWITCH_ACTIVE,
1355 EL_DC_TIMEGATE_SWITCH,
1362 EL_EMC_MAGIC_BALL_ACTIVE,
1363 EL_EMC_MAGIC_BALL_ACTIVE,
1370 EL_EMC_SPRING_BUMPER_ACTIVE,
1371 EL_EMC_SPRING_BUMPER,
1378 EL_DIAGONAL_SHRINKING,
1386 EL_DIAGONAL_GROWING,
1407 int push_delay_fixed, push_delay_random;
1411 { EL_SPRING, 0, 0 },
1412 { EL_BALLOON, 0, 0 },
1414 { EL_SOKOBAN_OBJECT, 2, 0 },
1415 { EL_SOKOBAN_FIELD_FULL, 2, 0 },
1416 { EL_SATELLITE, 2, 0 },
1417 { EL_SP_DISK_YELLOW, 2, 0 },
1419 { EL_UNDEFINED, 0, 0 },
1427 move_stepsize_list[] =
1429 { EL_AMOEBA_DROP, 2 },
1430 { EL_AMOEBA_DROPPING, 2 },
1431 { EL_QUICKSAND_FILLING, 1 },
1432 { EL_QUICKSAND_EMPTYING, 1 },
1433 { EL_QUICKSAND_FAST_FILLING, 2 },
1434 { EL_QUICKSAND_FAST_EMPTYING, 2 },
1435 { EL_MAGIC_WALL_FILLING, 2 },
1436 { EL_MAGIC_WALL_EMPTYING, 2 },
1437 { EL_BD_MAGIC_WALL_FILLING, 2 },
1438 { EL_BD_MAGIC_WALL_EMPTYING, 2 },
1439 { EL_DC_MAGIC_WALL_FILLING, 2 },
1440 { EL_DC_MAGIC_WALL_EMPTYING, 2 },
1442 { EL_UNDEFINED, 0 },
1450 collect_count_list[] =
1453 { EL_BD_DIAMOND, 1 },
1454 { EL_EMERALD_YELLOW, 1 },
1455 { EL_EMERALD_RED, 1 },
1456 { EL_EMERALD_PURPLE, 1 },
1458 { EL_SP_INFOTRON, 1 },
1462 { EL_UNDEFINED, 0 },
1470 access_direction_list[] =
1472 { EL_TUBE_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1473 { EL_TUBE_VERTICAL, MV_UP | MV_DOWN },
1474 { EL_TUBE_HORIZONTAL, MV_LEFT | MV_RIGHT },
1475 { EL_TUBE_VERTICAL_LEFT, MV_LEFT | MV_UP | MV_DOWN },
1476 { EL_TUBE_VERTICAL_RIGHT, MV_RIGHT | MV_UP | MV_DOWN },
1477 { EL_TUBE_HORIZONTAL_UP, MV_LEFT | MV_RIGHT | MV_UP },
1478 { EL_TUBE_HORIZONTAL_DOWN, MV_LEFT | MV_RIGHT | MV_DOWN },
1479 { EL_TUBE_LEFT_UP, MV_LEFT | MV_UP },
1480 { EL_TUBE_LEFT_DOWN, MV_LEFT | MV_DOWN },
1481 { EL_TUBE_RIGHT_UP, MV_RIGHT | MV_UP },
1482 { EL_TUBE_RIGHT_DOWN, MV_RIGHT | MV_DOWN },
1484 { EL_SP_PORT_LEFT, MV_RIGHT },
1485 { EL_SP_PORT_RIGHT, MV_LEFT },
1486 { EL_SP_PORT_UP, MV_DOWN },
1487 { EL_SP_PORT_DOWN, MV_UP },
1488 { EL_SP_PORT_HORIZONTAL, MV_LEFT | MV_RIGHT },
1489 { EL_SP_PORT_VERTICAL, MV_UP | MV_DOWN },
1490 { EL_SP_PORT_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1491 { EL_SP_GRAVITY_PORT_LEFT, MV_RIGHT },
1492 { EL_SP_GRAVITY_PORT_RIGHT, MV_LEFT },
1493 { EL_SP_GRAVITY_PORT_UP, MV_DOWN },
1494 { EL_SP_GRAVITY_PORT_DOWN, MV_UP },
1495 { EL_SP_GRAVITY_ON_PORT_LEFT, MV_RIGHT },
1496 { EL_SP_GRAVITY_ON_PORT_RIGHT, MV_LEFT },
1497 { EL_SP_GRAVITY_ON_PORT_UP, MV_DOWN },
1498 { EL_SP_GRAVITY_ON_PORT_DOWN, MV_UP },
1499 { EL_SP_GRAVITY_OFF_PORT_LEFT, MV_RIGHT },
1500 { EL_SP_GRAVITY_OFF_PORT_RIGHT, MV_LEFT },
1501 { EL_SP_GRAVITY_OFF_PORT_UP, MV_DOWN },
1502 { EL_SP_GRAVITY_OFF_PORT_DOWN, MV_UP },
1504 { EL_UNDEFINED, MV_NONE }
1507 static boolean trigger_events[MAX_NUM_ELEMENTS][NUM_CHANGE_EVENTS];
1509 #define IS_AUTO_CHANGING(e) (element_info[e].has_change_event[CE_DELAY])
1510 #define IS_JUST_CHANGING(x, y) (ChangeDelay[x][y] != 0)
1511 #define IS_CHANGING(x, y) (IS_AUTO_CHANGING(Feld[x][y]) || \
1512 IS_JUST_CHANGING(x, y))
1514 #define CE_PAGE(e, ce) (element_info[e].event_page[ce])
1516 /* static variables for playfield scan mode (scanning forward or backward) */
1517 static int playfield_scan_start_x = 0;
1518 static int playfield_scan_start_y = 0;
1519 static int playfield_scan_delta_x = 1;
1520 static int playfield_scan_delta_y = 1;
1522 #define SCAN_PLAYFIELD(x, y) for ((y) = playfield_scan_start_y; \
1523 (y) >= 0 && (y) <= lev_fieldy - 1; \
1524 (y) += playfield_scan_delta_y) \
1525 for ((x) = playfield_scan_start_x; \
1526 (x) >= 0 && (x) <= lev_fieldx - 1; \
1527 (x) += playfield_scan_delta_x)
1530 void DEBUG_SetMaximumDynamite()
1534 for (i = 0; i < MAX_INVENTORY_SIZE; i++)
1535 if (local_player->inventory_size < MAX_INVENTORY_SIZE)
1536 local_player->inventory_element[local_player->inventory_size++] =
1541 static void InitPlayfieldScanModeVars()
1543 if (game.use_reverse_scan_direction)
1545 playfield_scan_start_x = lev_fieldx - 1;
1546 playfield_scan_start_y = lev_fieldy - 1;
1548 playfield_scan_delta_x = -1;
1549 playfield_scan_delta_y = -1;
1553 playfield_scan_start_x = 0;
1554 playfield_scan_start_y = 0;
1556 playfield_scan_delta_x = 1;
1557 playfield_scan_delta_y = 1;
1561 static void InitPlayfieldScanMode(int mode)
1563 game.use_reverse_scan_direction =
1564 (mode == CA_ARG_SCAN_MODE_REVERSE ? TRUE : FALSE);
1566 InitPlayfieldScanModeVars();
1569 static int get_move_delay_from_stepsize(int move_stepsize)
1572 MIN(MAX(MOVE_STEPSIZE_MIN, move_stepsize), MOVE_STEPSIZE_MAX);
1574 /* make sure that stepsize value is always a power of 2 */
1575 move_stepsize = (1 << log_2(move_stepsize));
1577 return TILEX / move_stepsize;
1580 static void SetPlayerMoveSpeed(struct PlayerInfo *player, int move_stepsize,
1583 int player_nr = player->index_nr;
1584 int move_delay = get_move_delay_from_stepsize(move_stepsize);
1585 boolean cannot_move = (move_stepsize == STEPSIZE_NOT_MOVING ? TRUE : FALSE);
1587 /* do no immediately change move delay -- the player might just be moving */
1588 player->move_delay_value_next = move_delay;
1590 /* information if player can move must be set separately */
1591 player->cannot_move = cannot_move;
1595 player->move_delay = game.initial_move_delay[player_nr];
1596 player->move_delay_value = game.initial_move_delay_value[player_nr];
1598 player->move_delay_value_next = -1;
1600 player->move_delay_reset_counter = 0;
1604 void GetPlayerConfig()
1606 GameFrameDelay = setup.game_frame_delay;
1608 if (!audio.sound_available)
1609 setup.sound_simple = FALSE;
1611 if (!audio.loops_available)
1612 setup.sound_loops = FALSE;
1614 if (!audio.music_available)
1615 setup.sound_music = FALSE;
1617 if (!video.fullscreen_available)
1618 setup.fullscreen = FALSE;
1620 setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
1622 SetAudioMode(setup.sound);
1625 int GetElementFromGroupElement(int element)
1627 if (IS_GROUP_ELEMENT(element))
1629 struct ElementGroupInfo *group = element_info[element].group;
1630 int last_anim_random_frame = gfx.anim_random_frame;
1633 if (group->choice_mode == ANIM_RANDOM)
1634 gfx.anim_random_frame = RND(group->num_elements_resolved);
1636 element_pos = getAnimationFrame(group->num_elements_resolved, 1,
1637 group->choice_mode, 0,
1640 if (group->choice_mode == ANIM_RANDOM)
1641 gfx.anim_random_frame = last_anim_random_frame;
1643 group->choice_pos++;
1645 element = group->element_resolved[element_pos];
1651 static void InitPlayerField(int x, int y, int element, boolean init_game)
1653 if (element == EL_SP_MURPHY)
1657 if (stored_player[0].present)
1659 Feld[x][y] = EL_SP_MURPHY_CLONE;
1665 stored_player[0].initial_element = element;
1666 stored_player[0].use_murphy = TRUE;
1668 if (!level.use_artwork_element[0])
1669 stored_player[0].artwork_element = EL_SP_MURPHY;
1672 Feld[x][y] = EL_PLAYER_1;
1678 struct PlayerInfo *player = &stored_player[Feld[x][y] - EL_PLAYER_1];
1679 int jx = player->jx, jy = player->jy;
1681 player->present = TRUE;
1683 player->block_last_field = (element == EL_SP_MURPHY ?
1684 level.sp_block_last_field :
1685 level.block_last_field);
1687 /* ---------- initialize player's last field block delay --------------- */
1689 /* always start with reliable default value (no adjustment needed) */
1690 player->block_delay_adjustment = 0;
1692 /* special case 1: in Supaplex, Murphy blocks last field one more frame */
1693 if (player->block_last_field && element == EL_SP_MURPHY)
1694 player->block_delay_adjustment = 1;
1696 /* special case 2: in game engines before 3.1.1, blocking was different */
1697 if (game.use_block_last_field_bug)
1698 player->block_delay_adjustment = (player->block_last_field ? -1 : 1);
1700 if (!options.network || player->connected)
1702 player->active = TRUE;
1704 /* remove potentially duplicate players */
1705 if (StorePlayer[jx][jy] == Feld[x][y])
1706 StorePlayer[jx][jy] = 0;
1708 StorePlayer[x][y] = Feld[x][y];
1710 #if DEBUG_INIT_PLAYER
1713 printf("- player element %d activated", player->element_nr);
1714 printf(" (local player is %d and currently %s)\n",
1715 local_player->element_nr,
1716 local_player->active ? "active" : "not active");
1721 Feld[x][y] = EL_EMPTY;
1723 player->jx = player->last_jx = x;
1724 player->jy = player->last_jy = y;
1729 int player_nr = GET_PLAYER_NR(element);
1730 struct PlayerInfo *player = &stored_player[player_nr];
1732 if (player->active && player->killed)
1733 player->reanimated = TRUE; /* if player was just killed, reanimate him */
1737 static void InitField(int x, int y, boolean init_game)
1739 int element = Feld[x][y];
1748 InitPlayerField(x, y, element, init_game);
1751 case EL_SOKOBAN_FIELD_PLAYER:
1752 element = Feld[x][y] = EL_PLAYER_1;
1753 InitField(x, y, init_game);
1755 element = Feld[x][y] = EL_SOKOBAN_FIELD_EMPTY;
1756 InitField(x, y, init_game);
1759 case EL_SOKOBAN_FIELD_EMPTY:
1760 local_player->sokobanfields_still_needed++;
1764 if (x < lev_fieldx-1 && Feld[x+1][y] == EL_ACID)
1765 Feld[x][y] = EL_ACID_POOL_TOPLEFT;
1766 else if (x > 0 && Feld[x-1][y] == EL_ACID)
1767 Feld[x][y] = EL_ACID_POOL_TOPRIGHT;
1768 else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPLEFT)
1769 Feld[x][y] = EL_ACID_POOL_BOTTOMLEFT;
1770 else if (y > 0 && Feld[x][y-1] == EL_ACID)
1771 Feld[x][y] = EL_ACID_POOL_BOTTOM;
1772 else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPRIGHT)
1773 Feld[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
1782 case EL_SPACESHIP_RIGHT:
1783 case EL_SPACESHIP_UP:
1784 case EL_SPACESHIP_LEFT:
1785 case EL_SPACESHIP_DOWN:
1786 case EL_BD_BUTTERFLY:
1787 case EL_BD_BUTTERFLY_RIGHT:
1788 case EL_BD_BUTTERFLY_UP:
1789 case EL_BD_BUTTERFLY_LEFT:
1790 case EL_BD_BUTTERFLY_DOWN:
1792 case EL_BD_FIREFLY_RIGHT:
1793 case EL_BD_FIREFLY_UP:
1794 case EL_BD_FIREFLY_LEFT:
1795 case EL_BD_FIREFLY_DOWN:
1796 case EL_PACMAN_RIGHT:
1798 case EL_PACMAN_LEFT:
1799 case EL_PACMAN_DOWN:
1801 case EL_YAMYAM_LEFT:
1802 case EL_YAMYAM_RIGHT:
1804 case EL_YAMYAM_DOWN:
1805 case EL_DARK_YAMYAM:
1808 case EL_SP_SNIKSNAK:
1809 case EL_SP_ELECTRON:
1818 case EL_AMOEBA_FULL:
1823 case EL_AMOEBA_DROP:
1824 if (y == lev_fieldy - 1)
1826 Feld[x][y] = EL_AMOEBA_GROWING;
1827 Store[x][y] = EL_AMOEBA_WET;
1831 case EL_DYNAMITE_ACTIVE:
1832 case EL_SP_DISK_RED_ACTIVE:
1833 case EL_DYNABOMB_PLAYER_1_ACTIVE:
1834 case EL_DYNABOMB_PLAYER_2_ACTIVE:
1835 case EL_DYNABOMB_PLAYER_3_ACTIVE:
1836 case EL_DYNABOMB_PLAYER_4_ACTIVE:
1837 MovDelay[x][y] = 96;
1840 case EL_EM_DYNAMITE_ACTIVE:
1841 MovDelay[x][y] = 32;
1845 local_player->lights_still_needed++;
1849 local_player->friends_still_needed++;
1854 GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
1857 case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
1858 case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
1859 case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
1860 case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
1861 case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
1862 case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
1863 case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
1864 case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
1865 case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
1866 case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
1867 case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
1868 case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
1871 int belt_nr = getBeltNrFromBeltSwitchElement(Feld[x][y]);
1872 int belt_dir = getBeltDirFromBeltSwitchElement(Feld[x][y]);
1873 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Feld[x][y]);
1875 if (game.belt_dir_nr[belt_nr] == 3) /* initial value */
1877 game.belt_dir[belt_nr] = belt_dir;
1878 game.belt_dir_nr[belt_nr] = belt_dir_nr;
1880 else /* more than one switch -- set it like the first switch */
1882 Feld[x][y] = Feld[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
1887 case EL_LIGHT_SWITCH_ACTIVE:
1889 game.light_time_left = level.time_light * FRAMES_PER_SECOND;
1892 case EL_INVISIBLE_STEELWALL:
1893 case EL_INVISIBLE_WALL:
1894 case EL_INVISIBLE_SAND:
1895 if (game.light_time_left > 0 ||
1896 game.lenses_time_left > 0)
1897 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
1900 case EL_EMC_MAGIC_BALL:
1901 if (game.ball_state)
1902 Feld[x][y] = EL_EMC_MAGIC_BALL_ACTIVE;
1905 case EL_EMC_MAGIC_BALL_SWITCH:
1906 if (game.ball_state)
1907 Feld[x][y] = EL_EMC_MAGIC_BALL_SWITCH_ACTIVE;
1910 case EL_TRIGGER_PLAYER:
1911 case EL_TRIGGER_ELEMENT:
1912 case EL_TRIGGER_CE_VALUE:
1913 case EL_TRIGGER_CE_SCORE:
1915 case EL_ANY_ELEMENT:
1916 case EL_CURRENT_CE_VALUE:
1917 case EL_CURRENT_CE_SCORE:
1934 /* reference elements should not be used on the playfield */
1935 Feld[x][y] = EL_EMPTY;
1939 if (IS_CUSTOM_ELEMENT(element))
1941 if (CAN_MOVE(element))
1944 if (!element_info[element].use_last_ce_value || init_game)
1945 CustomValue[x][y] = GET_NEW_CE_VALUE(Feld[x][y]);
1947 else if (IS_GROUP_ELEMENT(element))
1949 Feld[x][y] = GetElementFromGroupElement(element);
1951 InitField(x, y, init_game);
1958 CheckTriggeredElementChange(x, y, element, CE_CREATION_OF_X);
1961 inline static void InitField_WithBug1(int x, int y, boolean init_game)
1963 InitField(x, y, init_game);
1965 /* not needed to call InitMovDir() -- already done by InitField()! */
1966 if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1967 CAN_MOVE(Feld[x][y]))
1971 inline static void InitField_WithBug2(int x, int y, boolean init_game)
1973 int old_element = Feld[x][y];
1975 InitField(x, y, init_game);
1977 /* not needed to call InitMovDir() -- already done by InitField()! */
1978 if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1979 CAN_MOVE(old_element) &&
1980 (old_element < EL_MOLE_LEFT || old_element > EL_MOLE_DOWN))
1983 /* this case is in fact a combination of not less than three bugs:
1984 first, it calls InitMovDir() for elements that can move, although this is
1985 already done by InitField(); then, it checks the element that was at this
1986 field _before_ the call to InitField() (which can change it); lastly, it
1987 was not called for "mole with direction" elements, which were treated as
1988 "cannot move" due to (fixed) wrong element initialization in "src/init.c"
1992 static int get_key_element_from_nr(int key_nr)
1994 int key_base_element = (key_nr >= STD_NUM_KEYS ? EL_EMC_KEY_5 - STD_NUM_KEYS :
1995 level.game_engine_type == GAME_ENGINE_TYPE_EM ?
1996 EL_EM_KEY_1 : EL_KEY_1);
1998 return key_base_element + key_nr;
2001 static int get_next_dropped_element(struct PlayerInfo *player)
2003 return (player->inventory_size > 0 ?
2004 player->inventory_element[player->inventory_size - 1] :
2005 player->inventory_infinite_element != EL_UNDEFINED ?
2006 player->inventory_infinite_element :
2007 player->dynabombs_left > 0 ?
2008 EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
2012 static int get_inventory_element_from_pos(struct PlayerInfo *player, int pos)
2014 /* pos >= 0: get element from bottom of the stack;
2015 pos < 0: get element from top of the stack */
2019 int min_inventory_size = -pos;
2020 int inventory_pos = player->inventory_size - min_inventory_size;
2021 int min_dynabombs_left = min_inventory_size - player->inventory_size;
2023 return (player->inventory_size >= min_inventory_size ?
2024 player->inventory_element[inventory_pos] :
2025 player->inventory_infinite_element != EL_UNDEFINED ?
2026 player->inventory_infinite_element :
2027 player->dynabombs_left >= min_dynabombs_left ?
2028 EL_DYNABOMB_PLAYER_1 + player->index_nr :
2033 int min_dynabombs_left = pos + 1;
2034 int min_inventory_size = pos + 1 - player->dynabombs_left;
2035 int inventory_pos = pos - player->dynabombs_left;
2037 return (player->inventory_infinite_element != EL_UNDEFINED ?
2038 player->inventory_infinite_element :
2039 player->dynabombs_left >= min_dynabombs_left ?
2040 EL_DYNABOMB_PLAYER_1 + player->index_nr :
2041 player->inventory_size >= min_inventory_size ?
2042 player->inventory_element[inventory_pos] :
2047 static int compareGamePanelOrderInfo(const void *object1, const void *object2)
2049 const struct GamePanelOrderInfo *gpo1 = (struct GamePanelOrderInfo *)object1;
2050 const struct GamePanelOrderInfo *gpo2 = (struct GamePanelOrderInfo *)object2;
2053 if (gpo1->sort_priority != gpo2->sort_priority)
2054 compare_result = gpo1->sort_priority - gpo2->sort_priority;
2056 compare_result = gpo1->nr - gpo2->nr;
2058 return compare_result;
2061 int getPlayerInventorySize(int player_nr)
2063 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2064 return level.native_em_level->ply[player_nr]->dynamite;
2065 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
2066 return level.native_sp_level->game_sp->red_disk_count;
2068 return stored_player[player_nr].inventory_size;
2071 void InitGameControlValues()
2075 for (i = 0; game_panel_controls[i].nr != -1; i++)
2077 struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2078 struct GamePanelOrderInfo *gpo = &game_panel_order[i];
2079 struct TextPosInfo *pos = gpc->pos;
2081 int type = gpc->type;
2085 Error(ERR_INFO, "'game_panel_controls' structure corrupted at %d", i);
2086 Error(ERR_EXIT, "this should not happen -- please debug");
2089 /* force update of game controls after initialization */
2090 gpc->value = gpc->last_value = -1;
2091 gpc->frame = gpc->last_frame = -1;
2092 gpc->gfx_frame = -1;
2094 /* determine panel value width for later calculation of alignment */
2095 if (type == TYPE_INTEGER || type == TYPE_STRING)
2097 pos->width = pos->size * getFontWidth(pos->font);
2098 pos->height = getFontHeight(pos->font);
2100 else if (type == TYPE_ELEMENT)
2102 pos->width = pos->size;
2103 pos->height = pos->size;
2106 /* fill structure for game panel draw order */
2108 gpo->sort_priority = pos->sort_priority;
2111 /* sort game panel controls according to sort_priority and control number */
2112 qsort(game_panel_order, NUM_GAME_PANEL_CONTROLS,
2113 sizeof(struct GamePanelOrderInfo), compareGamePanelOrderInfo);
2116 void UpdatePlayfieldElementCount()
2118 boolean use_element_count = FALSE;
2121 /* first check if it is needed at all to calculate playfield element count */
2122 for (i = GAME_PANEL_ELEMENT_COUNT_1; i <= GAME_PANEL_ELEMENT_COUNT_8; i++)
2123 if (!PANEL_DEACTIVATED(game_panel_controls[i].pos))
2124 use_element_count = TRUE;
2126 if (!use_element_count)
2129 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2130 element_info[i].element_count = 0;
2132 SCAN_PLAYFIELD(x, y)
2134 element_info[Feld[x][y]].element_count++;
2137 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
2138 for (j = 0; j < MAX_NUM_ELEMENTS; j++)
2139 if (IS_IN_GROUP(j, i))
2140 element_info[EL_GROUP_START + i].element_count +=
2141 element_info[j].element_count;
2144 void UpdateGameControlValues()
2147 int time = (local_player->LevelSolved ?
2148 local_player->LevelSolved_CountingTime :
2149 level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2150 level.native_em_level->lev->time :
2151 level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2152 level.native_sp_level->game_sp->time_played :
2153 level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2154 game_mm.energy_left :
2155 game.no_time_limit ? TimePlayed : TimeLeft);
2156 int score = (local_player->LevelSolved ?
2157 local_player->LevelSolved_CountingScore :
2158 level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2159 level.native_em_level->lev->score :
2160 level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2161 level.native_sp_level->game_sp->score :
2162 level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2164 local_player->score);
2165 int gems = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2166 level.native_em_level->lev->required :
2167 level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2168 level.native_sp_level->game_sp->infotrons_still_needed :
2169 level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2170 game_mm.kettles_still_needed :
2171 local_player->gems_still_needed);
2172 int exit_closed = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2173 level.native_em_level->lev->required > 0 :
2174 level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2175 level.native_sp_level->game_sp->infotrons_still_needed > 0 :
2176 level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2177 game_mm.kettles_still_needed > 0 ||
2178 game_mm.lights_still_needed > 0 :
2179 local_player->gems_still_needed > 0 ||
2180 local_player->sokobanfields_still_needed > 0 ||
2181 local_player->lights_still_needed > 0);
2183 UpdatePlayfieldElementCount();
2185 /* update game panel control values */
2187 game_panel_controls[GAME_PANEL_LEVEL_NUMBER].value = level_nr;
2188 game_panel_controls[GAME_PANEL_GEMS].value = gems;
2190 game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value = 0;
2191 for (i = 0; i < MAX_NUM_KEYS; i++)
2192 game_panel_controls[GAME_PANEL_KEY_1 + i].value = EL_EMPTY;
2193 game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_EMPTY;
2194 game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value = 0;
2196 if (game.centered_player_nr == -1)
2198 for (i = 0; i < MAX_PLAYERS; i++)
2200 /* only one player in Supaplex game engine */
2201 if (level.game_engine_type == GAME_ENGINE_TYPE_SP && i > 0)
2204 for (k = 0; k < MAX_NUM_KEYS; k++)
2206 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2208 if (level.native_em_level->ply[i]->keys & (1 << k))
2209 game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2210 get_key_element_from_nr(k);
2212 else if (stored_player[i].key[k])
2213 game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2214 get_key_element_from_nr(k);
2217 game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2218 getPlayerInventorySize(i);
2220 if (stored_player[i].num_white_keys > 0)
2221 game_panel_controls[GAME_PANEL_KEY_WHITE].value =
2224 game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2225 stored_player[i].num_white_keys;
2230 int player_nr = game.centered_player_nr;
2232 for (k = 0; k < MAX_NUM_KEYS; k++)
2234 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2236 if (level.native_em_level->ply[player_nr]->keys & (1 << k))
2237 game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2238 get_key_element_from_nr(k);
2240 else if (stored_player[player_nr].key[k])
2241 game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2242 get_key_element_from_nr(k);
2245 game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2246 getPlayerInventorySize(player_nr);
2248 if (stored_player[player_nr].num_white_keys > 0)
2249 game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_DC_KEY_WHITE;
2251 game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2252 stored_player[player_nr].num_white_keys;
2255 for (i = 0; i < NUM_PANEL_INVENTORY; i++)
2257 game_panel_controls[GAME_PANEL_INVENTORY_FIRST_1 + i].value =
2258 get_inventory_element_from_pos(local_player, i);
2259 game_panel_controls[GAME_PANEL_INVENTORY_LAST_1 + i].value =
2260 get_inventory_element_from_pos(local_player, -i - 1);
2263 game_panel_controls[GAME_PANEL_SCORE].value = score;
2264 game_panel_controls[GAME_PANEL_HIGHSCORE].value = highscore[0].Score;
2266 game_panel_controls[GAME_PANEL_TIME].value = time;
2268 game_panel_controls[GAME_PANEL_TIME_HH].value = time / 3600;
2269 game_panel_controls[GAME_PANEL_TIME_MM].value = (time / 60) % 60;
2270 game_panel_controls[GAME_PANEL_TIME_SS].value = time % 60;
2272 game_panel_controls[GAME_PANEL_FRAME].value = FrameCounter;
2274 game_panel_controls[GAME_PANEL_SHIELD_NORMAL].value =
2275 (local_player->shield_normal_time_left > 0 ? EL_SHIELD_NORMAL_ACTIVE :
2277 game_panel_controls[GAME_PANEL_SHIELD_NORMAL_TIME].value =
2278 local_player->shield_normal_time_left;
2279 game_panel_controls[GAME_PANEL_SHIELD_DEADLY].value =
2280 (local_player->shield_deadly_time_left > 0 ? EL_SHIELD_DEADLY_ACTIVE :
2282 game_panel_controls[GAME_PANEL_SHIELD_DEADLY_TIME].value =
2283 local_player->shield_deadly_time_left;
2285 game_panel_controls[GAME_PANEL_EXIT].value =
2286 (exit_closed ? EL_EXIT_CLOSED : EL_EXIT_OPEN);
2288 game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL].value =
2289 (game.ball_state ? EL_EMC_MAGIC_BALL_ACTIVE : EL_EMC_MAGIC_BALL);
2290 game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL_SWITCH].value =
2291 (game.ball_state ? EL_EMC_MAGIC_BALL_SWITCH_ACTIVE :
2292 EL_EMC_MAGIC_BALL_SWITCH);
2294 game_panel_controls[GAME_PANEL_LIGHT_SWITCH].value =
2295 (game.light_time_left > 0 ? EL_LIGHT_SWITCH_ACTIVE : EL_LIGHT_SWITCH);
2296 game_panel_controls[GAME_PANEL_LIGHT_SWITCH_TIME].value =
2297 game.light_time_left;
2299 game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH].value =
2300 (game.timegate_time_left > 0 ? EL_TIMEGATE_OPEN : EL_TIMEGATE_CLOSED);
2301 game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH_TIME].value =
2302 game.timegate_time_left;
2304 game_panel_controls[GAME_PANEL_SWITCHGATE_SWITCH].value =
2305 EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
2307 game_panel_controls[GAME_PANEL_EMC_LENSES].value =
2308 (game.lenses_time_left > 0 ? EL_EMC_LENSES : EL_EMPTY);
2309 game_panel_controls[GAME_PANEL_EMC_LENSES_TIME].value =
2310 game.lenses_time_left;
2312 game_panel_controls[GAME_PANEL_EMC_MAGNIFIER].value =
2313 (game.magnify_time_left > 0 ? EL_EMC_MAGNIFIER : EL_EMPTY);
2314 game_panel_controls[GAME_PANEL_EMC_MAGNIFIER_TIME].value =
2315 game.magnify_time_left;
2317 game_panel_controls[GAME_PANEL_BALLOON_SWITCH].value =
2318 (game.wind_direction == MV_LEFT ? EL_BALLOON_SWITCH_LEFT :
2319 game.wind_direction == MV_RIGHT ? EL_BALLOON_SWITCH_RIGHT :
2320 game.wind_direction == MV_UP ? EL_BALLOON_SWITCH_UP :
2321 game.wind_direction == MV_DOWN ? EL_BALLOON_SWITCH_DOWN :
2322 EL_BALLOON_SWITCH_NONE);
2324 game_panel_controls[GAME_PANEL_DYNABOMB_NUMBER].value =
2325 local_player->dynabomb_count;
2326 game_panel_controls[GAME_PANEL_DYNABOMB_SIZE].value =
2327 local_player->dynabomb_size;
2328 game_panel_controls[GAME_PANEL_DYNABOMB_POWER].value =
2329 (local_player->dynabomb_xl ? EL_DYNABOMB_INCREASE_POWER : EL_EMPTY);
2331 game_panel_controls[GAME_PANEL_PENGUINS].value =
2332 local_player->friends_still_needed;
2334 game_panel_controls[GAME_PANEL_SOKOBAN_OBJECTS].value =
2335 local_player->sokobanfields_still_needed;
2336 game_panel_controls[GAME_PANEL_SOKOBAN_FIELDS].value =
2337 local_player->sokobanfields_still_needed;
2339 game_panel_controls[GAME_PANEL_ROBOT_WHEEL].value =
2340 (game.robot_wheel_active ? EL_ROBOT_WHEEL_ACTIVE : EL_ROBOT_WHEEL);
2342 for (i = 0; i < NUM_BELTS; i++)
2344 game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1 + i].value =
2345 (game.belt_dir[i] != MV_NONE ? EL_CONVEYOR_BELT_1_MIDDLE_ACTIVE :
2346 EL_CONVEYOR_BELT_1_MIDDLE) + i;
2347 game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1_SWITCH + i].value =
2348 getBeltSwitchElementFromBeltNrAndBeltDir(i, game.belt_dir[i]);
2351 game_panel_controls[GAME_PANEL_MAGIC_WALL].value =
2352 (game.magic_wall_active ? EL_MAGIC_WALL_ACTIVE : EL_MAGIC_WALL);
2353 game_panel_controls[GAME_PANEL_MAGIC_WALL_TIME].value =
2354 game.magic_wall_time_left;
2356 game_panel_controls[GAME_PANEL_GRAVITY_STATE].value =
2357 local_player->gravity;
2359 for (i = 0; i < NUM_PANEL_GRAPHICS; i++)
2360 game_panel_controls[GAME_PANEL_GRAPHIC_1 + i].value = EL_GRAPHIC_1 + i;
2362 for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2363 game_panel_controls[GAME_PANEL_ELEMENT_1 + i].value =
2364 (IS_DRAWABLE_ELEMENT(game.panel.element[i].id) ?
2365 game.panel.element[i].id : EL_UNDEFINED);
2367 for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2368 game_panel_controls[GAME_PANEL_ELEMENT_COUNT_1 + i].value =
2369 (IS_VALID_ELEMENT(game.panel.element_count[i].id) ?
2370 element_info[game.panel.element_count[i].id].element_count : 0);
2372 for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2373 game_panel_controls[GAME_PANEL_CE_SCORE_1 + i].value =
2374 (IS_CUSTOM_ELEMENT(game.panel.ce_score[i].id) ?
2375 element_info[game.panel.ce_score[i].id].collect_score : 0);
2377 for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2378 game_panel_controls[GAME_PANEL_CE_SCORE_1_ELEMENT + i].value =
2379 (IS_CUSTOM_ELEMENT(game.panel.ce_score_element[i].id) ?
2380 element_info[game.panel.ce_score_element[i].id].collect_score :
2383 game_panel_controls[GAME_PANEL_PLAYER_NAME].value = 0;
2384 game_panel_controls[GAME_PANEL_LEVEL_NAME].value = 0;
2385 game_panel_controls[GAME_PANEL_LEVEL_AUTHOR].value = 0;
2387 /* update game panel control frames */
2389 for (i = 0; game_panel_controls[i].nr != -1; i++)
2391 struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2393 if (gpc->type == TYPE_ELEMENT)
2395 if (gpc->value != EL_UNDEFINED && gpc->value != EL_EMPTY)
2397 int last_anim_random_frame = gfx.anim_random_frame;
2398 int element = gpc->value;
2399 int graphic = el2panelimg(element);
2401 if (gpc->value != gpc->last_value)
2404 gpc->gfx_random = INIT_GFX_RANDOM();
2410 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2411 IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2412 gpc->gfx_random = INIT_GFX_RANDOM();
2415 if (ANIM_MODE(graphic) == ANIM_RANDOM)
2416 gfx.anim_random_frame = gpc->gfx_random;
2418 if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
2419 gpc->gfx_frame = element_info[element].collect_score;
2421 gpc->frame = getGraphicAnimationFrame(el2panelimg(gpc->value),
2424 if (ANIM_MODE(graphic) == ANIM_RANDOM)
2425 gfx.anim_random_frame = last_anim_random_frame;
2431 void DisplayGameControlValues()
2433 boolean redraw_panel = FALSE;
2436 for (i = 0; game_panel_controls[i].nr != -1; i++)
2438 struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2440 if (PANEL_DEACTIVATED(gpc->pos))
2443 if (gpc->value == gpc->last_value &&
2444 gpc->frame == gpc->last_frame)
2447 redraw_panel = TRUE;
2453 /* copy default game door content to main double buffer */
2455 /* !!! CHECK AGAIN !!! */
2456 SetPanelBackground();
2457 // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
2458 DrawBackground(DX, DY, DXSIZE, DYSIZE);
2460 /* redraw game control buttons */
2461 RedrawGameButtons();
2463 SetGameStatus(GAME_MODE_PSEUDO_PANEL);
2465 for (i = 0; i < NUM_GAME_PANEL_CONTROLS; i++)
2467 int nr = game_panel_order[i].nr;
2468 struct GamePanelControlInfo *gpc = &game_panel_controls[nr];
2469 struct TextPosInfo *pos = gpc->pos;
2470 int type = gpc->type;
2471 int value = gpc->value;
2472 int frame = gpc->frame;
2473 int size = pos->size;
2474 int font = pos->font;
2475 boolean draw_masked = pos->draw_masked;
2476 int mask_mode = (draw_masked ? BLIT_MASKED : BLIT_OPAQUE);
2478 if (PANEL_DEACTIVATED(pos))
2481 gpc->last_value = value;
2482 gpc->last_frame = frame;
2484 if (type == TYPE_INTEGER)
2486 if (nr == GAME_PANEL_LEVEL_NUMBER ||
2487 nr == GAME_PANEL_TIME)
2489 boolean use_dynamic_size = (size == -1 ? TRUE : FALSE);
2491 if (use_dynamic_size) /* use dynamic number of digits */
2493 int value_change = (nr == GAME_PANEL_LEVEL_NUMBER ? 100 : 1000);
2494 int size1 = (nr == GAME_PANEL_LEVEL_NUMBER ? 2 : 3);
2495 int size2 = size1 + 1;
2496 int font1 = pos->font;
2497 int font2 = pos->font_alt;
2499 size = (value < value_change ? size1 : size2);
2500 font = (value < value_change ? font1 : font2);
2504 /* correct text size if "digits" is zero or less */
2506 size = strlen(int2str(value, size));
2508 /* dynamically correct text alignment */
2509 pos->width = size * getFontWidth(font);
2511 DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2512 int2str(value, size), font, mask_mode);
2514 else if (type == TYPE_ELEMENT)
2516 int element, graphic;
2520 int dst_x = PANEL_XPOS(pos);
2521 int dst_y = PANEL_YPOS(pos);
2523 if (value != EL_UNDEFINED && value != EL_EMPTY)
2526 graphic = el2panelimg(value);
2528 // printf("::: %d, '%s' [%d]\n", element, EL_NAME(element), size);
2530 if (element >= EL_GRAPHIC_1 && element <= EL_GRAPHIC_8 && size == 0)
2533 getSizedGraphicSource(graphic, frame, size, &src_bitmap,
2536 width = graphic_info[graphic].width * size / TILESIZE;
2537 height = graphic_info[graphic].height * size / TILESIZE;
2540 BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2543 BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2547 else if (type == TYPE_STRING)
2549 boolean active = (value != 0);
2550 char *state_normal = "off";
2551 char *state_active = "on";
2552 char *state = (active ? state_active : state_normal);
2553 char *s = (nr == GAME_PANEL_GRAVITY_STATE ? state :
2554 nr == GAME_PANEL_PLAYER_NAME ? setup.player_name :
2555 nr == GAME_PANEL_LEVEL_NAME ? level.name :
2556 nr == GAME_PANEL_LEVEL_AUTHOR ? level.author : NULL);
2558 if (nr == GAME_PANEL_GRAVITY_STATE)
2560 int font1 = pos->font; /* (used for normal state) */
2561 int font2 = pos->font_alt; /* (used for active state) */
2563 font = (active ? font2 : font1);
2572 /* don't truncate output if "chars" is zero or less */
2575 /* dynamically correct text alignment */
2576 pos->width = size * getFontWidth(font);
2579 s_cut = getStringCopyN(s, size);
2581 DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2582 s_cut, font, mask_mode);
2588 redraw_mask |= REDRAW_DOOR_1;
2591 SetGameStatus(GAME_MODE_PLAYING);
2594 void UpdateAndDisplayGameControlValues()
2596 if (tape.deactivate_display)
2599 UpdateGameControlValues();
2600 DisplayGameControlValues();
2603 void UpdateGameDoorValues()
2605 UpdateGameControlValues();
2608 void DrawGameDoorValues()
2610 DisplayGameControlValues();
2615 =============================================================================
2617 -----------------------------------------------------------------------------
2618 initialize game engine due to level / tape version number
2619 =============================================================================
2622 static void InitGameEngine()
2624 int i, j, k, l, x, y;
2626 /* set game engine from tape file when re-playing, else from level file */
2627 game.engine_version = (tape.playing ? tape.engine_version :
2628 level.game_version);
2630 /* set single or multi-player game mode (needed for re-playing tapes) */
2631 game.team_mode = setup.team_mode;
2635 int num_players = 0;
2637 for (i = 0; i < MAX_PLAYERS; i++)
2638 if (tape.player_participates[i])
2641 /* multi-player tapes contain input data for more than one player */
2642 game.team_mode = (num_players > 1);
2645 /* ---------------------------------------------------------------------- */
2646 /* set flags for bugs and changes according to active game engine version */
2647 /* ---------------------------------------------------------------------- */
2650 Summary of bugfix/change:
2651 Fixed handling for custom elements that change when pushed by the player.
2653 Fixed/changed in version:
2657 Before 3.1.0, custom elements that "change when pushing" changed directly
2658 after the player started pushing them (until then handled in "DigField()").
2659 Since 3.1.0, these custom elements are not changed until the "pushing"
2660 move of the element is finished (now handled in "ContinueMoving()").
2662 Affected levels/tapes:
2663 The first condition is generally needed for all levels/tapes before version
2664 3.1.0, which might use the old behaviour before it was changed; known tapes
2665 that are affected are some tapes from the level set "Walpurgis Gardens" by
2667 The second condition is an exception from the above case and is needed for
2668 the special case of tapes recorded with game (not engine!) version 3.1.0 or
2669 above (including some development versions of 3.1.0), but before it was
2670 known that this change would break tapes like the above and was fixed in
2671 3.1.1, so that the changed behaviour was active although the engine version
2672 while recording maybe was before 3.1.0. There is at least one tape that is
2673 affected by this exception, which is the tape for the one-level set "Bug
2674 Machine" by Juergen Bonhagen.
2677 game.use_change_when_pushing_bug =
2678 (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2680 tape.game_version >= VERSION_IDENT(3,1,0,0) &&
2681 tape.game_version < VERSION_IDENT(3,1,1,0)));
2684 Summary of bugfix/change:
2685 Fixed handling for blocking the field the player leaves when moving.
2687 Fixed/changed in version:
2691 Before 3.1.1, when "block last field when moving" was enabled, the field
2692 the player is leaving when moving was blocked for the time of the move,
2693 and was directly unblocked afterwards. This resulted in the last field
2694 being blocked for exactly one less than the number of frames of one player
2695 move. Additionally, even when blocking was disabled, the last field was
2696 blocked for exactly one frame.
2697 Since 3.1.1, due to changes in player movement handling, the last field
2698 is not blocked at all when blocking is disabled. When blocking is enabled,
2699 the last field is blocked for exactly the number of frames of one player
2700 move. Additionally, if the player is Murphy, the hero of Supaplex, the
2701 last field is blocked for exactly one more than the number of frames of
2704 Affected levels/tapes:
2705 (!!! yet to be determined -- probably many !!!)
2708 game.use_block_last_field_bug =
2709 (game.engine_version < VERSION_IDENT(3,1,1,0));
2711 game_em.use_single_button =
2712 (game.engine_version > VERSION_IDENT(4,0,0,2));
2714 game_em.use_snap_key_bug =
2715 (game.engine_version < VERSION_IDENT(4,0,1,0));
2717 /* ---------------------------------------------------------------------- */
2719 /* set maximal allowed number of custom element changes per game frame */
2720 game.max_num_changes_per_frame = 1;
2722 /* default scan direction: scan playfield from top/left to bottom/right */
2723 InitPlayfieldScanMode(CA_ARG_SCAN_MODE_NORMAL);
2725 /* dynamically adjust element properties according to game engine version */
2726 InitElementPropertiesEngine(game.engine_version);
2729 printf("level %d: level version == %06d\n", level_nr, level.game_version);
2730 printf(" tape version == %06d [%s] [file: %06d]\n",
2731 tape.engine_version, (tape.playing ? "PLAYING" : "RECORDING"),
2733 printf(" => game.engine_version == %06d\n", game.engine_version);
2736 /* ---------- initialize player's initial move delay --------------------- */
2738 /* dynamically adjust player properties according to level information */
2739 for (i = 0; i < MAX_PLAYERS; i++)
2740 game.initial_move_delay_value[i] =
2741 get_move_delay_from_stepsize(level.initial_player_stepsize[i]);
2743 /* dynamically adjust player properties according to game engine version */
2744 for (i = 0; i < MAX_PLAYERS; i++)
2745 game.initial_move_delay[i] =
2746 (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
2747 game.initial_move_delay_value[i] : 0);
2749 /* ---------- initialize player's initial push delay --------------------- */
2751 /* dynamically adjust player properties according to game engine version */
2752 game.initial_push_delay_value =
2753 (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
2755 /* ---------- initialize changing elements ------------------------------- */
2757 /* initialize changing elements information */
2758 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2760 struct ElementInfo *ei = &element_info[i];
2762 /* this pointer might have been changed in the level editor */
2763 ei->change = &ei->change_page[0];
2765 if (!IS_CUSTOM_ELEMENT(i))
2767 ei->change->target_element = EL_EMPTY_SPACE;
2768 ei->change->delay_fixed = 0;
2769 ei->change->delay_random = 0;
2770 ei->change->delay_frames = 1;
2773 for (j = 0; j < NUM_CHANGE_EVENTS; j++)
2775 ei->has_change_event[j] = FALSE;
2777 ei->event_page_nr[j] = 0;
2778 ei->event_page[j] = &ei->change_page[0];
2782 /* add changing elements from pre-defined list */
2783 for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
2785 struct ChangingElementInfo *ch_delay = &change_delay_list[i];
2786 struct ElementInfo *ei = &element_info[ch_delay->element];
2788 ei->change->target_element = ch_delay->target_element;
2789 ei->change->delay_fixed = ch_delay->change_delay;
2791 ei->change->pre_change_function = ch_delay->pre_change_function;
2792 ei->change->change_function = ch_delay->change_function;
2793 ei->change->post_change_function = ch_delay->post_change_function;
2795 ei->change->can_change = TRUE;
2796 ei->change->can_change_or_has_action = TRUE;
2798 ei->has_change_event[CE_DELAY] = TRUE;
2800 SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
2801 SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE_OR_HAS_ACTION, TRUE);
2804 /* ---------- initialize internal run-time variables --------------------- */
2806 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2808 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
2810 for (j = 0; j < ei->num_change_pages; j++)
2812 ei->change_page[j].can_change_or_has_action =
2813 (ei->change_page[j].can_change |
2814 ei->change_page[j].has_action);
2818 /* add change events from custom element configuration */
2819 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2821 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
2823 for (j = 0; j < ei->num_change_pages; j++)
2825 if (!ei->change_page[j].can_change_or_has_action)
2828 for (k = 0; k < NUM_CHANGE_EVENTS; k++)
2830 /* only add event page for the first page found with this event */
2831 if (ei->change_page[j].has_event[k] && !(ei->has_change_event[k]))
2833 ei->has_change_event[k] = TRUE;
2835 ei->event_page_nr[k] = j;
2836 ei->event_page[k] = &ei->change_page[j];
2842 /* ---------- initialize reference elements in change conditions --------- */
2844 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2846 int element = EL_CUSTOM_START + i;
2847 struct ElementInfo *ei = &element_info[element];
2849 for (j = 0; j < ei->num_change_pages; j++)
2851 int trigger_element = ei->change_page[j].initial_trigger_element;
2853 if (trigger_element >= EL_PREV_CE_8 &&
2854 trigger_element <= EL_NEXT_CE_8)
2855 trigger_element = RESOLVED_REFERENCE_ELEMENT(element, trigger_element);
2857 ei->change_page[j].trigger_element = trigger_element;
2861 /* ---------- initialize run-time trigger player and element ------------- */
2863 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2865 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
2867 for (j = 0; j < ei->num_change_pages; j++)
2869 ei->change_page[j].actual_trigger_element = EL_EMPTY;
2870 ei->change_page[j].actual_trigger_player = EL_EMPTY;
2871 ei->change_page[j].actual_trigger_player_bits = CH_PLAYER_NONE;
2872 ei->change_page[j].actual_trigger_side = CH_SIDE_NONE;
2873 ei->change_page[j].actual_trigger_ce_value = 0;
2874 ei->change_page[j].actual_trigger_ce_score = 0;
2878 /* ---------- initialize trigger events ---------------------------------- */
2880 /* initialize trigger events information */
2881 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2882 for (j = 0; j < NUM_CHANGE_EVENTS; j++)
2883 trigger_events[i][j] = FALSE;
2885 /* add trigger events from element change event properties */
2886 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2888 struct ElementInfo *ei = &element_info[i];
2890 for (j = 0; j < ei->num_change_pages; j++)
2892 if (!ei->change_page[j].can_change_or_has_action)
2895 if (ei->change_page[j].has_event[CE_BY_OTHER_ACTION])
2897 int trigger_element = ei->change_page[j].trigger_element;
2899 for (k = 0; k < NUM_CHANGE_EVENTS; k++)
2901 if (ei->change_page[j].has_event[k])
2903 if (IS_GROUP_ELEMENT(trigger_element))
2905 struct ElementGroupInfo *group =
2906 element_info[trigger_element].group;
2908 for (l = 0; l < group->num_elements_resolved; l++)
2909 trigger_events[group->element_resolved[l]][k] = TRUE;
2911 else if (trigger_element == EL_ANY_ELEMENT)
2912 for (l = 0; l < MAX_NUM_ELEMENTS; l++)
2913 trigger_events[l][k] = TRUE;
2915 trigger_events[trigger_element][k] = TRUE;
2922 /* ---------- initialize push delay -------------------------------------- */
2924 /* initialize push delay values to default */
2925 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2927 if (!IS_CUSTOM_ELEMENT(i))
2929 /* set default push delay values (corrected since version 3.0.7-1) */
2930 if (game.engine_version < VERSION_IDENT(3,0,7,1))
2932 element_info[i].push_delay_fixed = 2;
2933 element_info[i].push_delay_random = 8;
2937 element_info[i].push_delay_fixed = 8;
2938 element_info[i].push_delay_random = 8;
2943 /* set push delay value for certain elements from pre-defined list */
2944 for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
2946 int e = push_delay_list[i].element;
2948 element_info[e].push_delay_fixed = push_delay_list[i].push_delay_fixed;
2949 element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
2952 /* set push delay value for Supaplex elements for newer engine versions */
2953 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
2955 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2957 if (IS_SP_ELEMENT(i))
2959 /* set SP push delay to just enough to push under a falling zonk */
2960 int delay = (game.engine_version >= VERSION_IDENT(3,1,1,0) ? 8 : 6);
2962 element_info[i].push_delay_fixed = delay;
2963 element_info[i].push_delay_random = 0;
2968 /* ---------- initialize move stepsize ----------------------------------- */
2970 /* initialize move stepsize values to default */
2971 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2972 if (!IS_CUSTOM_ELEMENT(i))
2973 element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
2975 /* set move stepsize value for certain elements from pre-defined list */
2976 for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
2978 int e = move_stepsize_list[i].element;
2980 element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
2983 /* ---------- initialize collect score ----------------------------------- */
2985 /* initialize collect score values for custom elements from initial value */
2986 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2987 if (IS_CUSTOM_ELEMENT(i))
2988 element_info[i].collect_score = element_info[i].collect_score_initial;
2990 /* ---------- initialize collect count ----------------------------------- */
2992 /* initialize collect count values for non-custom elements */
2993 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2994 if (!IS_CUSTOM_ELEMENT(i))
2995 element_info[i].collect_count_initial = 0;
2997 /* add collect count values for all elements from pre-defined list */
2998 for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
2999 element_info[collect_count_list[i].element].collect_count_initial =
3000 collect_count_list[i].count;
3002 /* ---------- initialize access direction -------------------------------- */
3004 /* initialize access direction values to default (access from every side) */
3005 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3006 if (!IS_CUSTOM_ELEMENT(i))
3007 element_info[i].access_direction = MV_ALL_DIRECTIONS;
3009 /* set access direction value for certain elements from pre-defined list */
3010 for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
3011 element_info[access_direction_list[i].element].access_direction =
3012 access_direction_list[i].direction;
3014 /* ---------- initialize explosion content ------------------------------- */
3015 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3017 if (IS_CUSTOM_ELEMENT(i))
3020 for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
3022 /* (content for EL_YAMYAM set at run-time with game.yamyam_content_nr) */
3024 element_info[i].content.e[x][y] =
3025 (i == EL_PLAYER_1 ? EL_EMERALD_YELLOW :
3026 i == EL_PLAYER_2 ? EL_EMERALD_RED :
3027 i == EL_PLAYER_3 ? EL_EMERALD :
3028 i == EL_PLAYER_4 ? EL_EMERALD_PURPLE :
3029 i == EL_MOLE ? EL_EMERALD_RED :
3030 i == EL_PENGUIN ? EL_EMERALD_PURPLE :
3031 i == EL_BUG ? (x == 1 && y == 1 ? EL_DIAMOND : EL_EMERALD) :
3032 i == EL_BD_BUTTERFLY ? EL_BD_DIAMOND :
3033 i == EL_SP_ELECTRON ? EL_SP_INFOTRON :
3034 i == EL_AMOEBA_TO_DIAMOND ? level.amoeba_content :
3035 i == EL_WALL_EMERALD ? EL_EMERALD :
3036 i == EL_WALL_DIAMOND ? EL_DIAMOND :
3037 i == EL_WALL_BD_DIAMOND ? EL_BD_DIAMOND :
3038 i == EL_WALL_EMERALD_YELLOW ? EL_EMERALD_YELLOW :
3039 i == EL_WALL_EMERALD_RED ? EL_EMERALD_RED :
3040 i == EL_WALL_EMERALD_PURPLE ? EL_EMERALD_PURPLE :
3041 i == EL_WALL_PEARL ? EL_PEARL :
3042 i == EL_WALL_CRYSTAL ? EL_CRYSTAL :
3047 /* ---------- initialize recursion detection ------------------------------ */
3048 recursion_loop_depth = 0;
3049 recursion_loop_detected = FALSE;
3050 recursion_loop_element = EL_UNDEFINED;
3052 /* ---------- initialize graphics engine ---------------------------------- */
3053 game.scroll_delay_value =
3054 (game.forced_scroll_delay_value != -1 ? game.forced_scroll_delay_value :
3055 setup.scroll_delay ? setup.scroll_delay_value : 0);
3056 game.scroll_delay_value =
3057 MIN(MAX(MIN_SCROLL_DELAY, game.scroll_delay_value), MAX_SCROLL_DELAY);
3059 /* ---------- initialize game engine snapshots ---------------------------- */
3060 for (i = 0; i < MAX_PLAYERS; i++)
3061 game.snapshot.last_action[i] = 0;
3062 game.snapshot.changed_action = FALSE;
3063 game.snapshot.collected_item = FALSE;
3064 game.snapshot.mode =
3065 (strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_STEP) ?
3066 SNAPSHOT_MODE_EVERY_STEP :
3067 strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_MOVE) ?
3068 SNAPSHOT_MODE_EVERY_MOVE :
3069 strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_COLLECT) ?
3070 SNAPSHOT_MODE_EVERY_COLLECT : SNAPSHOT_MODE_OFF);
3071 game.snapshot.save_snapshot = FALSE;
3073 /* ---------- initialize level time for Supaplex engine ------------------- */
3074 /* Supaplex levels with time limit currently unsupported -- should be added */
3075 if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
3079 int get_num_special_action(int element, int action_first, int action_last)
3081 int num_special_action = 0;
3084 for (i = action_first; i <= action_last; i++)
3086 boolean found = FALSE;
3088 for (j = 0; j < NUM_DIRECTIONS; j++)
3089 if (el_act_dir2img(element, i, j) !=
3090 el_act_dir2img(element, ACTION_DEFAULT, j))
3094 num_special_action++;
3099 return num_special_action;
3104 =============================================================================
3106 -----------------------------------------------------------------------------
3107 initialize and start new game
3108 =============================================================================
3113 int full_lev_fieldx = lev_fieldx + (BorderElement != EL_EMPTY ? 2 : 0);
3114 int full_lev_fieldy = lev_fieldy + (BorderElement != EL_EMPTY ? 2 : 0);
3115 int fade_mask = REDRAW_FIELD;
3117 boolean emulate_bd = TRUE; /* unless non-BOULDERDASH elements found */
3118 boolean emulate_sb = TRUE; /* unless non-SOKOBAN elements found */
3119 boolean emulate_sp = TRUE; /* unless non-SUPAPLEX elements found */
3120 int initial_move_dir = MV_DOWN;
3123 // required here to update video display before fading (FIX THIS)
3124 DrawMaskedBorder(REDRAW_DOOR_2);
3126 if (!game.restart_level)
3127 CloseDoor(DOOR_CLOSE_1);
3129 SetGameStatus(GAME_MODE_PLAYING);
3131 if (level_editor_test_game)
3132 FadeSkipNextFadeIn();
3134 FadeSetEnterScreen();
3136 if (CheckIfGlobalBorderHasChanged())
3137 fade_mask = REDRAW_ALL;
3139 FadeLevelSoundsAndMusic();
3141 ExpireSoundLoops(TRUE);
3145 /* needed if different viewport properties defined for playing */
3146 ChangeViewportPropertiesIfNeeded();
3150 OpenDoor(GetDoorState() | DOOR_NO_DELAY | DOOR_FORCE_REDRAW);
3152 DrawCompleteVideoDisplay();
3155 InitGameControlValues();
3157 /* don't play tapes over network */
3158 network_playing = (options.network && !tape.playing);
3160 for (i = 0; i < MAX_PLAYERS; i++)
3162 struct PlayerInfo *player = &stored_player[i];
3164 player->index_nr = i;
3165 player->index_bit = (1 << i);
3166 player->element_nr = EL_PLAYER_1 + i;
3168 player->present = FALSE;
3169 player->active = FALSE;
3170 player->mapped = FALSE;
3172 player->killed = FALSE;
3173 player->reanimated = FALSE;
3176 player->effective_action = 0;
3177 player->programmed_action = 0;
3180 player->score_final = 0;
3182 player->gems_still_needed = level.gems_needed;
3183 player->sokobanfields_still_needed = 0;
3184 player->lights_still_needed = 0;
3185 player->friends_still_needed = 0;
3187 for (j = 0; j < MAX_NUM_KEYS; j++)
3188 player->key[j] = FALSE;
3190 player->num_white_keys = 0;
3192 player->dynabomb_count = 0;
3193 player->dynabomb_size = 1;
3194 player->dynabombs_left = 0;
3195 player->dynabomb_xl = FALSE;
3197 player->MovDir = initial_move_dir;
3200 player->GfxDir = initial_move_dir;
3201 player->GfxAction = ACTION_DEFAULT;
3203 player->StepFrame = 0;
3205 player->initial_element = player->element_nr;
3206 player->artwork_element =
3207 (level.use_artwork_element[i] ? level.artwork_element[i] :
3208 player->element_nr);
3209 player->use_murphy = FALSE;
3211 player->block_last_field = FALSE; /* initialized in InitPlayerField() */
3212 player->block_delay_adjustment = 0; /* initialized in InitPlayerField() */
3214 player->gravity = level.initial_player_gravity[i];
3216 player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
3218 player->actual_frame_counter = 0;
3220 player->step_counter = 0;
3222 player->last_move_dir = initial_move_dir;
3224 player->is_active = FALSE;
3226 player->is_waiting = FALSE;
3227 player->is_moving = FALSE;
3228 player->is_auto_moving = FALSE;
3229 player->is_digging = FALSE;
3230 player->is_snapping = FALSE;
3231 player->is_collecting = FALSE;
3232 player->is_pushing = FALSE;
3233 player->is_switching = FALSE;
3234 player->is_dropping = FALSE;
3235 player->is_dropping_pressed = FALSE;
3237 player->is_bored = FALSE;
3238 player->is_sleeping = FALSE;
3240 player->was_waiting = TRUE;
3241 player->was_moving = FALSE;
3242 player->was_snapping = FALSE;
3243 player->was_dropping = FALSE;
3245 player->force_dropping = FALSE;
3247 player->frame_counter_bored = -1;
3248 player->frame_counter_sleeping = -1;
3250 player->anim_delay_counter = 0;
3251 player->post_delay_counter = 0;
3253 player->dir_waiting = initial_move_dir;
3254 player->action_waiting = ACTION_DEFAULT;
3255 player->last_action_waiting = ACTION_DEFAULT;
3256 player->special_action_bored = ACTION_DEFAULT;
3257 player->special_action_sleeping = ACTION_DEFAULT;
3259 player->switch_x = -1;
3260 player->switch_y = -1;
3262 player->drop_x = -1;
3263 player->drop_y = -1;
3265 player->show_envelope = 0;
3267 SetPlayerMoveSpeed(player, level.initial_player_stepsize[i], TRUE);
3269 player->push_delay = -1; /* initialized when pushing starts */
3270 player->push_delay_value = game.initial_push_delay_value;
3272 player->drop_delay = 0;
3273 player->drop_pressed_delay = 0;
3275 player->last_jx = -1;
3276 player->last_jy = -1;
3280 player->shield_normal_time_left = 0;
3281 player->shield_deadly_time_left = 0;
3283 player->inventory_infinite_element = EL_UNDEFINED;
3284 player->inventory_size = 0;
3286 if (level.use_initial_inventory[i])
3288 for (j = 0; j < level.initial_inventory_size[i]; j++)
3290 int element = level.initial_inventory_content[i][j];
3291 int collect_count = element_info[element].collect_count_initial;
3294 if (!IS_CUSTOM_ELEMENT(element))
3297 if (collect_count == 0)
3298 player->inventory_infinite_element = element;
3300 for (k = 0; k < collect_count; k++)
3301 if (player->inventory_size < MAX_INVENTORY_SIZE)
3302 player->inventory_element[player->inventory_size++] = element;
3306 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
3307 SnapField(player, 0, 0);
3309 player->LevelSolved = FALSE;
3310 player->GameOver = FALSE;
3312 player->LevelSolved_GameWon = FALSE;
3313 player->LevelSolved_GameEnd = FALSE;
3314 player->LevelSolved_PanelOff = FALSE;
3315 player->LevelSolved_SaveTape = FALSE;
3316 player->LevelSolved_SaveScore = FALSE;
3317 player->LevelSolved_CountingTime = 0;
3318 player->LevelSolved_CountingScore = 0;
3320 map_player_action[i] = i;
3323 network_player_action_received = FALSE;
3325 #if defined(NETWORK_AVALIABLE)
3326 /* initial null action */
3327 if (network_playing)
3328 SendToServer_MovePlayer(MV_NONE);
3337 TimeLeft = level.time;
3340 ScreenMovDir = MV_NONE;
3344 ScrollStepSize = 0; /* will be correctly initialized by ScrollScreen() */
3346 AllPlayersGone = FALSE;
3348 game.no_time_limit = (level.time == 0);
3350 game.yamyam_content_nr = 0;
3351 game.robot_wheel_active = FALSE;
3352 game.magic_wall_active = FALSE;
3353 game.magic_wall_time_left = 0;
3354 game.light_time_left = 0;
3355 game.timegate_time_left = 0;
3356 game.switchgate_pos = 0;
3357 game.wind_direction = level.wind_direction_initial;
3359 game.lenses_time_left = 0;
3360 game.magnify_time_left = 0;
3362 game.ball_state = level.ball_state_initial;
3363 game.ball_content_nr = 0;
3365 game.envelope_active = FALSE;
3367 /* set focus to local player for network games, else to all players */
3368 game.centered_player_nr = (network_playing ? local_player->index_nr : -1);
3369 game.centered_player_nr_next = game.centered_player_nr;
3370 game.set_centered_player = FALSE;
3372 if (network_playing && tape.recording)
3374 /* store client dependent player focus when recording network games */
3375 tape.centered_player_nr_next = game.centered_player_nr_next;
3376 tape.set_centered_player = TRUE;
3379 for (i = 0; i < NUM_BELTS; i++)
3381 game.belt_dir[i] = MV_NONE;
3382 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
3385 for (i = 0; i < MAX_NUM_AMOEBA; i++)
3386 AmoebaCnt[i] = AmoebaCnt2[i] = 0;
3388 #if DEBUG_INIT_PLAYER
3391 printf("Player status at level initialization:\n");
3395 SCAN_PLAYFIELD(x, y)
3397 Feld[x][y] = level.field[x][y];
3398 MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
3399 ChangeDelay[x][y] = 0;
3400 ChangePage[x][y] = -1;
3401 CustomValue[x][y] = 0; /* initialized in InitField() */
3402 Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
3404 WasJustMoving[x][y] = 0;
3405 WasJustFalling[x][y] = 0;
3406 CheckCollision[x][y] = 0;
3407 CheckImpact[x][y] = 0;
3409 Pushed[x][y] = FALSE;
3411 ChangeCount[x][y] = 0;
3412 ChangeEvent[x][y] = -1;
3414 ExplodePhase[x][y] = 0;
3415 ExplodeDelay[x][y] = 0;
3416 ExplodeField[x][y] = EX_TYPE_NONE;
3418 RunnerVisit[x][y] = 0;
3419 PlayerVisit[x][y] = 0;
3422 GfxRandom[x][y] = INIT_GFX_RANDOM();
3423 GfxElement[x][y] = EL_UNDEFINED;
3424 GfxAction[x][y] = ACTION_DEFAULT;
3425 GfxDir[x][y] = MV_NONE;
3426 GfxRedraw[x][y] = GFX_REDRAW_NONE;
3429 SCAN_PLAYFIELD(x, y)
3431 if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
3433 if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
3435 if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
3438 InitField(x, y, TRUE);
3440 ResetGfxAnimation(x, y);
3445 for (i = 0; i < MAX_PLAYERS; i++)
3447 struct PlayerInfo *player = &stored_player[i];
3449 /* set number of special actions for bored and sleeping animation */
3450 player->num_special_action_bored =
3451 get_num_special_action(player->artwork_element,
3452 ACTION_BORING_1, ACTION_BORING_LAST);
3453 player->num_special_action_sleeping =
3454 get_num_special_action(player->artwork_element,
3455 ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
3458 game.emulation = (emulate_bd ? EMU_BOULDERDASH :
3459 emulate_sb ? EMU_SOKOBAN :
3460 emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
3462 /* initialize type of slippery elements */
3463 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3465 if (!IS_CUSTOM_ELEMENT(i))
3467 /* default: elements slip down either to the left or right randomly */
3468 element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
3470 /* SP style elements prefer to slip down on the left side */
3471 if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
3472 element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3474 /* BD style elements prefer to slip down on the left side */
3475 if (game.emulation == EMU_BOULDERDASH)
3476 element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3480 /* initialize explosion and ignition delay */
3481 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3483 if (!IS_CUSTOM_ELEMENT(i))
3486 int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
3487 game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
3488 game.emulation == EMU_SUPAPLEX ? 3 : 2);
3489 int last_phase = (num_phase + 1) * delay;
3490 int half_phase = (num_phase / 2) * delay;
3492 element_info[i].explosion_delay = last_phase - 1;
3493 element_info[i].ignition_delay = half_phase;
3495 if (i == EL_BLACK_ORB)
3496 element_info[i].ignition_delay = 1;
3500 /* correct non-moving belts to start moving left */
3501 for (i = 0; i < NUM_BELTS; i++)
3502 if (game.belt_dir[i] == MV_NONE)
3503 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
3505 #if USE_NEW_PLAYER_ASSIGNMENTS
3506 /* !!! SAME AS init.c:InitPlayerInfo() -- FIX THIS !!! */
3507 /* choose default local player */
3508 local_player = &stored_player[0];
3510 for (i = 0; i < MAX_PLAYERS; i++)
3511 stored_player[i].connected = FALSE;
3513 local_player->connected = TRUE;
3514 /* !!! SAME AS init.c:InitPlayerInfo() -- FIX THIS !!! */
3518 for (i = 0; i < MAX_PLAYERS; i++)
3519 stored_player[i].connected = tape.player_participates[i];
3521 else if (game.team_mode && !options.network)
3523 /* try to guess locally connected team mode players (needed for correct
3524 assignment of player figures from level to locally playing players) */
3526 for (i = 0; i < MAX_PLAYERS; i++)
3527 if (setup.input[i].use_joystick ||
3528 setup.input[i].key.left != KSYM_UNDEFINED)
3529 stored_player[i].connected = TRUE;
3532 #if DEBUG_INIT_PLAYER
3535 printf("Player status after level initialization:\n");
3537 for (i = 0; i < MAX_PLAYERS; i++)
3539 struct PlayerInfo *player = &stored_player[i];
3541 printf("- player %d: present == %d, connected == %d, active == %d",
3547 if (local_player == player)
3548 printf(" (local player)");
3555 #if DEBUG_INIT_PLAYER
3557 printf("Reassigning players ...\n");
3560 /* check if any connected player was not found in playfield */
3561 for (i = 0; i < MAX_PLAYERS; i++)
3563 struct PlayerInfo *player = &stored_player[i];
3565 if (player->connected && !player->present)
3567 struct PlayerInfo *field_player = NULL;
3569 #if DEBUG_INIT_PLAYER
3571 printf("- looking for field player for player %d ...\n", i + 1);
3574 /* assign first free player found that is present in the playfield */
3576 /* first try: look for unmapped playfield player that is not connected */
3577 for (j = 0; j < MAX_PLAYERS; j++)
3578 if (field_player == NULL &&
3579 stored_player[j].present &&
3580 !stored_player[j].mapped &&
3581 !stored_player[j].connected)
3582 field_player = &stored_player[j];
3584 /* second try: look for *any* unmapped playfield player */
3585 for (j = 0; j < MAX_PLAYERS; j++)
3586 if (field_player == NULL &&
3587 stored_player[j].present &&
3588 !stored_player[j].mapped)
3589 field_player = &stored_player[j];
3591 if (field_player != NULL)
3593 int jx = field_player->jx, jy = field_player->jy;
3595 #if DEBUG_INIT_PLAYER
3597 printf("- found player %d\n", field_player->index_nr + 1);
3600 player->present = FALSE;
3601 player->active = FALSE;
3603 field_player->present = TRUE;
3604 field_player->active = TRUE;
3607 player->initial_element = field_player->initial_element;
3608 player->artwork_element = field_player->artwork_element;
3610 player->block_last_field = field_player->block_last_field;
3611 player->block_delay_adjustment = field_player->block_delay_adjustment;
3614 StorePlayer[jx][jy] = field_player->element_nr;
3616 field_player->jx = field_player->last_jx = jx;
3617 field_player->jy = field_player->last_jy = jy;
3619 if (local_player == player)
3620 local_player = field_player;
3622 map_player_action[field_player->index_nr] = i;
3624 field_player->mapped = TRUE;
3626 #if DEBUG_INIT_PLAYER
3628 printf("- map_player_action[%d] == %d\n",
3629 field_player->index_nr + 1, i + 1);
3634 if (player->connected && player->present)
3635 player->mapped = TRUE;
3638 #if DEBUG_INIT_PLAYER
3641 printf("Player status after player assignment (first stage):\n");
3643 for (i = 0; i < MAX_PLAYERS; i++)
3645 struct PlayerInfo *player = &stored_player[i];
3647 printf("- player %d: present == %d, connected == %d, active == %d",
3653 if (local_player == player)
3654 printf(" (local player)");
3663 /* check if any connected player was not found in playfield */
3664 for (i = 0; i < MAX_PLAYERS; i++)
3666 struct PlayerInfo *player = &stored_player[i];
3668 if (player->connected && !player->present)
3670 for (j = 0; j < MAX_PLAYERS; j++)
3672 struct PlayerInfo *field_player = &stored_player[j];
3673 int jx = field_player->jx, jy = field_player->jy;
3675 /* assign first free player found that is present in the playfield */
3676 if (field_player->present && !field_player->connected)
3678 player->present = TRUE;
3679 player->active = TRUE;
3681 field_player->present = FALSE;
3682 field_player->active = FALSE;
3684 player->initial_element = field_player->initial_element;
3685 player->artwork_element = field_player->artwork_element;
3687 player->block_last_field = field_player->block_last_field;
3688 player->block_delay_adjustment = field_player->block_delay_adjustment;
3690 StorePlayer[jx][jy] = player->element_nr;
3692 player->jx = player->last_jx = jx;
3693 player->jy = player->last_jy = jy;
3703 printf("::: local_player->present == %d\n", local_player->present);
3708 /* when playing a tape, eliminate all players who do not participate */
3710 #if USE_NEW_PLAYER_ASSIGNMENTS
3712 if (!game.team_mode)
3714 for (i = 0; i < MAX_PLAYERS; i++)
3716 if (stored_player[i].active &&
3717 !tape.player_participates[map_player_action[i]])
3719 struct PlayerInfo *player = &stored_player[i];
3720 int jx = player->jx, jy = player->jy;
3722 #if DEBUG_INIT_PLAYER
3724 printf("Removing player %d at (%d, %d)\n", i + 1, jx, jy);
3727 player->active = FALSE;
3728 StorePlayer[jx][jy] = 0;
3729 Feld[jx][jy] = EL_EMPTY;
3736 for (i = 0; i < MAX_PLAYERS; i++)
3738 if (stored_player[i].active &&
3739 !tape.player_participates[i])
3741 struct PlayerInfo *player = &stored_player[i];
3742 int jx = player->jx, jy = player->jy;
3744 player->active = FALSE;
3745 StorePlayer[jx][jy] = 0;
3746 Feld[jx][jy] = EL_EMPTY;
3751 else if (!options.network && !game.team_mode) /* && !tape.playing */
3753 /* when in single player mode, eliminate all but the first active player */
3755 for (i = 0; i < MAX_PLAYERS; i++)
3757 if (stored_player[i].active)
3759 for (j = i + 1; j < MAX_PLAYERS; j++)
3761 if (stored_player[j].active)
3763 struct PlayerInfo *player = &stored_player[j];
3764 int jx = player->jx, jy = player->jy;
3766 player->active = FALSE;
3767 player->present = FALSE;
3769 StorePlayer[jx][jy] = 0;
3770 Feld[jx][jy] = EL_EMPTY;
3777 /* when recording the game, store which players take part in the game */
3780 #if USE_NEW_PLAYER_ASSIGNMENTS
3781 for (i = 0; i < MAX_PLAYERS; i++)
3782 if (stored_player[i].connected)
3783 tape.player_participates[i] = TRUE;
3785 for (i = 0; i < MAX_PLAYERS; i++)
3786 if (stored_player[i].active)
3787 tape.player_participates[i] = TRUE;
3791 #if DEBUG_INIT_PLAYER
3794 printf("Player status after player assignment (final stage):\n");
3796 for (i = 0; i < MAX_PLAYERS; i++)
3798 struct PlayerInfo *player = &stored_player[i];
3800 printf("- player %d: present == %d, connected == %d, active == %d",
3806 if (local_player == player)
3807 printf(" (local player)");
3814 if (BorderElement == EL_EMPTY)
3817 SBX_Right = lev_fieldx - SCR_FIELDX;
3819 SBY_Lower = lev_fieldy - SCR_FIELDY;
3824 SBX_Right = lev_fieldx - SCR_FIELDX + 1;
3826 SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
3829 if (full_lev_fieldx <= SCR_FIELDX)
3830 SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
3831 if (full_lev_fieldy <= SCR_FIELDY)
3832 SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
3834 if (EVEN(SCR_FIELDX) && full_lev_fieldx > SCR_FIELDX)
3836 if (EVEN(SCR_FIELDY) && full_lev_fieldy > SCR_FIELDY)
3839 /* if local player not found, look for custom element that might create
3840 the player (make some assumptions about the right custom element) */
3841 if (!local_player->present)
3843 int start_x = 0, start_y = 0;
3844 int found_rating = 0;
3845 int found_element = EL_UNDEFINED;
3846 int player_nr = local_player->index_nr;
3848 SCAN_PLAYFIELD(x, y)
3850 int element = Feld[x][y];
3855 if (level.use_start_element[player_nr] &&
3856 level.start_element[player_nr] == element &&
3863 found_element = element;
3866 if (!IS_CUSTOM_ELEMENT(element))
3869 if (CAN_CHANGE(element))
3871 for (i = 0; i < element_info[element].num_change_pages; i++)
3873 /* check for player created from custom element as single target */
3874 content = element_info[element].change_page[i].target_element;
3875 is_player = ELEM_IS_PLAYER(content);
3877 if (is_player && (found_rating < 3 ||
3878 (found_rating == 3 && element < found_element)))
3884 found_element = element;
3889 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
3891 /* check for player created from custom element as explosion content */
3892 content = element_info[element].content.e[xx][yy];
3893 is_player = ELEM_IS_PLAYER(content);
3895 if (is_player && (found_rating < 2 ||
3896 (found_rating == 2 && element < found_element)))
3898 start_x = x + xx - 1;
3899 start_y = y + yy - 1;
3902 found_element = element;
3905 if (!CAN_CHANGE(element))
3908 for (i = 0; i < element_info[element].num_change_pages; i++)
3910 /* check for player created from custom element as extended target */
3912 element_info[element].change_page[i].target_content.e[xx][yy];
3914 is_player = ELEM_IS_PLAYER(content);
3916 if (is_player && (found_rating < 1 ||
3917 (found_rating == 1 && element < found_element)))
3919 start_x = x + xx - 1;
3920 start_y = y + yy - 1;
3923 found_element = element;
3929 scroll_x = SCROLL_POSITION_X(start_x);
3930 scroll_y = SCROLL_POSITION_Y(start_y);
3934 scroll_x = SCROLL_POSITION_X(local_player->jx);
3935 scroll_y = SCROLL_POSITION_Y(local_player->jy);
3938 /* !!! FIX THIS (START) !!! */
3939 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
3941 InitGameEngine_EM();
3943 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
3945 InitGameEngine_SP();
3947 else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
3949 InitGameEngine_MM();
3953 DrawLevel(REDRAW_FIELD);
3956 /* after drawing the level, correct some elements */
3957 if (game.timegate_time_left == 0)
3958 CloseAllOpenTimegates();
3961 /* blit playfield from scroll buffer to normal back buffer for fading in */
3962 BlitScreenToBitmap(backbuffer);
3963 /* !!! FIX THIS (END) !!! */
3965 DrawMaskedBorder(fade_mask);
3969 if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
3970 InitGameEngine_MM_AfterFadingIn();
3973 // full screen redraw is required at this point in the following cases:
3974 // - special editor door undrawn when game was started from level editor
3975 // - drawing area (playfield) was changed and has to be removed completely
3976 redraw_mask = REDRAW_ALL;
3980 if (!game.restart_level)
3982 /* copy default game door content to main double buffer */
3984 /* !!! CHECK AGAIN !!! */
3985 SetPanelBackground();
3986 // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
3987 DrawBackground(DX, DY, DXSIZE, DYSIZE);
3990 SetPanelBackground();
3991 SetDrawBackgroundMask(REDRAW_DOOR_1);
3993 UpdateAndDisplayGameControlValues();
3995 if (!game.restart_level)
4001 CreateGameButtons();
4003 game_gadget[SOUND_CTRL_ID_MUSIC]->checked = setup.sound_music;
4004 game_gadget[SOUND_CTRL_ID_LOOPS]->checked = setup.sound_loops;
4005 game_gadget[SOUND_CTRL_ID_SIMPLE]->checked = setup.sound_simple;
4010 /* copy actual game door content to door double buffer for OpenDoor() */
4011 BlitBitmap(drawto, bitmap_db_door_1, DX, DY, DXSIZE, DYSIZE, 0, 0);
4013 OpenDoor(DOOR_OPEN_ALL);
4015 PlaySound(SND_GAME_STARTING);
4017 if (setup.sound_music)
4020 KeyboardAutoRepeatOffUnlessAutoplay();
4022 #if DEBUG_INIT_PLAYER
4025 printf("Player status (final):\n");
4027 for (i = 0; i < MAX_PLAYERS; i++)
4029 struct PlayerInfo *player = &stored_player[i];
4031 printf("- player %d: present == %d, connected == %d, active == %d",
4037 if (local_player == player)
4038 printf(" (local player)");
4051 if (!game.restart_level && !tape.playing)
4053 LevelStats_incPlayed(level_nr);
4055 SaveLevelSetup_SeriesInfo();
4058 game.restart_level = FALSE;
4060 SaveEngineSnapshotToListInitial();
4063 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y,
4064 int actual_player_x, int actual_player_y)
4066 /* this is used for non-R'n'D game engines to update certain engine values */
4068 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
4070 actual_player_x = correctLevelPosX_EM(actual_player_x);
4071 actual_player_y = correctLevelPosY_EM(actual_player_y);
4074 /* needed to determine if sounds are played within the visible screen area */
4075 scroll_x = actual_scroll_x;
4076 scroll_y = actual_scroll_y;
4078 /* needed to get player position for "follow finger" playing input method */
4079 local_player->jx = actual_player_x;
4080 local_player->jy = actual_player_y;
4083 void InitMovDir(int x, int y)
4085 int i, element = Feld[x][y];
4086 static int xy[4][2] =
4093 static int direction[3][4] =
4095 { MV_RIGHT, MV_UP, MV_LEFT, MV_DOWN },
4096 { MV_LEFT, MV_DOWN, MV_RIGHT, MV_UP },
4097 { MV_LEFT, MV_RIGHT, MV_UP, MV_DOWN }
4106 Feld[x][y] = EL_BUG;
4107 MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
4110 case EL_SPACESHIP_RIGHT:
4111 case EL_SPACESHIP_UP:
4112 case EL_SPACESHIP_LEFT:
4113 case EL_SPACESHIP_DOWN:
4114 Feld[x][y] = EL_SPACESHIP;
4115 MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
4118 case EL_BD_BUTTERFLY_RIGHT:
4119 case EL_BD_BUTTERFLY_UP:
4120 case EL_BD_BUTTERFLY_LEFT:
4121 case EL_BD_BUTTERFLY_DOWN:
4122 Feld[x][y] = EL_BD_BUTTERFLY;
4123 MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
4126 case EL_BD_FIREFLY_RIGHT:
4127 case EL_BD_FIREFLY_UP:
4128 case EL_BD_FIREFLY_LEFT:
4129 case EL_BD_FIREFLY_DOWN:
4130 Feld[x][y] = EL_BD_FIREFLY;
4131 MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
4134 case EL_PACMAN_RIGHT:
4136 case EL_PACMAN_LEFT:
4137 case EL_PACMAN_DOWN:
4138 Feld[x][y] = EL_PACMAN;
4139 MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
4142 case EL_YAMYAM_LEFT:
4143 case EL_YAMYAM_RIGHT:
4145 case EL_YAMYAM_DOWN:
4146 Feld[x][y] = EL_YAMYAM;
4147 MovDir[x][y] = direction[2][element - EL_YAMYAM_LEFT];
4150 case EL_SP_SNIKSNAK:
4151 MovDir[x][y] = MV_UP;
4154 case EL_SP_ELECTRON:
4155 MovDir[x][y] = MV_LEFT;
4162 Feld[x][y] = EL_MOLE;
4163 MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
4167 if (IS_CUSTOM_ELEMENT(element))
4169 struct ElementInfo *ei = &element_info[element];
4170 int move_direction_initial = ei->move_direction_initial;
4171 int move_pattern = ei->move_pattern;
4173 if (move_direction_initial == MV_START_PREVIOUS)
4175 if (MovDir[x][y] != MV_NONE)
4178 move_direction_initial = MV_START_AUTOMATIC;
4181 if (move_direction_initial == MV_START_RANDOM)
4182 MovDir[x][y] = 1 << RND(4);
4183 else if (move_direction_initial & MV_ANY_DIRECTION)
4184 MovDir[x][y] = move_direction_initial;
4185 else if (move_pattern == MV_ALL_DIRECTIONS ||
4186 move_pattern == MV_TURNING_LEFT ||
4187 move_pattern == MV_TURNING_RIGHT ||
4188 move_pattern == MV_TURNING_LEFT_RIGHT ||
4189 move_pattern == MV_TURNING_RIGHT_LEFT ||
4190 move_pattern == MV_TURNING_RANDOM)
4191 MovDir[x][y] = 1 << RND(4);
4192 else if (move_pattern == MV_HORIZONTAL)
4193 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
4194 else if (move_pattern == MV_VERTICAL)
4195 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
4196 else if (move_pattern & MV_ANY_DIRECTION)
4197 MovDir[x][y] = element_info[element].move_pattern;
4198 else if (move_pattern == MV_ALONG_LEFT_SIDE ||
4199 move_pattern == MV_ALONG_RIGHT_SIDE)
4201 /* use random direction as default start direction */
4202 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
4203 MovDir[x][y] = 1 << RND(4);
4205 for (i = 0; i < NUM_DIRECTIONS; i++)
4207 int x1 = x + xy[i][0];
4208 int y1 = y + xy[i][1];
4210 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4212 if (move_pattern == MV_ALONG_RIGHT_SIDE)
4213 MovDir[x][y] = direction[0][i];
4215 MovDir[x][y] = direction[1][i];
4224 MovDir[x][y] = 1 << RND(4);
4226 if (element != EL_BUG &&
4227 element != EL_SPACESHIP &&
4228 element != EL_BD_BUTTERFLY &&
4229 element != EL_BD_FIREFLY)
4232 for (i = 0; i < NUM_DIRECTIONS; i++)
4234 int x1 = x + xy[i][0];
4235 int y1 = y + xy[i][1];
4237 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4239 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
4241 MovDir[x][y] = direction[0][i];
4244 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
4245 element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4247 MovDir[x][y] = direction[1][i];
4256 GfxDir[x][y] = MovDir[x][y];
4259 void InitAmoebaNr(int x, int y)
4262 int group_nr = AmoebeNachbarNr(x, y);
4266 for (i = 1; i < MAX_NUM_AMOEBA; i++)
4268 if (AmoebaCnt[i] == 0)
4276 AmoebaNr[x][y] = group_nr;
4277 AmoebaCnt[group_nr]++;
4278 AmoebaCnt2[group_nr]++;
4281 static void PlayerWins(struct PlayerInfo *player)
4283 player->LevelSolved = TRUE;
4284 player->GameOver = TRUE;
4286 player->score_final = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
4287 level.native_em_level->lev->score :
4288 level.game_engine_type == GAME_ENGINE_TYPE_MM ?
4292 player->LevelSolved_CountingTime = (game.no_time_limit ? TimePlayed :
4294 player->LevelSolved_CountingScore = player->score_final;
4299 static int time, time_final;
4300 static int score, score_final;
4301 static int game_over_delay_1 = 0;
4302 static int game_over_delay_2 = 0;
4303 int game_over_delay_value_1 = 50;
4304 int game_over_delay_value_2 = 50;
4306 if (!local_player->LevelSolved_GameWon)
4310 /* do not start end game actions before the player stops moving (to exit) */
4311 if (local_player->MovPos)
4314 local_player->LevelSolved_GameWon = TRUE;
4315 local_player->LevelSolved_SaveTape = tape.recording;
4316 local_player->LevelSolved_SaveScore = !tape.playing;
4320 LevelStats_incSolved(level_nr);
4322 SaveLevelSetup_SeriesInfo();
4325 if (tape.auto_play) /* tape might already be stopped here */
4326 tape.auto_play_level_solved = TRUE;
4330 game_over_delay_1 = game_over_delay_value_1;
4331 game_over_delay_2 = game_over_delay_value_2;
4333 time = time_final = (game.no_time_limit ? TimePlayed : TimeLeft);
4334 score = score_final = local_player->score_final;
4339 score_final += TimeLeft * level.score[SC_TIME_BONUS];
4341 else if (game.no_time_limit && TimePlayed < 999)
4344 score_final += (999 - TimePlayed) * level.score[SC_TIME_BONUS];
4347 local_player->score_final = score_final;
4349 if (level_editor_test_game)
4352 score = score_final;
4354 local_player->LevelSolved_CountingTime = time;
4355 local_player->LevelSolved_CountingScore = score;
4357 game_panel_controls[GAME_PANEL_TIME].value = time;
4358 game_panel_controls[GAME_PANEL_SCORE].value = score;
4360 DisplayGameControlValues();
4363 if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
4365 if (ExitX >= 0 && ExitY >= 0) /* local player has left the level */
4367 /* close exit door after last player */
4368 if ((AllPlayersGone &&
4369 (Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
4370 Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN ||
4371 Feld[ExitX][ExitY] == EL_STEEL_EXIT_OPEN)) ||
4372 Feld[ExitX][ExitY] == EL_EM_EXIT_OPEN ||
4373 Feld[ExitX][ExitY] == EL_EM_STEEL_EXIT_OPEN)
4375 int element = Feld[ExitX][ExitY];
4377 Feld[ExitX][ExitY] =
4378 (element == EL_EXIT_OPEN ? EL_EXIT_CLOSING :
4379 element == EL_EM_EXIT_OPEN ? EL_EM_EXIT_CLOSING :
4380 element == EL_SP_EXIT_OPEN ? EL_SP_EXIT_CLOSING:
4381 element == EL_STEEL_EXIT_OPEN ? EL_STEEL_EXIT_CLOSING:
4382 EL_EM_STEEL_EXIT_CLOSING);
4384 PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING);
4387 /* player disappears */
4388 DrawLevelField(ExitX, ExitY);
4391 for (i = 0; i < MAX_PLAYERS; i++)
4393 struct PlayerInfo *player = &stored_player[i];
4395 if (player->present)
4397 RemovePlayer(player);
4399 /* player disappears */
4400 DrawLevelField(player->jx, player->jy);
4405 PlaySound(SND_GAME_WINNING);
4408 if (game_over_delay_1 > 0)
4410 game_over_delay_1--;
4415 if (time != time_final)
4417 int time_to_go = ABS(time_final - time);
4418 int time_count_dir = (time < time_final ? +1 : -1);
4419 int time_count_steps = (time_to_go > 100 && time_to_go % 10 == 0 ? 10 : 1);
4421 time += time_count_steps * time_count_dir;
4422 score += time_count_steps * level.score[SC_TIME_BONUS];
4424 local_player->LevelSolved_CountingTime = time;
4425 local_player->LevelSolved_CountingScore = score;
4427 game_panel_controls[GAME_PANEL_TIME].value = time;
4428 game_panel_controls[GAME_PANEL_SCORE].value = score;
4430 DisplayGameControlValues();
4432 if (time == time_final)
4433 StopSound(SND_GAME_LEVELTIME_BONUS);
4434 else if (setup.sound_loops)
4435 PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4437 PlaySound(SND_GAME_LEVELTIME_BONUS);
4442 local_player->LevelSolved_PanelOff = TRUE;
4444 if (game_over_delay_2 > 0)
4446 game_over_delay_2--;
4457 boolean raise_level = FALSE;
4459 local_player->LevelSolved_GameEnd = TRUE;
4461 if (!global.use_envelope_request)
4462 CloseDoor(DOOR_CLOSE_1);
4464 if (local_player->LevelSolved_SaveTape)
4466 SaveTapeChecked(tape.level_nr); /* ask to save tape */
4469 CloseDoor(DOOR_CLOSE_ALL);
4471 if (level_editor_test_game)
4473 SetGameStatus(GAME_MODE_MAIN);
4480 if (!local_player->LevelSolved_SaveScore)
4482 SetGameStatus(GAME_MODE_MAIN);
4489 if (level_nr == leveldir_current->handicap_level)
4491 leveldir_current->handicap_level++;
4493 SaveLevelSetup_SeriesInfo();
4496 if (setup.increment_levels &&
4497 level_nr < leveldir_current->last_level)
4498 raise_level = TRUE; /* advance to next level */
4500 if ((hi_pos = NewHiScore()) >= 0)
4502 SetGameStatus(GAME_MODE_SCORES);
4504 DrawHallOfFame(hi_pos);
4514 SetGameStatus(GAME_MODE_MAIN);
4530 boolean one_score_entry_per_name = !program.many_scores_per_name;
4532 LoadScore(level_nr);
4534 if (strEqual(setup.player_name, EMPTY_PLAYER_NAME) ||
4535 local_player->score_final < highscore[MAX_SCORE_ENTRIES - 1].Score)
4538 for (k = 0; k < MAX_SCORE_ENTRIES; k++)
4540 if (local_player->score_final > highscore[k].Score)
4542 /* player has made it to the hall of fame */
4544 if (k < MAX_SCORE_ENTRIES - 1)
4546 int m = MAX_SCORE_ENTRIES - 1;
4548 if (one_score_entry_per_name)
4550 for (l = k; l < MAX_SCORE_ENTRIES; l++)
4551 if (strEqual(setup.player_name, highscore[l].Name))
4554 if (m == k) /* player's new highscore overwrites his old one */
4558 for (l = m; l > k; l--)
4560 strcpy(highscore[l].Name, highscore[l - 1].Name);
4561 highscore[l].Score = highscore[l - 1].Score;
4567 strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
4568 highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
4569 highscore[k].Score = local_player->score_final;
4574 else if (one_score_entry_per_name &&
4575 !strncmp(setup.player_name, highscore[k].Name,
4576 MAX_PLAYER_NAME_LEN))
4577 break; /* player already there with a higher score */
4581 SaveScore(level_nr);
4586 inline static int getElementMoveStepsizeExt(int x, int y, int direction)
4588 int element = Feld[x][y];
4589 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
4590 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
4591 int horiz_move = (dx != 0);
4592 int sign = (horiz_move ? dx : dy);
4593 int step = sign * element_info[element].move_stepsize;
4595 /* special values for move stepsize for spring and things on conveyor belt */
4598 if (CAN_FALL(element) &&
4599 y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
4600 step = sign * MOVE_STEPSIZE_NORMAL / 2;
4601 else if (element == EL_SPRING)
4602 step = sign * MOVE_STEPSIZE_NORMAL * 2;
4608 inline static int getElementMoveStepsize(int x, int y)
4610 return getElementMoveStepsizeExt(x, y, MovDir[x][y]);
4613 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
4615 if (player->GfxAction != action || player->GfxDir != dir)
4617 player->GfxAction = action;
4618 player->GfxDir = dir;
4620 player->StepFrame = 0;
4624 static void ResetGfxFrame(int x, int y)
4626 // profiling showed that "autotest" spends 10~20% of its time in this function
4627 if (DrawingDeactivatedField())
4630 int element = Feld[x][y];
4631 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
4633 if (graphic_info[graphic].anim_global_sync)
4634 GfxFrame[x][y] = FrameCounter;
4635 else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
4636 GfxFrame[x][y] = CustomValue[x][y];
4637 else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
4638 GfxFrame[x][y] = element_info[element].collect_score;
4639 else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
4640 GfxFrame[x][y] = ChangeDelay[x][y];
4643 static void ResetGfxAnimation(int x, int y)
4645 GfxAction[x][y] = ACTION_DEFAULT;
4646 GfxDir[x][y] = MovDir[x][y];
4649 ResetGfxFrame(x, y);
4652 static void ResetRandomAnimationValue(int x, int y)
4654 GfxRandom[x][y] = INIT_GFX_RANDOM();
4657 void InitMovingField(int x, int y, int direction)
4659 int element = Feld[x][y];
4660 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
4661 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
4664 boolean is_moving_before, is_moving_after;
4666 /* check if element was/is moving or being moved before/after mode change */
4667 is_moving_before = (WasJustMoving[x][y] != 0);
4668 is_moving_after = (getElementMoveStepsizeExt(x, y, direction) != 0);
4670 /* reset animation only for moving elements which change direction of moving
4671 or which just started or stopped moving
4672 (else CEs with property "can move" / "not moving" are reset each frame) */
4673 if (is_moving_before != is_moving_after ||
4674 direction != MovDir[x][y])
4675 ResetGfxAnimation(x, y);
4677 MovDir[x][y] = direction;
4678 GfxDir[x][y] = direction;
4680 GfxAction[x][y] = (!is_moving_after ? ACTION_WAITING :
4681 direction == MV_DOWN && CAN_FALL(element) ?
4682 ACTION_FALLING : ACTION_MOVING);
4684 /* this is needed for CEs with property "can move" / "not moving" */
4686 if (is_moving_after)
4688 if (Feld[newx][newy] == EL_EMPTY)
4689 Feld[newx][newy] = EL_BLOCKED;
4691 MovDir[newx][newy] = MovDir[x][y];
4693 CustomValue[newx][newy] = CustomValue[x][y];
4695 GfxFrame[newx][newy] = GfxFrame[x][y];
4696 GfxRandom[newx][newy] = GfxRandom[x][y];
4697 GfxAction[newx][newy] = GfxAction[x][y];
4698 GfxDir[newx][newy] = GfxDir[x][y];
4702 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
4704 int direction = MovDir[x][y];
4705 int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0);
4706 int newy = y + (direction & MV_UP ? -1 : direction & MV_DOWN ? +1 : 0);
4712 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
4714 int oldx = x, oldy = y;
4715 int direction = MovDir[x][y];
4717 if (direction == MV_LEFT)
4719 else if (direction == MV_RIGHT)
4721 else if (direction == MV_UP)
4723 else if (direction == MV_DOWN)
4726 *comes_from_x = oldx;
4727 *comes_from_y = oldy;
4730 int MovingOrBlocked2Element(int x, int y)
4732 int element = Feld[x][y];
4734 if (element == EL_BLOCKED)
4738 Blocked2Moving(x, y, &oldx, &oldy);
4739 return Feld[oldx][oldy];
4745 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
4747 /* like MovingOrBlocked2Element(), but if element is moving
4748 and (x,y) is the field the moving element is just leaving,
4749 return EL_BLOCKED instead of the element value */
4750 int element = Feld[x][y];
4752 if (IS_MOVING(x, y))
4754 if (element == EL_BLOCKED)
4758 Blocked2Moving(x, y, &oldx, &oldy);
4759 return Feld[oldx][oldy];
4768 static void RemoveField(int x, int y)
4770 Feld[x][y] = EL_EMPTY;
4776 CustomValue[x][y] = 0;
4779 ChangeDelay[x][y] = 0;
4780 ChangePage[x][y] = -1;
4781 Pushed[x][y] = FALSE;
4783 GfxElement[x][y] = EL_UNDEFINED;
4784 GfxAction[x][y] = ACTION_DEFAULT;
4785 GfxDir[x][y] = MV_NONE;
4788 void RemoveMovingField(int x, int y)
4790 int oldx = x, oldy = y, newx = x, newy = y;
4791 int element = Feld[x][y];
4792 int next_element = EL_UNDEFINED;
4794 if (element != EL_BLOCKED && !IS_MOVING(x, y))
4797 if (IS_MOVING(x, y))
4799 Moving2Blocked(x, y, &newx, &newy);
4801 if (Feld[newx][newy] != EL_BLOCKED)
4803 /* element is moving, but target field is not free (blocked), but
4804 already occupied by something different (example: acid pool);
4805 in this case, only remove the moving field, but not the target */
4807 RemoveField(oldx, oldy);
4809 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
4811 TEST_DrawLevelField(oldx, oldy);
4816 else if (element == EL_BLOCKED)
4818 Blocked2Moving(x, y, &oldx, &oldy);
4819 if (!IS_MOVING(oldx, oldy))
4823 if (element == EL_BLOCKED &&
4824 (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
4825 Feld[oldx][oldy] == EL_QUICKSAND_FAST_EMPTYING ||
4826 Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
4827 Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
4828 Feld[oldx][oldy] == EL_DC_MAGIC_WALL_EMPTYING ||
4829 Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
4830 next_element = get_next_element(Feld[oldx][oldy]);
4832 RemoveField(oldx, oldy);
4833 RemoveField(newx, newy);
4835 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
4837 if (next_element != EL_UNDEFINED)
4838 Feld[oldx][oldy] = next_element;
4840 TEST_DrawLevelField(oldx, oldy);
4841 TEST_DrawLevelField(newx, newy);
4844 void DrawDynamite(int x, int y)
4846 int sx = SCREENX(x), sy = SCREENY(y);
4847 int graphic = el2img(Feld[x][y]);
4850 if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
4853 if (IS_WALKABLE_INSIDE(Back[x][y]))
4857 DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
4858 else if (Store[x][y])
4859 DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
4861 frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
4863 if (Back[x][y] || Store[x][y])
4864 DrawGraphicThruMask(sx, sy, graphic, frame);
4866 DrawGraphic(sx, sy, graphic, frame);
4869 void CheckDynamite(int x, int y)
4871 if (MovDelay[x][y] != 0) /* dynamite is still waiting to explode */
4875 if (MovDelay[x][y] != 0)
4878 PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
4884 StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
4889 static void setMinimalPlayerBoundaries(int *sx1, int *sy1, int *sx2, int *sy2)
4891 boolean num_checked_players = 0;
4894 for (i = 0; i < MAX_PLAYERS; i++)
4896 if (stored_player[i].active)
4898 int sx = stored_player[i].jx;
4899 int sy = stored_player[i].jy;
4901 if (num_checked_players == 0)
4908 *sx1 = MIN(*sx1, sx);
4909 *sy1 = MIN(*sy1, sy);
4910 *sx2 = MAX(*sx2, sx);
4911 *sy2 = MAX(*sy2, sy);
4914 num_checked_players++;
4919 static boolean checkIfAllPlayersFitToScreen_RND()
4921 int sx1 = 0, sy1 = 0, sx2 = 0, sy2 = 0;
4923 setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
4925 return (sx2 - sx1 < SCR_FIELDX &&
4926 sy2 - sy1 < SCR_FIELDY);
4929 static void setScreenCenteredToAllPlayers(int *sx, int *sy)
4931 int sx1 = scroll_x, sy1 = scroll_y, sx2 = scroll_x, sy2 = scroll_y;
4933 setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
4935 *sx = (sx1 + sx2) / 2;
4936 *sy = (sy1 + sy2) / 2;
4939 void DrawRelocateScreen(int old_x, int old_y, int x, int y, int move_dir,
4940 boolean center_screen, boolean quick_relocation)
4942 unsigned int frame_delay_value_old = GetVideoFrameDelay();
4943 boolean ffwd_delay = (tape.playing && tape.fast_forward);
4944 boolean no_delay = (tape.warp_forward);
4945 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
4946 int wait_delay_value = (no_delay ? 0 : frame_delay_value);
4947 int new_scroll_x, new_scroll_y;
4949 if (level.lazy_relocation && IN_VIS_FIELD(SCREENX(x), SCREENY(y)))
4951 /* case 1: quick relocation inside visible screen (without scrolling) */
4958 if (!level.shifted_relocation || center_screen)
4960 /* relocation _with_ centering of screen */
4962 new_scroll_x = SCROLL_POSITION_X(x);
4963 new_scroll_y = SCROLL_POSITION_Y(y);
4967 /* relocation _without_ centering of screen */
4969 int center_scroll_x = SCROLL_POSITION_X(old_x);
4970 int center_scroll_y = SCROLL_POSITION_Y(old_y);
4971 int offset_x = x + (scroll_x - center_scroll_x);
4972 int offset_y = y + (scroll_y - center_scroll_y);
4974 /* for new screen position, apply previous offset to center position */
4975 new_scroll_x = SCROLL_POSITION_X(offset_x);
4976 new_scroll_y = SCROLL_POSITION_Y(offset_y);
4979 if (quick_relocation)
4981 /* case 2: quick relocation (redraw without visible scrolling) */
4983 scroll_x = new_scroll_x;
4984 scroll_y = new_scroll_y;
4991 /* case 3: visible relocation (with scrolling to new position) */
4993 ScrollScreen(NULL, SCROLL_GO_ON); /* scroll last frame to full tile */
4995 SetVideoFrameDelay(wait_delay_value);
4997 while (scroll_x != new_scroll_x || scroll_y != new_scroll_y)
5000 int fx = FX, fy = FY;
5002 dx = (new_scroll_x < scroll_x ? +1 : new_scroll_x > scroll_x ? -1 : 0);
5003 dy = (new_scroll_y < scroll_y ? +1 : new_scroll_y > scroll_y ? -1 : 0);
5005 if (dx == 0 && dy == 0) /* no scrolling needed at all */
5011 fx += dx * TILEX / 2;
5012 fy += dy * TILEY / 2;
5014 ScrollLevel(dx, dy);
5017 /* scroll in two steps of half tile size to make things smoother */
5018 BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
5020 /* scroll second step to align at full tile size */
5021 BlitScreenToBitmap(window);
5027 SetVideoFrameDelay(frame_delay_value_old);
5030 void RelocatePlayer(int jx, int jy, int el_player_raw)
5032 int el_player = GET_PLAYER_ELEMENT(el_player_raw);
5033 int player_nr = GET_PLAYER_NR(el_player);
5034 struct PlayerInfo *player = &stored_player[player_nr];
5035 boolean ffwd_delay = (tape.playing && tape.fast_forward);
5036 boolean no_delay = (tape.warp_forward);
5037 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5038 int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5039 int old_jx = player->jx;
5040 int old_jy = player->jy;
5041 int old_element = Feld[old_jx][old_jy];
5042 int element = Feld[jx][jy];
5043 boolean player_relocated = (old_jx != jx || old_jy != jy);
5045 int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
5046 int move_dir_vert = (jy < old_jy ? MV_UP : jy > old_jy ? MV_DOWN : 0);
5047 int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
5048 int enter_side_vert = MV_DIR_OPPOSITE(move_dir_vert);
5049 int leave_side_horiz = move_dir_horiz;
5050 int leave_side_vert = move_dir_vert;
5051 int enter_side = enter_side_horiz | enter_side_vert;
5052 int leave_side = leave_side_horiz | leave_side_vert;
5054 if (player->GameOver) /* do not reanimate dead player */
5057 if (!player_relocated) /* no need to relocate the player */
5060 if (IS_PLAYER(jx, jy)) /* player already placed at new position */
5062 RemoveField(jx, jy); /* temporarily remove newly placed player */
5063 DrawLevelField(jx, jy);
5066 if (player->present)
5068 while (player->MovPos)
5070 ScrollPlayer(player, SCROLL_GO_ON);
5071 ScrollScreen(NULL, SCROLL_GO_ON);
5073 AdvanceFrameAndPlayerCounters(player->index_nr);
5077 BackToFront_WithFrameDelay(wait_delay_value);
5080 DrawPlayer(player); /* needed here only to cleanup last field */
5081 DrawLevelField(player->jx, player->jy); /* remove player graphic */
5083 player->is_moving = FALSE;
5086 if (IS_CUSTOM_ELEMENT(old_element))
5087 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
5089 player->index_bit, leave_side);
5091 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
5093 player->index_bit, leave_side);
5095 Feld[jx][jy] = el_player;
5096 InitPlayerField(jx, jy, el_player, TRUE);
5098 /* "InitPlayerField()" above sets Feld[jx][jy] to EL_EMPTY, but it may be
5099 possible that the relocation target field did not contain a player element,
5100 but a walkable element, to which the new player was relocated -- in this
5101 case, restore that (already initialized!) element on the player field */
5102 if (!ELEM_IS_PLAYER(element)) /* player may be set on walkable element */
5104 Feld[jx][jy] = element; /* restore previously existing element */
5107 /* only visually relocate centered player */
5108 DrawRelocateScreen(old_jx, old_jy, player->jx, player->jy, player->MovDir,
5109 FALSE, level.instant_relocation);
5111 TestIfPlayerTouchesBadThing(jx, jy);
5112 TestIfPlayerTouchesCustomElement(jx, jy);
5114 if (IS_CUSTOM_ELEMENT(element))
5115 CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
5116 player->index_bit, enter_side);
5118 CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
5119 player->index_bit, enter_side);
5121 if (player->is_switching)
5123 /* ensure that relocation while still switching an element does not cause
5124 a new element to be treated as also switched directly after relocation
5125 (this is important for teleporter switches that teleport the player to
5126 a place where another teleporter switch is in the same direction, which
5127 would then incorrectly be treated as immediately switched before the
5128 direction key that caused the switch was released) */
5130 player->switch_x += jx - old_jx;
5131 player->switch_y += jy - old_jy;
5135 void Explode(int ex, int ey, int phase, int mode)
5141 /* !!! eliminate this variable !!! */
5142 int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
5144 if (game.explosions_delayed)
5146 ExplodeField[ex][ey] = mode;
5150 if (phase == EX_PHASE_START) /* initialize 'Store[][]' field */
5152 int center_element = Feld[ex][ey];
5153 int artwork_element, explosion_element; /* set these values later */
5155 /* remove things displayed in background while burning dynamite */
5156 if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
5159 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
5161 /* put moving element to center field (and let it explode there) */
5162 center_element = MovingOrBlocked2Element(ex, ey);
5163 RemoveMovingField(ex, ey);
5164 Feld[ex][ey] = center_element;
5167 /* now "center_element" is finally determined -- set related values now */
5168 artwork_element = center_element; /* for custom player artwork */
5169 explosion_element = center_element; /* for custom player artwork */
5171 if (IS_PLAYER(ex, ey))
5173 int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
5175 artwork_element = stored_player[player_nr].artwork_element;
5177 if (level.use_explosion_element[player_nr])
5179 explosion_element = level.explosion_element[player_nr];
5180 artwork_element = explosion_element;
5184 if (mode == EX_TYPE_NORMAL ||
5185 mode == EX_TYPE_CENTER ||
5186 mode == EX_TYPE_CROSS)
5187 PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
5189 last_phase = element_info[explosion_element].explosion_delay + 1;
5191 for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
5193 int xx = x - ex + 1;
5194 int yy = y - ey + 1;
5197 if (!IN_LEV_FIELD(x, y) ||
5198 (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
5199 (mode == EX_TYPE_CROSS && (x != ex && y != ey)))
5202 element = Feld[x][y];
5204 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
5206 element = MovingOrBlocked2Element(x, y);
5208 if (!IS_EXPLOSION_PROOF(element))
5209 RemoveMovingField(x, y);
5212 /* indestructible elements can only explode in center (but not flames) */
5213 if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
5214 mode == EX_TYPE_BORDER)) ||
5215 element == EL_FLAMES)
5218 /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
5219 behaviour, for example when touching a yamyam that explodes to rocks
5220 with active deadly shield, a rock is created under the player !!! */
5221 /* (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8) */
5223 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
5224 (game.engine_version < VERSION_IDENT(3,1,0,0) ||
5225 (x == ex && y == ey && mode != EX_TYPE_BORDER)))
5227 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
5230 if (IS_ACTIVE_BOMB(element))
5232 /* re-activate things under the bomb like gate or penguin */
5233 Feld[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
5240 /* save walkable background elements while explosion on same tile */
5241 if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
5242 (x != ex || y != ey || mode == EX_TYPE_BORDER))
5243 Back[x][y] = element;
5245 /* ignite explodable elements reached by other explosion */
5246 if (element == EL_EXPLOSION)
5247 element = Store2[x][y];
5249 if (AmoebaNr[x][y] &&
5250 (element == EL_AMOEBA_FULL ||
5251 element == EL_BD_AMOEBA ||
5252 element == EL_AMOEBA_GROWING))
5254 AmoebaCnt[AmoebaNr[x][y]]--;
5255 AmoebaCnt2[AmoebaNr[x][y]]--;
5260 if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
5262 int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
5264 Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
5266 if (PLAYERINFO(ex, ey)->use_murphy)
5267 Store[x][y] = EL_EMPTY;
5270 /* !!! check this case -- currently needed for rnd_rado_negundo_v,
5271 !!! levels 015 018 019 020 021 022 023 026 027 028 !!! */
5272 else if (ELEM_IS_PLAYER(center_element))
5273 Store[x][y] = EL_EMPTY;
5274 else if (center_element == EL_YAMYAM)
5275 Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
5276 else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
5277 Store[x][y] = element_info[center_element].content.e[xx][yy];
5279 /* needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
5280 (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
5281 otherwise) -- FIX THIS !!! */
5282 else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
5283 Store[x][y] = element_info[element].content.e[1][1];
5285 else if (!CAN_EXPLODE(element))
5286 Store[x][y] = element_info[element].content.e[1][1];
5289 Store[x][y] = EL_EMPTY;
5291 if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
5292 center_element == EL_AMOEBA_TO_DIAMOND)
5293 Store2[x][y] = element;
5295 Feld[x][y] = EL_EXPLOSION;
5296 GfxElement[x][y] = artwork_element;
5298 ExplodePhase[x][y] = 1;
5299 ExplodeDelay[x][y] = last_phase;
5304 if (center_element == EL_YAMYAM)
5305 game.yamyam_content_nr =
5306 (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
5318 GfxFrame[x][y] = 0; /* restart explosion animation */
5320 last_phase = ExplodeDelay[x][y];
5322 ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
5324 /* this can happen if the player leaves an explosion just in time */
5325 if (GfxElement[x][y] == EL_UNDEFINED)
5326 GfxElement[x][y] = EL_EMPTY;
5328 border_element = Store2[x][y];
5329 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5330 border_element = StorePlayer[x][y];
5332 if (phase == element_info[border_element].ignition_delay ||
5333 phase == last_phase)
5335 boolean border_explosion = FALSE;
5337 if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
5338 !PLAYER_EXPLOSION_PROTECTED(x, y))
5340 KillPlayerUnlessExplosionProtected(x, y);
5341 border_explosion = TRUE;
5343 else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
5345 Feld[x][y] = Store2[x][y];
5348 border_explosion = TRUE;
5350 else if (border_element == EL_AMOEBA_TO_DIAMOND)
5352 AmoebeUmwandeln(x, y);
5354 border_explosion = TRUE;
5357 /* if an element just explodes due to another explosion (chain-reaction),
5358 do not immediately end the new explosion when it was the last frame of
5359 the explosion (as it would be done in the following "if"-statement!) */
5360 if (border_explosion && phase == last_phase)
5364 if (phase == last_phase)
5368 element = Feld[x][y] = Store[x][y];
5369 Store[x][y] = Store2[x][y] = 0;
5370 GfxElement[x][y] = EL_UNDEFINED;
5372 /* player can escape from explosions and might therefore be still alive */
5373 if (element >= EL_PLAYER_IS_EXPLODING_1 &&
5374 element <= EL_PLAYER_IS_EXPLODING_4)
5376 int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
5377 int explosion_element = EL_PLAYER_1 + player_nr;
5378 int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
5379 int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
5381 if (level.use_explosion_element[player_nr])
5382 explosion_element = level.explosion_element[player_nr];
5384 Feld[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
5385 element_info[explosion_element].content.e[xx][yy]);
5388 /* restore probably existing indestructible background element */
5389 if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
5390 element = Feld[x][y] = Back[x][y];
5393 MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
5394 GfxDir[x][y] = MV_NONE;
5395 ChangeDelay[x][y] = 0;
5396 ChangePage[x][y] = -1;
5398 CustomValue[x][y] = 0;
5400 InitField_WithBug2(x, y, FALSE);
5402 TEST_DrawLevelField(x, y);
5404 TestIfElementTouchesCustomElement(x, y);
5406 if (GFX_CRUMBLED(element))
5407 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
5409 if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
5410 StorePlayer[x][y] = 0;
5412 if (ELEM_IS_PLAYER(element))
5413 RelocatePlayer(x, y, element);
5415 else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
5417 int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
5418 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5421 TEST_DrawLevelFieldCrumbled(x, y);
5423 if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
5425 DrawLevelElement(x, y, Back[x][y]);
5426 DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
5428 else if (IS_WALKABLE_UNDER(Back[x][y]))
5430 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5431 DrawLevelElementThruMask(x, y, Back[x][y]);
5433 else if (!IS_WALKABLE_INSIDE(Back[x][y]))
5434 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5438 void DynaExplode(int ex, int ey)
5441 int dynabomb_element = Feld[ex][ey];
5442 int dynabomb_size = 1;
5443 boolean dynabomb_xl = FALSE;
5444 struct PlayerInfo *player;
5445 static int xy[4][2] =
5453 if (IS_ACTIVE_BOMB(dynabomb_element))
5455 player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
5456 dynabomb_size = player->dynabomb_size;
5457 dynabomb_xl = player->dynabomb_xl;
5458 player->dynabombs_left++;
5461 Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
5463 for (i = 0; i < NUM_DIRECTIONS; i++)
5465 for (j = 1; j <= dynabomb_size; j++)
5467 int x = ex + j * xy[i][0];
5468 int y = ey + j * xy[i][1];
5471 if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
5474 element = Feld[x][y];
5476 /* do not restart explosions of fields with active bombs */
5477 if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
5480 Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
5482 if (element != EL_EMPTY && element != EL_EXPLOSION &&
5483 !IS_DIGGABLE(element) && !dynabomb_xl)
5489 void Bang(int x, int y)
5491 int element = MovingOrBlocked2Element(x, y);
5492 int explosion_type = EX_TYPE_NORMAL;
5494 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5496 struct PlayerInfo *player = PLAYERINFO(x, y);
5498 element = Feld[x][y] = player->initial_element;
5500 if (level.use_explosion_element[player->index_nr])
5502 int explosion_element = level.explosion_element[player->index_nr];
5504 if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
5505 explosion_type = EX_TYPE_CROSS;
5506 else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
5507 explosion_type = EX_TYPE_CENTER;
5515 case EL_BD_BUTTERFLY:
5518 case EL_DARK_YAMYAM:
5522 RaiseScoreElement(element);
5525 case EL_DYNABOMB_PLAYER_1_ACTIVE:
5526 case EL_DYNABOMB_PLAYER_2_ACTIVE:
5527 case EL_DYNABOMB_PLAYER_3_ACTIVE:
5528 case EL_DYNABOMB_PLAYER_4_ACTIVE:
5529 case EL_DYNABOMB_INCREASE_NUMBER:
5530 case EL_DYNABOMB_INCREASE_SIZE:
5531 case EL_DYNABOMB_INCREASE_POWER:
5532 explosion_type = EX_TYPE_DYNA;
5535 case EL_DC_LANDMINE:
5536 explosion_type = EX_TYPE_CENTER;
5541 case EL_LAMP_ACTIVE:
5542 case EL_AMOEBA_TO_DIAMOND:
5543 if (!IS_PLAYER(x, y)) /* penguin and player may be at same field */
5544 explosion_type = EX_TYPE_CENTER;
5548 if (element_info[element].explosion_type == EXPLODES_CROSS)
5549 explosion_type = EX_TYPE_CROSS;
5550 else if (element_info[element].explosion_type == EXPLODES_1X1)
5551 explosion_type = EX_TYPE_CENTER;
5555 if (explosion_type == EX_TYPE_DYNA)
5558 Explode(x, y, EX_PHASE_START, explosion_type);
5560 CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
5563 void SplashAcid(int x, int y)
5565 if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
5566 (!IN_LEV_FIELD(x - 1, y - 2) ||
5567 !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
5568 Feld[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
5570 if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
5571 (!IN_LEV_FIELD(x + 1, y - 2) ||
5572 !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
5573 Feld[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
5575 PlayLevelSound(x, y, SND_ACID_SPLASHING);
5578 static void InitBeltMovement()
5580 static int belt_base_element[4] =
5582 EL_CONVEYOR_BELT_1_LEFT,
5583 EL_CONVEYOR_BELT_2_LEFT,
5584 EL_CONVEYOR_BELT_3_LEFT,
5585 EL_CONVEYOR_BELT_4_LEFT
5587 static int belt_base_active_element[4] =
5589 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
5590 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
5591 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
5592 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
5597 /* set frame order for belt animation graphic according to belt direction */
5598 for (i = 0; i < NUM_BELTS; i++)
5602 for (j = 0; j < NUM_BELT_PARTS; j++)
5604 int element = belt_base_active_element[belt_nr] + j;
5605 int graphic_1 = el2img(element);
5606 int graphic_2 = el2panelimg(element);
5608 if (game.belt_dir[i] == MV_LEFT)
5610 graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
5611 graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
5615 graphic_info[graphic_1].anim_mode |= ANIM_REVERSE;
5616 graphic_info[graphic_2].anim_mode |= ANIM_REVERSE;
5621 SCAN_PLAYFIELD(x, y)
5623 int element = Feld[x][y];
5625 for (i = 0; i < NUM_BELTS; i++)
5627 if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
5629 int e_belt_nr = getBeltNrFromBeltElement(element);
5632 if (e_belt_nr == belt_nr)
5634 int belt_part = Feld[x][y] - belt_base_element[belt_nr];
5636 Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
5643 static void ToggleBeltSwitch(int x, int y)
5645 static int belt_base_element[4] =
5647 EL_CONVEYOR_BELT_1_LEFT,
5648 EL_CONVEYOR_BELT_2_LEFT,
5649 EL_CONVEYOR_BELT_3_LEFT,
5650 EL_CONVEYOR_BELT_4_LEFT
5652 static int belt_base_active_element[4] =
5654 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
5655 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
5656 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
5657 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
5659 static int belt_base_switch_element[4] =
5661 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
5662 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
5663 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
5664 EL_CONVEYOR_BELT_4_SWITCH_LEFT
5666 static int belt_move_dir[4] =
5674 int element = Feld[x][y];
5675 int belt_nr = getBeltNrFromBeltSwitchElement(element);
5676 int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
5677 int belt_dir = belt_move_dir[belt_dir_nr];
5680 if (!IS_BELT_SWITCH(element))
5683 game.belt_dir_nr[belt_nr] = belt_dir_nr;
5684 game.belt_dir[belt_nr] = belt_dir;
5686 if (belt_dir_nr == 3)
5689 /* set frame order for belt animation graphic according to belt direction */
5690 for (i = 0; i < NUM_BELT_PARTS; i++)
5692 int element = belt_base_active_element[belt_nr] + i;
5693 int graphic_1 = el2img(element);
5694 int graphic_2 = el2panelimg(element);
5696 if (belt_dir == MV_LEFT)
5698 graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
5699 graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
5703 graphic_info[graphic_1].anim_mode |= ANIM_REVERSE;
5704 graphic_info[graphic_2].anim_mode |= ANIM_REVERSE;
5708 SCAN_PLAYFIELD(xx, yy)
5710 int element = Feld[xx][yy];
5712 if (IS_BELT_SWITCH(element))
5714 int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
5716 if (e_belt_nr == belt_nr)
5718 Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
5719 TEST_DrawLevelField(xx, yy);
5722 else if (IS_BELT(element) && belt_dir != MV_NONE)
5724 int e_belt_nr = getBeltNrFromBeltElement(element);
5726 if (e_belt_nr == belt_nr)
5728 int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
5730 Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
5731 TEST_DrawLevelField(xx, yy);
5734 else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
5736 int e_belt_nr = getBeltNrFromBeltActiveElement(element);
5738 if (e_belt_nr == belt_nr)
5740 int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
5742 Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
5743 TEST_DrawLevelField(xx, yy);
5749 static void ToggleSwitchgateSwitch(int x, int y)
5753 game.switchgate_pos = !game.switchgate_pos;
5755 SCAN_PLAYFIELD(xx, yy)
5757 int element = Feld[xx][yy];
5759 if (element == EL_SWITCHGATE_SWITCH_UP)
5761 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_DOWN;
5762 TEST_DrawLevelField(xx, yy);
5764 else if (element == EL_SWITCHGATE_SWITCH_DOWN)
5766 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP;
5767 TEST_DrawLevelField(xx, yy);
5769 else if (element == EL_DC_SWITCHGATE_SWITCH_UP)
5771 Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_DOWN;
5772 TEST_DrawLevelField(xx, yy);
5774 else if (element == EL_DC_SWITCHGATE_SWITCH_DOWN)
5776 Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP;
5777 TEST_DrawLevelField(xx, yy);
5779 else if (element == EL_SWITCHGATE_OPEN ||
5780 element == EL_SWITCHGATE_OPENING)
5782 Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
5784 PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
5786 else if (element == EL_SWITCHGATE_CLOSED ||
5787 element == EL_SWITCHGATE_CLOSING)
5789 Feld[xx][yy] = EL_SWITCHGATE_OPENING;
5791 PlayLevelSoundAction(xx, yy, ACTION_OPENING);
5796 static int getInvisibleActiveFromInvisibleElement(int element)
5798 return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
5799 element == EL_INVISIBLE_WALL ? EL_INVISIBLE_WALL_ACTIVE :
5800 element == EL_INVISIBLE_SAND ? EL_INVISIBLE_SAND_ACTIVE :
5804 static int getInvisibleFromInvisibleActiveElement(int element)
5806 return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
5807 element == EL_INVISIBLE_WALL_ACTIVE ? EL_INVISIBLE_WALL :
5808 element == EL_INVISIBLE_SAND_ACTIVE ? EL_INVISIBLE_SAND :
5812 static void RedrawAllLightSwitchesAndInvisibleElements()
5816 SCAN_PLAYFIELD(x, y)
5818 int element = Feld[x][y];
5820 if (element == EL_LIGHT_SWITCH &&
5821 game.light_time_left > 0)
5823 Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
5824 TEST_DrawLevelField(x, y);
5826 else if (element == EL_LIGHT_SWITCH_ACTIVE &&
5827 game.light_time_left == 0)
5829 Feld[x][y] = EL_LIGHT_SWITCH;
5830 TEST_DrawLevelField(x, y);
5832 else if (element == EL_EMC_DRIPPER &&
5833 game.light_time_left > 0)
5835 Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
5836 TEST_DrawLevelField(x, y);
5838 else if (element == EL_EMC_DRIPPER_ACTIVE &&
5839 game.light_time_left == 0)
5841 Feld[x][y] = EL_EMC_DRIPPER;
5842 TEST_DrawLevelField(x, y);
5844 else if (element == EL_INVISIBLE_STEELWALL ||
5845 element == EL_INVISIBLE_WALL ||
5846 element == EL_INVISIBLE_SAND)
5848 if (game.light_time_left > 0)
5849 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
5851 TEST_DrawLevelField(x, y);
5853 /* uncrumble neighbour fields, if needed */
5854 if (element == EL_INVISIBLE_SAND)
5855 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
5857 else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
5858 element == EL_INVISIBLE_WALL_ACTIVE ||
5859 element == EL_INVISIBLE_SAND_ACTIVE)
5861 if (game.light_time_left == 0)
5862 Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
5864 TEST_DrawLevelField(x, y);
5866 /* re-crumble neighbour fields, if needed */
5867 if (element == EL_INVISIBLE_SAND)
5868 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
5873 static void RedrawAllInvisibleElementsForLenses()
5877 SCAN_PLAYFIELD(x, y)
5879 int element = Feld[x][y];
5881 if (element == EL_EMC_DRIPPER &&
5882 game.lenses_time_left > 0)
5884 Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
5885 TEST_DrawLevelField(x, y);
5887 else if (element == EL_EMC_DRIPPER_ACTIVE &&
5888 game.lenses_time_left == 0)
5890 Feld[x][y] = EL_EMC_DRIPPER;
5891 TEST_DrawLevelField(x, y);
5893 else if (element == EL_INVISIBLE_STEELWALL ||
5894 element == EL_INVISIBLE_WALL ||
5895 element == EL_INVISIBLE_SAND)
5897 if (game.lenses_time_left > 0)
5898 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
5900 TEST_DrawLevelField(x, y);
5902 /* uncrumble neighbour fields, if needed */
5903 if (element == EL_INVISIBLE_SAND)
5904 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
5906 else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
5907 element == EL_INVISIBLE_WALL_ACTIVE ||
5908 element == EL_INVISIBLE_SAND_ACTIVE)
5910 if (game.lenses_time_left == 0)
5911 Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
5913 TEST_DrawLevelField(x, y);
5915 /* re-crumble neighbour fields, if needed */
5916 if (element == EL_INVISIBLE_SAND)
5917 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
5922 static void RedrawAllInvisibleElementsForMagnifier()
5926 SCAN_PLAYFIELD(x, y)
5928 int element = Feld[x][y];
5930 if (element == EL_EMC_FAKE_GRASS &&
5931 game.magnify_time_left > 0)
5933 Feld[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
5934 TEST_DrawLevelField(x, y);
5936 else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
5937 game.magnify_time_left == 0)
5939 Feld[x][y] = EL_EMC_FAKE_GRASS;
5940 TEST_DrawLevelField(x, y);
5942 else if (IS_GATE_GRAY(element) &&
5943 game.magnify_time_left > 0)
5945 Feld[x][y] = (IS_RND_GATE_GRAY(element) ?
5946 element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
5947 IS_EM_GATE_GRAY(element) ?
5948 element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
5949 IS_EMC_GATE_GRAY(element) ?
5950 element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
5951 IS_DC_GATE_GRAY(element) ?
5952 EL_DC_GATE_WHITE_GRAY_ACTIVE :
5954 TEST_DrawLevelField(x, y);
5956 else if (IS_GATE_GRAY_ACTIVE(element) &&
5957 game.magnify_time_left == 0)
5959 Feld[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
5960 element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
5961 IS_EM_GATE_GRAY_ACTIVE(element) ?
5962 element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
5963 IS_EMC_GATE_GRAY_ACTIVE(element) ?
5964 element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
5965 IS_DC_GATE_GRAY_ACTIVE(element) ?
5966 EL_DC_GATE_WHITE_GRAY :
5968 TEST_DrawLevelField(x, y);
5973 static void ToggleLightSwitch(int x, int y)
5975 int element = Feld[x][y];
5977 game.light_time_left =
5978 (element == EL_LIGHT_SWITCH ?
5979 level.time_light * FRAMES_PER_SECOND : 0);
5981 RedrawAllLightSwitchesAndInvisibleElements();
5984 static void ActivateTimegateSwitch(int x, int y)
5988 game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
5990 SCAN_PLAYFIELD(xx, yy)
5992 int element = Feld[xx][yy];
5994 if (element == EL_TIMEGATE_CLOSED ||
5995 element == EL_TIMEGATE_CLOSING)
5997 Feld[xx][yy] = EL_TIMEGATE_OPENING;
5998 PlayLevelSound(xx, yy, SND_CLASS_TIMEGATE_OPENING);
6002 else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
6004 Feld[xx][yy] = EL_TIMEGATE_SWITCH;
6005 TEST_DrawLevelField(xx, yy);
6011 Feld[x][y] = (Feld[x][y] == EL_TIMEGATE_SWITCH ? EL_TIMEGATE_SWITCH_ACTIVE :
6012 EL_DC_TIMEGATE_SWITCH_ACTIVE);
6015 void Impact(int x, int y)
6017 boolean last_line = (y == lev_fieldy - 1);
6018 boolean object_hit = FALSE;
6019 boolean impact = (last_line || object_hit);
6020 int element = Feld[x][y];
6021 int smashed = EL_STEELWALL;
6023 if (!last_line) /* check if element below was hit */
6025 if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
6028 object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
6029 MovDir[x][y + 1] != MV_DOWN ||
6030 MovPos[x][y + 1] <= TILEY / 2));
6032 /* do not smash moving elements that left the smashed field in time */
6033 if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
6034 ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
6037 #if USE_QUICKSAND_IMPACT_BUGFIX
6038 if (Feld[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
6040 RemoveMovingField(x, y + 1);
6041 Feld[x][y + 1] = EL_QUICKSAND_EMPTY;
6042 Feld[x][y + 2] = EL_ROCK;
6043 TEST_DrawLevelField(x, y + 2);
6048 if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTYING && object_hit == FALSE)
6050 RemoveMovingField(x, y + 1);
6051 Feld[x][y + 1] = EL_QUICKSAND_FAST_EMPTY;
6052 Feld[x][y + 2] = EL_ROCK;
6053 TEST_DrawLevelField(x, y + 2);
6060 smashed = MovingOrBlocked2Element(x, y + 1);
6062 impact = (last_line || object_hit);
6065 if (!last_line && smashed == EL_ACID) /* element falls into acid */
6067 SplashAcid(x, y + 1);
6071 /* !!! not sufficient for all cases -- see EL_PEARL below !!! */
6072 /* only reset graphic animation if graphic really changes after impact */
6074 el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
6076 ResetGfxAnimation(x, y);
6077 TEST_DrawLevelField(x, y);
6080 if (impact && CAN_EXPLODE_IMPACT(element))
6085 else if (impact && element == EL_PEARL &&
6086 smashed != EL_DC_MAGIC_WALL && smashed != EL_DC_MAGIC_WALL_ACTIVE)
6088 ResetGfxAnimation(x, y);
6090 Feld[x][y] = EL_PEARL_BREAKING;
6091 PlayLevelSound(x, y, SND_PEARL_BREAKING);
6094 else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
6096 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6101 if (impact && element == EL_AMOEBA_DROP)
6103 if (object_hit && IS_PLAYER(x, y + 1))
6104 KillPlayerUnlessEnemyProtected(x, y + 1);
6105 else if (object_hit && smashed == EL_PENGUIN)
6109 Feld[x][y] = EL_AMOEBA_GROWING;
6110 Store[x][y] = EL_AMOEBA_WET;
6112 ResetRandomAnimationValue(x, y);
6117 if (object_hit) /* check which object was hit */
6119 if ((CAN_PASS_MAGIC_WALL(element) &&
6120 (smashed == EL_MAGIC_WALL ||
6121 smashed == EL_BD_MAGIC_WALL)) ||
6122 (CAN_PASS_DC_MAGIC_WALL(element) &&
6123 smashed == EL_DC_MAGIC_WALL))
6126 int activated_magic_wall =
6127 (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
6128 smashed == EL_BD_MAGIC_WALL ? EL_BD_MAGIC_WALL_ACTIVE :
6129 EL_DC_MAGIC_WALL_ACTIVE);
6131 /* activate magic wall / mill */
6132 SCAN_PLAYFIELD(xx, yy)
6134 if (Feld[xx][yy] == smashed)
6135 Feld[xx][yy] = activated_magic_wall;
6138 game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
6139 game.magic_wall_active = TRUE;
6141 PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
6142 SND_MAGIC_WALL_ACTIVATING :
6143 smashed == EL_BD_MAGIC_WALL ?
6144 SND_BD_MAGIC_WALL_ACTIVATING :
6145 SND_DC_MAGIC_WALL_ACTIVATING));
6148 if (IS_PLAYER(x, y + 1))
6150 if (CAN_SMASH_PLAYER(element))
6152 KillPlayerUnlessEnemyProtected(x, y + 1);
6156 else if (smashed == EL_PENGUIN)
6158 if (CAN_SMASH_PLAYER(element))
6164 else if (element == EL_BD_DIAMOND)
6166 if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
6172 else if (((element == EL_SP_INFOTRON ||
6173 element == EL_SP_ZONK) &&
6174 (smashed == EL_SP_SNIKSNAK ||
6175 smashed == EL_SP_ELECTRON ||
6176 smashed == EL_SP_DISK_ORANGE)) ||
6177 (element == EL_SP_INFOTRON &&
6178 smashed == EL_SP_DISK_YELLOW))
6183 else if (CAN_SMASH_EVERYTHING(element))
6185 if (IS_CLASSIC_ENEMY(smashed) ||
6186 CAN_EXPLODE_SMASHED(smashed))
6191 else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
6193 if (smashed == EL_LAMP ||
6194 smashed == EL_LAMP_ACTIVE)
6199 else if (smashed == EL_NUT)
6201 Feld[x][y + 1] = EL_NUT_BREAKING;
6202 PlayLevelSound(x, y, SND_NUT_BREAKING);
6203 RaiseScoreElement(EL_NUT);
6206 else if (smashed == EL_PEARL)
6208 ResetGfxAnimation(x, y);
6210 Feld[x][y + 1] = EL_PEARL_BREAKING;
6211 PlayLevelSound(x, y, SND_PEARL_BREAKING);
6214 else if (smashed == EL_DIAMOND)
6216 Feld[x][y + 1] = EL_DIAMOND_BREAKING;
6217 PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
6220 else if (IS_BELT_SWITCH(smashed))
6222 ToggleBeltSwitch(x, y + 1);
6224 else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
6225 smashed == EL_SWITCHGATE_SWITCH_DOWN ||
6226 smashed == EL_DC_SWITCHGATE_SWITCH_UP ||
6227 smashed == EL_DC_SWITCHGATE_SWITCH_DOWN)
6229 ToggleSwitchgateSwitch(x, y + 1);
6231 else if (smashed == EL_LIGHT_SWITCH ||
6232 smashed == EL_LIGHT_SWITCH_ACTIVE)
6234 ToggleLightSwitch(x, y + 1);
6238 CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6240 CheckElementChangeBySide(x, y + 1, smashed, element,
6241 CE_SWITCHED, CH_SIDE_TOP);
6242 CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
6248 CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6253 /* play sound of magic wall / mill */
6255 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
6256 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ||
6257 Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE))
6259 if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
6260 PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
6261 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
6262 PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
6263 else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
6264 PlayLevelSound(x, y, SND_DC_MAGIC_WALL_FILLING);
6269 /* play sound of object that hits the ground */
6270 if (last_line || object_hit)
6271 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6274 inline static void TurnRoundExt(int x, int y)
6286 { 0, 0 }, { 0, 0 }, { 0, 0 },
6291 int left, right, back;
6295 { MV_DOWN, MV_UP, MV_RIGHT },
6296 { MV_UP, MV_DOWN, MV_LEFT },
6298 { MV_LEFT, MV_RIGHT, MV_DOWN },
6302 { MV_RIGHT, MV_LEFT, MV_UP }
6305 int element = Feld[x][y];
6306 int move_pattern = element_info[element].move_pattern;
6308 int old_move_dir = MovDir[x][y];
6309 int left_dir = turn[old_move_dir].left;
6310 int right_dir = turn[old_move_dir].right;
6311 int back_dir = turn[old_move_dir].back;
6313 int left_dx = move_xy[left_dir].dx, left_dy = move_xy[left_dir].dy;
6314 int right_dx = move_xy[right_dir].dx, right_dy = move_xy[right_dir].dy;
6315 int move_dx = move_xy[old_move_dir].dx, move_dy = move_xy[old_move_dir].dy;
6316 int back_dx = move_xy[back_dir].dx, back_dy = move_xy[back_dir].dy;
6318 int left_x = x + left_dx, left_y = y + left_dy;
6319 int right_x = x + right_dx, right_y = y + right_dy;
6320 int move_x = x + move_dx, move_y = y + move_dy;
6324 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
6326 TestIfBadThingTouchesOtherBadThing(x, y);
6328 if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
6329 MovDir[x][y] = right_dir;
6330 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6331 MovDir[x][y] = left_dir;
6333 if (element == EL_BUG && MovDir[x][y] != old_move_dir)
6335 else if (element == EL_BD_BUTTERFLY) /* && MovDir[x][y] == left_dir) */
6338 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
6340 TestIfBadThingTouchesOtherBadThing(x, y);
6342 if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
6343 MovDir[x][y] = left_dir;
6344 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6345 MovDir[x][y] = right_dir;
6347 if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
6349 else if (element == EL_BD_FIREFLY) /* && MovDir[x][y] == right_dir) */
6352 else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
6354 TestIfBadThingTouchesOtherBadThing(x, y);
6356 if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
6357 MovDir[x][y] = left_dir;
6358 else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
6359 MovDir[x][y] = right_dir;
6361 if (MovDir[x][y] != old_move_dir)
6364 else if (element == EL_YAMYAM)
6366 boolean can_turn_left = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
6367 boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
6369 if (can_turn_left && can_turn_right)
6370 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6371 else if (can_turn_left)
6372 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6373 else if (can_turn_right)
6374 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6376 MovDir[x][y] = back_dir;
6378 MovDelay[x][y] = 16 + 16 * RND(3);
6380 else if (element == EL_DARK_YAMYAM)
6382 boolean can_turn_left = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6384 boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6387 if (can_turn_left && can_turn_right)
6388 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6389 else if (can_turn_left)
6390 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6391 else if (can_turn_right)
6392 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6394 MovDir[x][y] = back_dir;
6396 MovDelay[x][y] = 16 + 16 * RND(3);
6398 else if (element == EL_PACMAN)
6400 boolean can_turn_left = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
6401 boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
6403 if (can_turn_left && can_turn_right)
6404 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6405 else if (can_turn_left)
6406 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6407 else if (can_turn_right)
6408 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6410 MovDir[x][y] = back_dir;
6412 MovDelay[x][y] = 6 + RND(40);
6414 else if (element == EL_PIG)
6416 boolean can_turn_left = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
6417 boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
6418 boolean can_move_on = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
6419 boolean should_turn_left, should_turn_right, should_move_on;
6421 int rnd = RND(rnd_value);
6423 should_turn_left = (can_turn_left &&
6425 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
6426 y + back_dy + left_dy)));
6427 should_turn_right = (can_turn_right &&
6429 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
6430 y + back_dy + right_dy)));
6431 should_move_on = (can_move_on &&
6434 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
6435 y + move_dy + left_dy) ||
6436 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
6437 y + move_dy + right_dy)));
6439 if (should_turn_left || should_turn_right || should_move_on)
6441 if (should_turn_left && should_turn_right && should_move_on)
6442 MovDir[x][y] = (rnd < rnd_value / 3 ? left_dir :
6443 rnd < 2 * rnd_value / 3 ? right_dir :
6445 else if (should_turn_left && should_turn_right)
6446 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6447 else if (should_turn_left && should_move_on)
6448 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
6449 else if (should_turn_right && should_move_on)
6450 MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
6451 else if (should_turn_left)
6452 MovDir[x][y] = left_dir;
6453 else if (should_turn_right)
6454 MovDir[x][y] = right_dir;
6455 else if (should_move_on)
6456 MovDir[x][y] = old_move_dir;
6458 else if (can_move_on && rnd > rnd_value / 8)
6459 MovDir[x][y] = old_move_dir;
6460 else if (can_turn_left && can_turn_right)
6461 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6462 else if (can_turn_left && rnd > rnd_value / 8)
6463 MovDir[x][y] = left_dir;
6464 else if (can_turn_right && rnd > rnd_value/8)
6465 MovDir[x][y] = right_dir;
6467 MovDir[x][y] = back_dir;
6469 xx = x + move_xy[MovDir[x][y]].dx;
6470 yy = y + move_xy[MovDir[x][y]].dy;
6472 if (!IN_LEV_FIELD(xx, yy) ||
6473 (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy])))
6474 MovDir[x][y] = old_move_dir;
6478 else if (element == EL_DRAGON)
6480 boolean can_turn_left = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
6481 boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
6482 boolean can_move_on = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
6484 int rnd = RND(rnd_value);
6486 if (can_move_on && rnd > rnd_value / 8)
6487 MovDir[x][y] = old_move_dir;
6488 else if (can_turn_left && can_turn_right)
6489 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6490 else if (can_turn_left && rnd > rnd_value / 8)
6491 MovDir[x][y] = left_dir;
6492 else if (can_turn_right && rnd > rnd_value / 8)
6493 MovDir[x][y] = right_dir;
6495 MovDir[x][y] = back_dir;
6497 xx = x + move_xy[MovDir[x][y]].dx;
6498 yy = y + move_xy[MovDir[x][y]].dy;
6500 if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
6501 MovDir[x][y] = old_move_dir;
6505 else if (element == EL_MOLE)
6507 boolean can_move_on =
6508 (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
6509 IS_AMOEBOID(Feld[move_x][move_y]) ||
6510 Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
6513 boolean can_turn_left =
6514 (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
6515 IS_AMOEBOID(Feld[left_x][left_y])));
6517 boolean can_turn_right =
6518 (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
6519 IS_AMOEBOID(Feld[right_x][right_y])));
6521 if (can_turn_left && can_turn_right)
6522 MovDir[x][y] = (RND(2) ? left_dir : right_dir);
6523 else if (can_turn_left)
6524 MovDir[x][y] = left_dir;
6526 MovDir[x][y] = right_dir;
6529 if (MovDir[x][y] != old_move_dir)
6532 else if (element == EL_BALLOON)
6534 MovDir[x][y] = game.wind_direction;
6537 else if (element == EL_SPRING)
6539 if (MovDir[x][y] & MV_HORIZONTAL)
6541 if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
6542 !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
6544 Feld[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
6545 ResetGfxAnimation(move_x, move_y);
6546 TEST_DrawLevelField(move_x, move_y);
6548 MovDir[x][y] = back_dir;
6550 else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
6551 SPRING_CAN_ENTER_FIELD(element, x, y + 1))
6552 MovDir[x][y] = MV_NONE;
6557 else if (element == EL_ROBOT ||
6558 element == EL_SATELLITE ||
6559 element == EL_PENGUIN ||
6560 element == EL_EMC_ANDROID)
6562 int attr_x = -1, attr_y = -1;
6573 for (i = 0; i < MAX_PLAYERS; i++)
6575 struct PlayerInfo *player = &stored_player[i];
6576 int jx = player->jx, jy = player->jy;
6578 if (!player->active)
6582 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
6590 if (element == EL_ROBOT && ZX >= 0 && ZY >= 0 &&
6591 (Feld[ZX][ZY] == EL_ROBOT_WHEEL_ACTIVE ||
6592 game.engine_version < VERSION_IDENT(3,1,0,0)))
6598 if (element == EL_PENGUIN)
6601 static int xy[4][2] =
6609 for (i = 0; i < NUM_DIRECTIONS; i++)
6611 int ex = x + xy[i][0];
6612 int ey = y + xy[i][1];
6614 if (IN_LEV_FIELD(ex, ey) && (Feld[ex][ey] == EL_EXIT_OPEN ||
6615 Feld[ex][ey] == EL_EM_EXIT_OPEN ||
6616 Feld[ex][ey] == EL_STEEL_EXIT_OPEN ||
6617 Feld[ex][ey] == EL_EM_STEEL_EXIT_OPEN))
6626 MovDir[x][y] = MV_NONE;
6628 MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
6629 else if (attr_x > x)
6630 MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
6632 MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
6633 else if (attr_y > y)
6634 MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
6636 if (element == EL_ROBOT)
6640 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6641 MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
6642 Moving2Blocked(x, y, &newx, &newy);
6644 if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
6645 MovDelay[x][y] = 8 + 8 * !RND(3);
6647 MovDelay[x][y] = 16;
6649 else if (element == EL_PENGUIN)
6655 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6657 boolean first_horiz = RND(2);
6658 int new_move_dir = MovDir[x][y];
6661 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6662 Moving2Blocked(x, y, &newx, &newy);
6664 if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
6668 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6669 Moving2Blocked(x, y, &newx, &newy);
6671 if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
6674 MovDir[x][y] = old_move_dir;
6678 else if (element == EL_SATELLITE)
6684 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6686 boolean first_horiz = RND(2);
6687 int new_move_dir = MovDir[x][y];
6690 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6691 Moving2Blocked(x, y, &newx, &newy);
6693 if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
6697 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6698 Moving2Blocked(x, y, &newx, &newy);
6700 if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
6703 MovDir[x][y] = old_move_dir;
6707 else if (element == EL_EMC_ANDROID)
6709 static int check_pos[16] =
6711 -1, /* 0 => (invalid) */
6712 7, /* 1 => MV_LEFT */
6713 3, /* 2 => MV_RIGHT */
6714 -1, /* 3 => (invalid) */
6716 0, /* 5 => MV_LEFT | MV_UP */
6717 2, /* 6 => MV_RIGHT | MV_UP */
6718 -1, /* 7 => (invalid) */
6719 5, /* 8 => MV_DOWN */
6720 6, /* 9 => MV_LEFT | MV_DOWN */
6721 4, /* 10 => MV_RIGHT | MV_DOWN */
6722 -1, /* 11 => (invalid) */
6723 -1, /* 12 => (invalid) */
6724 -1, /* 13 => (invalid) */
6725 -1, /* 14 => (invalid) */
6726 -1, /* 15 => (invalid) */
6734 { -1, -1, MV_LEFT | MV_UP },
6736 { +1, -1, MV_RIGHT | MV_UP },
6737 { +1, 0, MV_RIGHT },
6738 { +1, +1, MV_RIGHT | MV_DOWN },
6740 { -1, +1, MV_LEFT | MV_DOWN },
6743 int start_pos, check_order;
6744 boolean can_clone = FALSE;
6747 /* check if there is any free field around current position */
6748 for (i = 0; i < 8; i++)
6750 int newx = x + check_xy[i].dx;
6751 int newy = y + check_xy[i].dy;
6753 if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
6761 if (can_clone) /* randomly find an element to clone */
6765 start_pos = check_pos[RND(8)];
6766 check_order = (RND(2) ? -1 : +1);
6768 for (i = 0; i < 8; i++)
6770 int pos_raw = start_pos + i * check_order;
6771 int pos = (pos_raw + 8) % 8;
6772 int newx = x + check_xy[pos].dx;
6773 int newy = y + check_xy[pos].dy;
6775 if (ANDROID_CAN_CLONE_FIELD(newx, newy))
6777 element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
6778 element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
6780 Store[x][y] = Feld[newx][newy];
6789 if (can_clone) /* randomly find a direction to move */
6793 start_pos = check_pos[RND(8)];
6794 check_order = (RND(2) ? -1 : +1);
6796 for (i = 0; i < 8; i++)
6798 int pos_raw = start_pos + i * check_order;
6799 int pos = (pos_raw + 8) % 8;
6800 int newx = x + check_xy[pos].dx;
6801 int newy = y + check_xy[pos].dy;
6802 int new_move_dir = check_xy[pos].dir;
6804 if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
6806 MovDir[x][y] = new_move_dir;
6807 MovDelay[x][y] = level.android_clone_time * 8 + 1;
6816 if (can_clone) /* cloning and moving successful */
6819 /* cannot clone -- try to move towards player */
6821 start_pos = check_pos[MovDir[x][y] & 0x0f];
6822 check_order = (RND(2) ? -1 : +1);
6824 for (i = 0; i < 3; i++)
6826 /* first check start_pos, then previous/next or (next/previous) pos */
6827 int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
6828 int pos = (pos_raw + 8) % 8;
6829 int newx = x + check_xy[pos].dx;
6830 int newy = y + check_xy[pos].dy;
6831 int new_move_dir = check_xy[pos].dir;
6833 if (IS_PLAYER(newx, newy))
6836 if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
6838 MovDir[x][y] = new_move_dir;
6839 MovDelay[x][y] = level.android_move_time * 8 + 1;
6846 else if (move_pattern == MV_TURNING_LEFT ||
6847 move_pattern == MV_TURNING_RIGHT ||
6848 move_pattern == MV_TURNING_LEFT_RIGHT ||
6849 move_pattern == MV_TURNING_RIGHT_LEFT ||
6850 move_pattern == MV_TURNING_RANDOM ||
6851 move_pattern == MV_ALL_DIRECTIONS)
6853 boolean can_turn_left =
6854 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
6855 boolean can_turn_right =
6856 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
6858 if (element_info[element].move_stepsize == 0) /* "not moving" */
6861 if (move_pattern == MV_TURNING_LEFT)
6862 MovDir[x][y] = left_dir;
6863 else if (move_pattern == MV_TURNING_RIGHT)
6864 MovDir[x][y] = right_dir;
6865 else if (move_pattern == MV_TURNING_LEFT_RIGHT)
6866 MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
6867 else if (move_pattern == MV_TURNING_RIGHT_LEFT)
6868 MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
6869 else if (move_pattern == MV_TURNING_RANDOM)
6870 MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
6871 can_turn_right && !can_turn_left ? right_dir :
6872 RND(2) ? left_dir : right_dir);
6873 else if (can_turn_left && can_turn_right)
6874 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6875 else if (can_turn_left)
6876 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6877 else if (can_turn_right)
6878 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6880 MovDir[x][y] = back_dir;
6882 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6884 else if (move_pattern == MV_HORIZONTAL ||
6885 move_pattern == MV_VERTICAL)
6887 if (move_pattern & old_move_dir)
6888 MovDir[x][y] = back_dir;
6889 else if (move_pattern == MV_HORIZONTAL)
6890 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
6891 else if (move_pattern == MV_VERTICAL)
6892 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
6894 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6896 else if (move_pattern & MV_ANY_DIRECTION)
6898 MovDir[x][y] = move_pattern;
6899 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6901 else if (move_pattern & MV_WIND_DIRECTION)
6903 MovDir[x][y] = game.wind_direction;
6904 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6906 else if (move_pattern == MV_ALONG_LEFT_SIDE)
6908 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
6909 MovDir[x][y] = left_dir;
6910 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
6911 MovDir[x][y] = right_dir;
6913 if (MovDir[x][y] != old_move_dir)
6914 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6916 else if (move_pattern == MV_ALONG_RIGHT_SIDE)
6918 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
6919 MovDir[x][y] = right_dir;
6920 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
6921 MovDir[x][y] = left_dir;
6923 if (MovDir[x][y] != old_move_dir)
6924 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6926 else if (move_pattern == MV_TOWARDS_PLAYER ||
6927 move_pattern == MV_AWAY_FROM_PLAYER)
6929 int attr_x = -1, attr_y = -1;
6931 boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
6942 for (i = 0; i < MAX_PLAYERS; i++)
6944 struct PlayerInfo *player = &stored_player[i];
6945 int jx = player->jx, jy = player->jy;
6947 if (!player->active)
6951 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
6959 MovDir[x][y] = MV_NONE;
6961 MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
6962 else if (attr_x > x)
6963 MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
6965 MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
6966 else if (attr_y > y)
6967 MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
6969 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6971 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6973 boolean first_horiz = RND(2);
6974 int new_move_dir = MovDir[x][y];
6976 if (element_info[element].move_stepsize == 0) /* "not moving" */
6978 first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
6979 MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6985 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6986 Moving2Blocked(x, y, &newx, &newy);
6988 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
6992 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6993 Moving2Blocked(x, y, &newx, &newy);
6995 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
6998 MovDir[x][y] = old_move_dir;
7001 else if (move_pattern == MV_WHEN_PUSHED ||
7002 move_pattern == MV_WHEN_DROPPED)
7004 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7005 MovDir[x][y] = MV_NONE;
7009 else if (move_pattern & MV_MAZE_RUNNER_STYLE)
7011 static int test_xy[7][2] =
7021 static int test_dir[7] =
7031 boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
7032 int move_preference = -1000000; /* start with very low preference */
7033 int new_move_dir = MV_NONE;
7034 int start_test = RND(4);
7037 for (i = 0; i < NUM_DIRECTIONS; i++)
7039 int move_dir = test_dir[start_test + i];
7040 int move_dir_preference;
7042 xx = x + test_xy[start_test + i][0];
7043 yy = y + test_xy[start_test + i][1];
7045 if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
7046 (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
7048 new_move_dir = move_dir;
7053 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
7056 move_dir_preference = -1 * RunnerVisit[xx][yy];
7057 if (hunter_mode && PlayerVisit[xx][yy] > 0)
7058 move_dir_preference = PlayerVisit[xx][yy];
7060 if (move_dir_preference > move_preference)
7062 /* prefer field that has not been visited for the longest time */
7063 move_preference = move_dir_preference;
7064 new_move_dir = move_dir;
7066 else if (move_dir_preference == move_preference &&
7067 move_dir == old_move_dir)
7069 /* prefer last direction when all directions are preferred equally */
7070 move_preference = move_dir_preference;
7071 new_move_dir = move_dir;
7075 MovDir[x][y] = new_move_dir;
7076 if (old_move_dir != new_move_dir)
7077 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7081 static void TurnRound(int x, int y)
7083 int direction = MovDir[x][y];
7087 GfxDir[x][y] = MovDir[x][y];
7089 if (direction != MovDir[x][y])
7093 GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
7095 ResetGfxFrame(x, y);
7098 static boolean JustBeingPushed(int x, int y)
7102 for (i = 0; i < MAX_PLAYERS; i++)
7104 struct PlayerInfo *player = &stored_player[i];
7106 if (player->active && player->is_pushing && player->MovPos)
7108 int next_jx = player->jx + (player->jx - player->last_jx);
7109 int next_jy = player->jy + (player->jy - player->last_jy);
7111 if (x == next_jx && y == next_jy)
7119 void StartMoving(int x, int y)
7121 boolean started_moving = FALSE; /* some elements can fall _and_ move */
7122 int element = Feld[x][y];
7127 if (MovDelay[x][y] == 0)
7128 GfxAction[x][y] = ACTION_DEFAULT;
7130 if (CAN_FALL(element) && y < lev_fieldy - 1)
7132 if ((x > 0 && IS_PLAYER(x - 1, y)) ||
7133 (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
7134 if (JustBeingPushed(x, y))
7137 if (element == EL_QUICKSAND_FULL)
7139 if (IS_FREE(x, y + 1))
7141 InitMovingField(x, y, MV_DOWN);
7142 started_moving = TRUE;
7144 Feld[x][y] = EL_QUICKSAND_EMPTYING;
7145 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7146 if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7147 Store[x][y] = EL_ROCK;
7149 Store[x][y] = EL_ROCK;
7152 PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7154 else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7156 if (!MovDelay[x][y])
7158 MovDelay[x][y] = TILEY + 1;
7160 ResetGfxAnimation(x, y);
7161 ResetGfxAnimation(x, y + 1);
7166 DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7167 DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7174 Feld[x][y] = EL_QUICKSAND_EMPTY;
7175 Feld[x][y + 1] = EL_QUICKSAND_FULL;
7176 Store[x][y + 1] = Store[x][y];
7179 PlayLevelSoundAction(x, y, ACTION_FILLING);
7181 else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7183 if (!MovDelay[x][y])
7185 MovDelay[x][y] = TILEY + 1;
7187 ResetGfxAnimation(x, y);
7188 ResetGfxAnimation(x, y + 1);
7193 DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7194 DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7201 Feld[x][y] = EL_QUICKSAND_EMPTY;
7202 Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7203 Store[x][y + 1] = Store[x][y];
7206 PlayLevelSoundAction(x, y, ACTION_FILLING);
7209 else if (element == EL_QUICKSAND_FAST_FULL)
7211 if (IS_FREE(x, y + 1))
7213 InitMovingField(x, y, MV_DOWN);
7214 started_moving = TRUE;
7216 Feld[x][y] = EL_QUICKSAND_FAST_EMPTYING;
7217 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7218 if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7219 Store[x][y] = EL_ROCK;
7221 Store[x][y] = EL_ROCK;
7224 PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7226 else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7228 if (!MovDelay[x][y])
7230 MovDelay[x][y] = TILEY + 1;
7232 ResetGfxAnimation(x, y);
7233 ResetGfxAnimation(x, y + 1);
7238 DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7239 DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7246 Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
7247 Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7248 Store[x][y + 1] = Store[x][y];
7251 PlayLevelSoundAction(x, y, ACTION_FILLING);
7253 else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7255 if (!MovDelay[x][y])
7257 MovDelay[x][y] = TILEY + 1;
7259 ResetGfxAnimation(x, y);
7260 ResetGfxAnimation(x, y + 1);
7265 DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7266 DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7273 Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
7274 Feld[x][y + 1] = EL_QUICKSAND_FULL;
7275 Store[x][y + 1] = Store[x][y];
7278 PlayLevelSoundAction(x, y, ACTION_FILLING);
7281 else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7282 Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7284 InitMovingField(x, y, MV_DOWN);
7285 started_moving = TRUE;
7287 Feld[x][y] = EL_QUICKSAND_FILLING;
7288 Store[x][y] = element;
7290 PlayLevelSoundAction(x, y, ACTION_FILLING);
7292 else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7293 Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7295 InitMovingField(x, y, MV_DOWN);
7296 started_moving = TRUE;
7298 Feld[x][y] = EL_QUICKSAND_FAST_FILLING;
7299 Store[x][y] = element;
7301 PlayLevelSoundAction(x, y, ACTION_FILLING);
7303 else if (element == EL_MAGIC_WALL_FULL)
7305 if (IS_FREE(x, y + 1))
7307 InitMovingField(x, y, MV_DOWN);
7308 started_moving = TRUE;
7310 Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
7311 Store[x][y] = EL_CHANGED(Store[x][y]);
7313 else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
7315 if (!MovDelay[x][y])
7316 MovDelay[x][y] = TILEY / 4 + 1;
7325 Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
7326 Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
7327 Store[x][y + 1] = EL_CHANGED(Store[x][y]);
7331 else if (element == EL_BD_MAGIC_WALL_FULL)
7333 if (IS_FREE(x, y + 1))
7335 InitMovingField(x, y, MV_DOWN);
7336 started_moving = TRUE;
7338 Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
7339 Store[x][y] = EL_CHANGED_BD(Store[x][y]);
7341 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
7343 if (!MovDelay[x][y])
7344 MovDelay[x][y] = TILEY / 4 + 1;
7353 Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
7354 Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
7355 Store[x][y + 1] = EL_CHANGED_BD(Store[x][y]);
7359 else if (element == EL_DC_MAGIC_WALL_FULL)
7361 if (IS_FREE(x, y + 1))
7363 InitMovingField(x, y, MV_DOWN);
7364 started_moving = TRUE;
7366 Feld[x][y] = EL_DC_MAGIC_WALL_EMPTYING;
7367 Store[x][y] = EL_CHANGED_DC(Store[x][y]);
7369 else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
7371 if (!MovDelay[x][y])
7372 MovDelay[x][y] = TILEY / 4 + 1;
7381 Feld[x][y] = EL_DC_MAGIC_WALL_ACTIVE;
7382 Feld[x][y + 1] = EL_DC_MAGIC_WALL_FULL;
7383 Store[x][y + 1] = EL_CHANGED_DC(Store[x][y]);
7387 else if ((CAN_PASS_MAGIC_WALL(element) &&
7388 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
7389 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)) ||
7390 (CAN_PASS_DC_MAGIC_WALL(element) &&
7391 (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)))
7394 InitMovingField(x, y, MV_DOWN);
7395 started_moving = TRUE;
7398 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
7399 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ? EL_BD_MAGIC_WALL_FILLING :
7400 EL_DC_MAGIC_WALL_FILLING);
7401 Store[x][y] = element;
7403 else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
7405 SplashAcid(x, y + 1);
7407 InitMovingField(x, y, MV_DOWN);
7408 started_moving = TRUE;
7410 Store[x][y] = EL_ACID;
7413 (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7414 CheckImpact[x][y] && !IS_FREE(x, y + 1)) ||
7415 (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
7416 CAN_FALL(element) && WasJustFalling[x][y] &&
7417 (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
7419 (game.engine_version < VERSION_IDENT(2,2,0,7) &&
7420 CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
7421 (Feld[x][y + 1] == EL_BLOCKED)))
7423 /* this is needed for a special case not covered by calling "Impact()"
7424 from "ContinueMoving()": if an element moves to a tile directly below
7425 another element which was just falling on that tile (which was empty
7426 in the previous frame), the falling element above would just stop
7427 instead of smashing the element below (in previous version, the above
7428 element was just checked for "moving" instead of "falling", resulting
7429 in incorrect smashes caused by horizontal movement of the above
7430 element; also, the case of the player being the element to smash was
7431 simply not covered here... :-/ ) */
7433 CheckCollision[x][y] = 0;
7434 CheckImpact[x][y] = 0;
7438 else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
7440 if (MovDir[x][y] == MV_NONE)
7442 InitMovingField(x, y, MV_DOWN);
7443 started_moving = TRUE;
7446 else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
7448 if (WasJustFalling[x][y]) /* prevent animation from being restarted */
7449 MovDir[x][y] = MV_DOWN;
7451 InitMovingField(x, y, MV_DOWN);
7452 started_moving = TRUE;
7454 else if (element == EL_AMOEBA_DROP)
7456 Feld[x][y] = EL_AMOEBA_GROWING;
7457 Store[x][y] = EL_AMOEBA_WET;
7459 else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
7460 (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
7461 !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
7462 element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
7464 boolean can_fall_left = (x > 0 && IS_FREE(x - 1, y) &&
7465 (IS_FREE(x - 1, y + 1) ||
7466 Feld[x - 1][y + 1] == EL_ACID));
7467 boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
7468 (IS_FREE(x + 1, y + 1) ||
7469 Feld[x + 1][y + 1] == EL_ACID));
7470 boolean can_fall_any = (can_fall_left || can_fall_right);
7471 boolean can_fall_both = (can_fall_left && can_fall_right);
7472 int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
7474 if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
7476 if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
7477 can_fall_right = FALSE;
7478 else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
7479 can_fall_left = FALSE;
7480 else if (slippery_type == SLIPPERY_ONLY_LEFT)
7481 can_fall_right = FALSE;
7482 else if (slippery_type == SLIPPERY_ONLY_RIGHT)
7483 can_fall_left = FALSE;
7485 can_fall_any = (can_fall_left || can_fall_right);
7486 can_fall_both = FALSE;
7491 if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
7492 can_fall_right = FALSE; /* slip down on left side */
7494 can_fall_left = !(can_fall_right = RND(2));
7496 can_fall_both = FALSE;
7501 /* if not determined otherwise, prefer left side for slipping down */
7502 InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
7503 started_moving = TRUE;
7506 else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
7508 boolean left_is_free = (x > 0 && IS_FREE(x - 1, y));
7509 boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
7510 int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
7511 int belt_dir = game.belt_dir[belt_nr];
7513 if ((belt_dir == MV_LEFT && left_is_free) ||
7514 (belt_dir == MV_RIGHT && right_is_free))
7516 int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
7518 InitMovingField(x, y, belt_dir);
7519 started_moving = TRUE;
7521 Pushed[x][y] = TRUE;
7522 Pushed[nextx][y] = TRUE;
7524 GfxAction[x][y] = ACTION_DEFAULT;
7528 MovDir[x][y] = 0; /* if element was moving, stop it */
7533 /* not "else if" because of elements that can fall and move (EL_SPRING) */
7534 if (CAN_MOVE(element) && !started_moving)
7536 int move_pattern = element_info[element].move_pattern;
7539 Moving2Blocked(x, y, &newx, &newy);
7541 if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
7544 if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7545 CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7547 WasJustMoving[x][y] = 0;
7548 CheckCollision[x][y] = 0;
7550 TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
7552 if (Feld[x][y] != element) /* element has changed */
7556 if (!MovDelay[x][y]) /* start new movement phase */
7558 /* all objects that can change their move direction after each step
7559 (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
7561 if (element != EL_YAMYAM &&
7562 element != EL_DARK_YAMYAM &&
7563 element != EL_PACMAN &&
7564 !(move_pattern & MV_ANY_DIRECTION) &&
7565 move_pattern != MV_TURNING_LEFT &&
7566 move_pattern != MV_TURNING_RIGHT &&
7567 move_pattern != MV_TURNING_LEFT_RIGHT &&
7568 move_pattern != MV_TURNING_RIGHT_LEFT &&
7569 move_pattern != MV_TURNING_RANDOM)
7573 if (MovDelay[x][y] && (element == EL_BUG ||
7574 element == EL_SPACESHIP ||
7575 element == EL_SP_SNIKSNAK ||
7576 element == EL_SP_ELECTRON ||
7577 element == EL_MOLE))
7578 TEST_DrawLevelField(x, y);
7582 if (MovDelay[x][y]) /* wait some time before next movement */
7586 if (element == EL_ROBOT ||
7587 element == EL_YAMYAM ||
7588 element == EL_DARK_YAMYAM)
7590 DrawLevelElementAnimationIfNeeded(x, y, element);
7591 PlayLevelSoundAction(x, y, ACTION_WAITING);
7593 else if (element == EL_SP_ELECTRON)
7594 DrawLevelElementAnimationIfNeeded(x, y, element);
7595 else if (element == EL_DRAGON)
7598 int dir = MovDir[x][y];
7599 int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
7600 int dy = (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
7601 int graphic = (dir == MV_LEFT ? IMG_FLAMES_1_LEFT :
7602 dir == MV_RIGHT ? IMG_FLAMES_1_RIGHT :
7603 dir == MV_UP ? IMG_FLAMES_1_UP :
7604 dir == MV_DOWN ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
7605 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
7607 GfxAction[x][y] = ACTION_ATTACKING;
7609 if (IS_PLAYER(x, y))
7610 DrawPlayerField(x, y);
7612 TEST_DrawLevelField(x, y);
7614 PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
7616 for (i = 1; i <= 3; i++)
7618 int xx = x + i * dx;
7619 int yy = y + i * dy;
7620 int sx = SCREENX(xx);
7621 int sy = SCREENY(yy);
7622 int flame_graphic = graphic + (i - 1);
7624 if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
7629 int flamed = MovingOrBlocked2Element(xx, yy);
7631 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
7634 RemoveMovingField(xx, yy);
7636 ChangeDelay[xx][yy] = 0;
7638 Feld[xx][yy] = EL_FLAMES;
7640 if (IN_SCR_FIELD(sx, sy))
7642 TEST_DrawLevelFieldCrumbled(xx, yy);
7643 DrawGraphic(sx, sy, flame_graphic, frame);
7648 if (Feld[xx][yy] == EL_FLAMES)
7649 Feld[xx][yy] = EL_EMPTY;
7650 TEST_DrawLevelField(xx, yy);
7655 if (MovDelay[x][y]) /* element still has to wait some time */
7657 PlayLevelSoundAction(x, y, ACTION_WAITING);
7663 /* now make next step */
7665 Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
7667 if (DONT_COLLIDE_WITH(element) &&
7668 IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
7669 !PLAYER_ENEMY_PROTECTED(newx, newy))
7671 TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
7676 else if (CAN_MOVE_INTO_ACID(element) &&
7677 IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID &&
7678 !IS_MV_DIAGONAL(MovDir[x][y]) &&
7679 (MovDir[x][y] == MV_DOWN ||
7680 game.engine_version >= VERSION_IDENT(3,1,0,0)))
7682 SplashAcid(newx, newy);
7683 Store[x][y] = EL_ACID;
7685 else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
7687 if (Feld[newx][newy] == EL_EXIT_OPEN ||
7688 Feld[newx][newy] == EL_EM_EXIT_OPEN ||
7689 Feld[newx][newy] == EL_STEEL_EXIT_OPEN ||
7690 Feld[newx][newy] == EL_EM_STEEL_EXIT_OPEN)
7693 TEST_DrawLevelField(x, y);
7695 PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
7696 if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
7697 DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
7699 local_player->friends_still_needed--;
7700 if (!local_player->friends_still_needed &&
7701 !local_player->GameOver && AllPlayersGone)
7702 PlayerWins(local_player);
7706 else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
7708 if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MP_MOVING)
7709 TEST_DrawLevelField(newx, newy);
7711 GfxDir[x][y] = MovDir[x][y] = MV_NONE;
7713 else if (!IS_FREE(newx, newy))
7715 GfxAction[x][y] = ACTION_WAITING;
7717 if (IS_PLAYER(x, y))
7718 DrawPlayerField(x, y);
7720 TEST_DrawLevelField(x, y);
7725 else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
7727 if (IS_FOOD_PIG(Feld[newx][newy]))
7729 if (IS_MOVING(newx, newy))
7730 RemoveMovingField(newx, newy);
7733 Feld[newx][newy] = EL_EMPTY;
7734 TEST_DrawLevelField(newx, newy);
7737 PlayLevelSound(x, y, SND_PIG_DIGGING);
7739 else if (!IS_FREE(newx, newy))
7741 if (IS_PLAYER(x, y))
7742 DrawPlayerField(x, y);
7744 TEST_DrawLevelField(x, y);
7749 else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
7751 if (Store[x][y] != EL_EMPTY)
7753 boolean can_clone = FALSE;
7756 /* check if element to clone is still there */
7757 for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
7759 if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == Store[x][y])
7767 /* cannot clone or target field not free anymore -- do not clone */
7768 if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7769 Store[x][y] = EL_EMPTY;
7772 if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7774 if (IS_MV_DIAGONAL(MovDir[x][y]))
7776 int diagonal_move_dir = MovDir[x][y];
7777 int stored = Store[x][y];
7778 int change_delay = 8;
7781 /* android is moving diagonally */
7783 CreateField(x, y, EL_DIAGONAL_SHRINKING);
7785 Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
7786 GfxElement[x][y] = EL_EMC_ANDROID;
7787 GfxAction[x][y] = ACTION_SHRINKING;
7788 GfxDir[x][y] = diagonal_move_dir;
7789 ChangeDelay[x][y] = change_delay;
7791 graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
7794 DrawLevelGraphicAnimation(x, y, graphic);
7795 PlayLevelSoundAction(x, y, ACTION_SHRINKING);
7797 if (Feld[newx][newy] == EL_ACID)
7799 SplashAcid(newx, newy);
7804 CreateField(newx, newy, EL_DIAGONAL_GROWING);
7806 Store[newx][newy] = EL_EMC_ANDROID;
7807 GfxElement[newx][newy] = EL_EMC_ANDROID;
7808 GfxAction[newx][newy] = ACTION_GROWING;
7809 GfxDir[newx][newy] = diagonal_move_dir;
7810 ChangeDelay[newx][newy] = change_delay;
7812 graphic = el_act_dir2img(GfxElement[newx][newy],
7813 GfxAction[newx][newy], GfxDir[newx][newy]);
7815 DrawLevelGraphicAnimation(newx, newy, graphic);
7816 PlayLevelSoundAction(newx, newy, ACTION_GROWING);
7822 Feld[newx][newy] = EL_EMPTY;
7823 TEST_DrawLevelField(newx, newy);
7825 PlayLevelSoundAction(x, y, ACTION_DIGGING);
7828 else if (!IS_FREE(newx, newy))
7833 else if (IS_CUSTOM_ELEMENT(element) &&
7834 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7836 if (!DigFieldByCE(newx, newy, element))
7839 if (move_pattern & MV_MAZE_RUNNER_STYLE)
7841 RunnerVisit[x][y] = FrameCounter;
7842 PlayerVisit[x][y] /= 8; /* expire player visit path */
7845 else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
7847 if (!IS_FREE(newx, newy))
7849 if (IS_PLAYER(x, y))
7850 DrawPlayerField(x, y);
7852 TEST_DrawLevelField(x, y);
7858 boolean wanna_flame = !RND(10);
7859 int dx = newx - x, dy = newy - y;
7860 int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
7861 int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
7862 int element1 = (IN_LEV_FIELD(newx1, newy1) ?
7863 MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
7864 int element2 = (IN_LEV_FIELD(newx2, newy2) ?
7865 MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
7868 IS_CLASSIC_ENEMY(element1) ||
7869 IS_CLASSIC_ENEMY(element2)) &&
7870 element1 != EL_DRAGON && element2 != EL_DRAGON &&
7871 element1 != EL_FLAMES && element2 != EL_FLAMES)
7873 ResetGfxAnimation(x, y);
7874 GfxAction[x][y] = ACTION_ATTACKING;
7876 if (IS_PLAYER(x, y))
7877 DrawPlayerField(x, y);
7879 TEST_DrawLevelField(x, y);
7881 PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
7883 MovDelay[x][y] = 50;
7885 Feld[newx][newy] = EL_FLAMES;
7886 if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
7887 Feld[newx1][newy1] = EL_FLAMES;
7888 if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
7889 Feld[newx2][newy2] = EL_FLAMES;
7895 else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
7896 Feld[newx][newy] == EL_DIAMOND)
7898 if (IS_MOVING(newx, newy))
7899 RemoveMovingField(newx, newy);
7902 Feld[newx][newy] = EL_EMPTY;
7903 TEST_DrawLevelField(newx, newy);
7906 PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
7908 else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
7909 IS_FOOD_DARK_YAMYAM(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 (IS_MOVING(newx, newy))
7921 RemoveMovingField(newx, newy);
7925 Feld[newx][newy] = EL_EMPTY;
7926 TEST_DrawLevelField(newx, newy);
7929 PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
7931 else if ((element == EL_PACMAN || element == EL_MOLE)
7932 && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
7934 if (AmoebaNr[newx][newy])
7936 AmoebaCnt2[AmoebaNr[newx][newy]]--;
7937 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
7938 Feld[newx][newy] == EL_BD_AMOEBA)
7939 AmoebaCnt[AmoebaNr[newx][newy]]--;
7942 if (element == EL_MOLE)
7944 Feld[newx][newy] = EL_AMOEBA_SHRINKING;
7945 PlayLevelSound(x, y, SND_MOLE_DIGGING);
7947 ResetGfxAnimation(x, y);
7948 GfxAction[x][y] = ACTION_DIGGING;
7949 TEST_DrawLevelField(x, y);
7951 MovDelay[newx][newy] = 0; /* start amoeba shrinking delay */
7953 return; /* wait for shrinking amoeba */
7955 else /* element == EL_PACMAN */
7957 Feld[newx][newy] = EL_EMPTY;
7958 TEST_DrawLevelField(newx, newy);
7959 PlayLevelSound(x, y, SND_PACMAN_DIGGING);
7962 else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
7963 (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
7964 (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
7966 /* wait for shrinking amoeba to completely disappear */
7969 else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
7971 /* object was running against a wall */
7975 if (GFX_ELEMENT(element) != EL_SAND) /* !!! FIX THIS (crumble) !!! */
7976 DrawLevelElementAnimation(x, y, element);
7978 if (DONT_TOUCH(element))
7979 TestIfBadThingTouchesPlayer(x, y);
7984 InitMovingField(x, y, MovDir[x][y]);
7986 PlayLevelSoundAction(x, y, ACTION_MOVING);
7990 ContinueMoving(x, y);
7993 void ContinueMoving(int x, int y)
7995 int element = Feld[x][y];
7996 struct ElementInfo *ei = &element_info[element];
7997 int direction = MovDir[x][y];
7998 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
7999 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
8000 int newx = x + dx, newy = y + dy;
8001 int stored = Store[x][y];
8002 int stored_new = Store[newx][newy];
8003 boolean pushed_by_player = (Pushed[x][y] && IS_PLAYER(x, y));
8004 boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
8005 boolean last_line = (newy == lev_fieldy - 1);
8007 MovPos[x][y] += getElementMoveStepsize(x, y);
8009 if (pushed_by_player) /* special case: moving object pushed by player */
8010 MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
8012 if (ABS(MovPos[x][y]) < TILEX)
8014 TEST_DrawLevelField(x, y);
8016 return; /* element is still moving */
8019 /* element reached destination field */
8021 Feld[x][y] = EL_EMPTY;
8022 Feld[newx][newy] = element;
8023 MovPos[x][y] = 0; /* force "not moving" for "crumbled sand" */
8025 if (Store[x][y] == EL_ACID) /* element is moving into acid pool */
8027 element = Feld[newx][newy] = EL_ACID;
8029 else if (element == EL_MOLE)
8031 Feld[x][y] = EL_SAND;
8033 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8035 else if (element == EL_QUICKSAND_FILLING)
8037 element = Feld[newx][newy] = get_next_element(element);
8038 Store[newx][newy] = Store[x][y];
8040 else if (element == EL_QUICKSAND_EMPTYING)
8042 Feld[x][y] = get_next_element(element);
8043 element = Feld[newx][newy] = Store[x][y];
8045 else if (element == EL_QUICKSAND_FAST_FILLING)
8047 element = Feld[newx][newy] = get_next_element(element);
8048 Store[newx][newy] = Store[x][y];
8050 else if (element == EL_QUICKSAND_FAST_EMPTYING)
8052 Feld[x][y] = get_next_element(element);
8053 element = Feld[newx][newy] = Store[x][y];
8055 else if (element == EL_MAGIC_WALL_FILLING)
8057 element = Feld[newx][newy] = get_next_element(element);
8058 if (!game.magic_wall_active)
8059 element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
8060 Store[newx][newy] = Store[x][y];
8062 else if (element == EL_MAGIC_WALL_EMPTYING)
8064 Feld[x][y] = get_next_element(element);
8065 if (!game.magic_wall_active)
8066 Feld[x][y] = EL_MAGIC_WALL_DEAD;
8067 element = Feld[newx][newy] = Store[x][y];
8069 InitField(newx, newy, FALSE);
8071 else if (element == EL_BD_MAGIC_WALL_FILLING)
8073 element = Feld[newx][newy] = get_next_element(element);
8074 if (!game.magic_wall_active)
8075 element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
8076 Store[newx][newy] = Store[x][y];
8078 else if (element == EL_BD_MAGIC_WALL_EMPTYING)
8080 Feld[x][y] = get_next_element(element);
8081 if (!game.magic_wall_active)
8082 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
8083 element = Feld[newx][newy] = Store[x][y];
8085 InitField(newx, newy, FALSE);
8087 else if (element == EL_DC_MAGIC_WALL_FILLING)
8089 element = Feld[newx][newy] = get_next_element(element);
8090 if (!game.magic_wall_active)
8091 element = Feld[newx][newy] = EL_DC_MAGIC_WALL_DEAD;
8092 Store[newx][newy] = Store[x][y];
8094 else if (element == EL_DC_MAGIC_WALL_EMPTYING)
8096 Feld[x][y] = get_next_element(element);
8097 if (!game.magic_wall_active)
8098 Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
8099 element = Feld[newx][newy] = Store[x][y];
8101 InitField(newx, newy, FALSE);
8103 else if (element == EL_AMOEBA_DROPPING)
8105 Feld[x][y] = get_next_element(element);
8106 element = Feld[newx][newy] = Store[x][y];
8108 else if (element == EL_SOKOBAN_OBJECT)
8111 Feld[x][y] = Back[x][y];
8113 if (Back[newx][newy])
8114 Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
8116 Back[x][y] = Back[newx][newy] = 0;
8119 Store[x][y] = EL_EMPTY;
8124 MovDelay[newx][newy] = 0;
8126 if (CAN_CHANGE_OR_HAS_ACTION(element))
8128 /* copy element change control values to new field */
8129 ChangeDelay[newx][newy] = ChangeDelay[x][y];
8130 ChangePage[newx][newy] = ChangePage[x][y];
8131 ChangeCount[newx][newy] = ChangeCount[x][y];
8132 ChangeEvent[newx][newy] = ChangeEvent[x][y];
8135 CustomValue[newx][newy] = CustomValue[x][y];
8137 ChangeDelay[x][y] = 0;
8138 ChangePage[x][y] = -1;
8139 ChangeCount[x][y] = 0;
8140 ChangeEvent[x][y] = -1;
8142 CustomValue[x][y] = 0;
8144 /* copy animation control values to new field */
8145 GfxFrame[newx][newy] = GfxFrame[x][y];
8146 GfxRandom[newx][newy] = GfxRandom[x][y]; /* keep same random value */
8147 GfxAction[newx][newy] = GfxAction[x][y]; /* keep action one frame */
8148 GfxDir[newx][newy] = GfxDir[x][y]; /* keep element direction */
8150 Pushed[x][y] = Pushed[newx][newy] = FALSE;
8152 /* some elements can leave other elements behind after moving */
8153 if (ei->move_leave_element != EL_EMPTY &&
8154 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
8155 (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
8157 int move_leave_element = ei->move_leave_element;
8159 /* this makes it possible to leave the removed element again */
8160 if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
8161 move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
8163 Feld[x][y] = move_leave_element;
8165 if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
8166 MovDir[x][y] = direction;
8168 InitField(x, y, FALSE);
8170 if (GFX_CRUMBLED(Feld[x][y]))
8171 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8173 if (ELEM_IS_PLAYER(move_leave_element))
8174 RelocatePlayer(x, y, move_leave_element);
8177 /* do this after checking for left-behind element */
8178 ResetGfxAnimation(x, y); /* reset animation values for old field */
8180 if (!CAN_MOVE(element) ||
8181 (CAN_FALL(element) && direction == MV_DOWN &&
8182 (element == EL_SPRING ||
8183 element_info[element].move_pattern == MV_WHEN_PUSHED ||
8184 element_info[element].move_pattern == MV_WHEN_DROPPED)))
8185 GfxDir[x][y] = MovDir[newx][newy] = 0;
8187 TEST_DrawLevelField(x, y);
8188 TEST_DrawLevelField(newx, newy);
8190 Stop[newx][newy] = TRUE; /* ignore this element until the next frame */
8192 /* prevent pushed element from moving on in pushed direction */
8193 if (pushed_by_player && CAN_MOVE(element) &&
8194 element_info[element].move_pattern & MV_ANY_DIRECTION &&
8195 !(element_info[element].move_pattern & direction))
8196 TurnRound(newx, newy);
8198 /* prevent elements on conveyor belt from moving on in last direction */
8199 if (pushed_by_conveyor && CAN_FALL(element) &&
8200 direction & MV_HORIZONTAL)
8201 MovDir[newx][newy] = 0;
8203 if (!pushed_by_player)
8205 int nextx = newx + dx, nexty = newy + dy;
8206 boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
8208 WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
8210 if (CAN_FALL(element) && direction == MV_DOWN)
8211 WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
8213 if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
8214 CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
8216 if (CAN_FALL(element) && direction == MV_DOWN && check_collision_again)
8217 CheckImpact[newx][newy] = CHECK_DELAY_IMPACT;
8220 if (DONT_TOUCH(element)) /* object may be nasty to player or others */
8222 TestIfBadThingTouchesPlayer(newx, newy);
8223 TestIfBadThingTouchesFriend(newx, newy);
8225 if (!IS_CUSTOM_ELEMENT(element))
8226 TestIfBadThingTouchesOtherBadThing(newx, newy);
8228 else if (element == EL_PENGUIN)
8229 TestIfFriendTouchesBadThing(newx, newy);
8231 if (DONT_GET_HIT_BY(element))
8233 TestIfGoodThingGetsHitByBadThing(newx, newy, direction);
8236 /* give the player one last chance (one more frame) to move away */
8237 if (CAN_FALL(element) && direction == MV_DOWN &&
8238 (last_line || (!IS_FREE(x, newy + 1) &&
8239 (!IS_PLAYER(x, newy + 1) ||
8240 game.engine_version < VERSION_IDENT(3,1,1,0)))))
8243 if (pushed_by_player && !game.use_change_when_pushing_bug)
8245 int push_side = MV_DIR_OPPOSITE(direction);
8246 struct PlayerInfo *player = PLAYERINFO(x, y);
8248 CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
8249 player->index_bit, push_side);
8250 CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
8251 player->index_bit, push_side);
8254 if (element == EL_EMC_ANDROID && pushed_by_player) /* make another move */
8255 MovDelay[newx][newy] = 1;
8257 CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
8259 TestIfElementTouchesCustomElement(x, y); /* empty or new element */
8260 TestIfElementHitsCustomElement(newx, newy, direction);
8261 TestIfPlayerTouchesCustomElement(newx, newy);
8262 TestIfElementTouchesCustomElement(newx, newy);
8264 if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
8265 IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
8266 CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
8267 MV_DIR_OPPOSITE(direction));
8270 int AmoebeNachbarNr(int ax, int ay)
8273 int element = Feld[ax][ay];
8275 static int xy[4][2] =
8283 for (i = 0; i < NUM_DIRECTIONS; i++)
8285 int x = ax + xy[i][0];
8286 int y = ay + xy[i][1];
8288 if (!IN_LEV_FIELD(x, y))
8291 if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
8292 group_nr = AmoebaNr[x][y];
8298 void AmoebenVereinigen(int ax, int ay)
8300 int i, x, y, xx, yy;
8301 int new_group_nr = AmoebaNr[ax][ay];
8302 static int xy[4][2] =
8310 if (new_group_nr == 0)
8313 for (i = 0; i < NUM_DIRECTIONS; i++)
8318 if (!IN_LEV_FIELD(x, y))
8321 if ((Feld[x][y] == EL_AMOEBA_FULL ||
8322 Feld[x][y] == EL_BD_AMOEBA ||
8323 Feld[x][y] == EL_AMOEBA_DEAD) &&
8324 AmoebaNr[x][y] != new_group_nr)
8326 int old_group_nr = AmoebaNr[x][y];
8328 if (old_group_nr == 0)
8331 AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
8332 AmoebaCnt[old_group_nr] = 0;
8333 AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
8334 AmoebaCnt2[old_group_nr] = 0;
8336 SCAN_PLAYFIELD(xx, yy)
8338 if (AmoebaNr[xx][yy] == old_group_nr)
8339 AmoebaNr[xx][yy] = new_group_nr;
8345 void AmoebeUmwandeln(int ax, int ay)
8349 if (Feld[ax][ay] == EL_AMOEBA_DEAD)
8351 int group_nr = AmoebaNr[ax][ay];
8356 printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
8357 printf("AmoebeUmwandeln(): This should never happen!\n");
8362 SCAN_PLAYFIELD(x, y)
8364 if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
8367 Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
8371 PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
8372 SND_AMOEBA_TURNING_TO_GEM :
8373 SND_AMOEBA_TURNING_TO_ROCK));
8378 static int xy[4][2] =
8386 for (i = 0; i < NUM_DIRECTIONS; i++)
8391 if (!IN_LEV_FIELD(x, y))
8394 if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
8396 PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
8397 SND_AMOEBA_TURNING_TO_GEM :
8398 SND_AMOEBA_TURNING_TO_ROCK));
8405 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
8408 int group_nr = AmoebaNr[ax][ay];
8409 boolean done = FALSE;
8414 printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
8415 printf("AmoebeUmwandelnBD(): This should never happen!\n");
8420 SCAN_PLAYFIELD(x, y)
8422 if (AmoebaNr[x][y] == group_nr &&
8423 (Feld[x][y] == EL_AMOEBA_DEAD ||
8424 Feld[x][y] == EL_BD_AMOEBA ||
8425 Feld[x][y] == EL_AMOEBA_GROWING))
8428 Feld[x][y] = new_element;
8429 InitField(x, y, FALSE);
8430 TEST_DrawLevelField(x, y);
8436 PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
8437 SND_BD_AMOEBA_TURNING_TO_ROCK :
8438 SND_BD_AMOEBA_TURNING_TO_GEM));
8441 void AmoebeWaechst(int x, int y)
8443 static unsigned int sound_delay = 0;
8444 static unsigned int sound_delay_value = 0;
8446 if (!MovDelay[x][y]) /* start new growing cycle */
8450 if (DelayReached(&sound_delay, sound_delay_value))
8452 PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
8453 sound_delay_value = 30;
8457 if (MovDelay[x][y]) /* wait some time before growing bigger */
8460 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8462 int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
8463 6 - MovDelay[x][y]);
8465 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
8468 if (!MovDelay[x][y])
8470 Feld[x][y] = Store[x][y];
8472 TEST_DrawLevelField(x, y);
8477 void AmoebaDisappearing(int x, int y)
8479 static unsigned int sound_delay = 0;
8480 static unsigned int sound_delay_value = 0;
8482 if (!MovDelay[x][y]) /* start new shrinking cycle */
8486 if (DelayReached(&sound_delay, sound_delay_value))
8487 sound_delay_value = 30;
8490 if (MovDelay[x][y]) /* wait some time before shrinking */
8493 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8495 int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
8496 6 - MovDelay[x][y]);
8498 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
8501 if (!MovDelay[x][y])
8503 Feld[x][y] = EL_EMPTY;
8504 TEST_DrawLevelField(x, y);
8506 /* don't let mole enter this field in this cycle;
8507 (give priority to objects falling to this field from above) */
8513 void AmoebeAbleger(int ax, int ay)
8516 int element = Feld[ax][ay];
8517 int graphic = el2img(element);
8518 int newax = ax, neway = ay;
8519 boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
8520 static int xy[4][2] =
8528 if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
8530 Feld[ax][ay] = EL_AMOEBA_DEAD;
8531 TEST_DrawLevelField(ax, ay);
8535 if (IS_ANIMATED(graphic))
8536 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
8538 if (!MovDelay[ax][ay]) /* start making new amoeba field */
8539 MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
8541 if (MovDelay[ax][ay]) /* wait some time before making new amoeba */
8544 if (MovDelay[ax][ay])
8548 if (can_drop) /* EL_AMOEBA_WET or EL_EMC_DRIPPER */
8551 int x = ax + xy[start][0];
8552 int y = ay + xy[start][1];
8554 if (!IN_LEV_FIELD(x, y))
8557 if (IS_FREE(x, y) ||
8558 CAN_GROW_INTO(Feld[x][y]) ||
8559 Feld[x][y] == EL_QUICKSAND_EMPTY ||
8560 Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
8566 if (newax == ax && neway == ay)
8569 else /* normal or "filled" (BD style) amoeba */
8572 boolean waiting_for_player = FALSE;
8574 for (i = 0; i < NUM_DIRECTIONS; i++)
8576 int j = (start + i) % 4;
8577 int x = ax + xy[j][0];
8578 int y = ay + xy[j][1];
8580 if (!IN_LEV_FIELD(x, y))
8583 if (IS_FREE(x, y) ||
8584 CAN_GROW_INTO(Feld[x][y]) ||
8585 Feld[x][y] == EL_QUICKSAND_EMPTY ||
8586 Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
8592 else if (IS_PLAYER(x, y))
8593 waiting_for_player = TRUE;
8596 if (newax == ax && neway == ay) /* amoeba cannot grow */
8598 if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
8600 Feld[ax][ay] = EL_AMOEBA_DEAD;
8601 TEST_DrawLevelField(ax, ay);
8602 AmoebaCnt[AmoebaNr[ax][ay]]--;
8604 if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0) /* amoeba is completely dead */
8606 if (element == EL_AMOEBA_FULL)
8607 AmoebeUmwandeln(ax, ay);
8608 else if (element == EL_BD_AMOEBA)
8609 AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
8614 else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
8616 /* amoeba gets larger by growing in some direction */
8618 int new_group_nr = AmoebaNr[ax][ay];
8621 if (new_group_nr == 0)
8623 printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
8624 printf("AmoebeAbleger(): This should never happen!\n");
8629 AmoebaNr[newax][neway] = new_group_nr;
8630 AmoebaCnt[new_group_nr]++;
8631 AmoebaCnt2[new_group_nr]++;
8633 /* if amoeba touches other amoeba(s) after growing, unify them */
8634 AmoebenVereinigen(newax, neway);
8636 if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
8638 AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
8644 if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
8645 (neway == lev_fieldy - 1 && newax != ax))
8647 Feld[newax][neway] = EL_AMOEBA_GROWING; /* creation of new amoeba */
8648 Store[newax][neway] = element;
8650 else if (neway == ay || element == EL_EMC_DRIPPER)
8652 Feld[newax][neway] = EL_AMOEBA_DROP; /* drop left/right of amoeba */
8654 PlayLevelSoundAction(newax, neway, ACTION_GROWING);
8658 InitMovingField(ax, ay, MV_DOWN); /* drop dripping from amoeba */
8659 Feld[ax][ay] = EL_AMOEBA_DROPPING;
8660 Store[ax][ay] = EL_AMOEBA_DROP;
8661 ContinueMoving(ax, ay);
8665 TEST_DrawLevelField(newax, neway);
8668 void Life(int ax, int ay)
8672 int element = Feld[ax][ay];
8673 int graphic = el2img(element);
8674 int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
8676 boolean changed = FALSE;
8678 if (IS_ANIMATED(graphic))
8679 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
8684 if (!MovDelay[ax][ay]) /* start new "game of life" cycle */
8685 MovDelay[ax][ay] = life_time;
8687 if (MovDelay[ax][ay]) /* wait some time before next cycle */
8690 if (MovDelay[ax][ay])
8694 for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
8696 int xx = ax+x1, yy = ay+y1;
8699 if (!IN_LEV_FIELD(xx, yy))
8702 for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
8704 int x = xx+x2, y = yy+y2;
8706 if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
8709 if (((Feld[x][y] == element ||
8710 (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
8712 (IS_FREE(x, y) && Stop[x][y]))
8716 if (xx == ax && yy == ay) /* field in the middle */
8718 if (nachbarn < life_parameter[0] ||
8719 nachbarn > life_parameter[1])
8721 Feld[xx][yy] = EL_EMPTY;
8723 TEST_DrawLevelField(xx, yy);
8724 Stop[xx][yy] = TRUE;
8728 else if (IS_FREE(xx, yy) || CAN_GROW_INTO(Feld[xx][yy]))
8729 { /* free border field */
8730 if (nachbarn >= life_parameter[2] &&
8731 nachbarn <= life_parameter[3])
8733 Feld[xx][yy] = element;
8734 MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
8736 TEST_DrawLevelField(xx, yy);
8737 Stop[xx][yy] = TRUE;
8744 PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
8745 SND_GAME_OF_LIFE_GROWING);
8748 static void InitRobotWheel(int x, int y)
8750 ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
8753 static void RunRobotWheel(int x, int y)
8755 PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
8758 static void StopRobotWheel(int x, int y)
8760 if (ZX == x && ZY == y)
8764 game.robot_wheel_active = FALSE;
8768 static void InitTimegateWheel(int x, int y)
8770 ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
8773 static void RunTimegateWheel(int x, int y)
8775 PlayLevelSound(x, y, SND_CLASS_TIMEGATE_SWITCH_ACTIVE);
8778 static void InitMagicBallDelay(int x, int y)
8780 ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
8783 static void ActivateMagicBall(int bx, int by)
8787 if (level.ball_random)
8789 int pos_border = RND(8); /* select one of the eight border elements */
8790 int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
8791 int xx = pos_content % 3;
8792 int yy = pos_content / 3;
8797 if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
8798 CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
8802 for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
8804 int xx = x - bx + 1;
8805 int yy = y - by + 1;
8807 if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
8808 CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
8812 game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
8815 void CheckExit(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_EXIT_OPENING;
8835 PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
8838 void CheckExitEM(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_EM_EXIT_OPENING;
8858 PlayLevelSoundNearest(x, y, SND_CLASS_EM_EXIT_OPENING);
8861 void CheckExitSteel(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_STEEL_EXIT_OPENING;
8881 PlayLevelSoundNearest(x, y, SND_CLASS_STEEL_EXIT_OPENING);
8884 void CheckExitSteelEM(int x, int y)
8886 if (local_player->gems_still_needed > 0 ||
8887 local_player->sokobanfields_still_needed > 0 ||
8888 local_player->lights_still_needed > 0)
8890 int element = Feld[x][y];
8891 int graphic = el2img(element);
8893 if (IS_ANIMATED(graphic))
8894 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8899 if (AllPlayersGone) /* do not re-open exit door closed after last player */
8902 Feld[x][y] = EL_EM_STEEL_EXIT_OPENING;
8904 PlayLevelSoundNearest(x, y, SND_CLASS_EM_STEEL_EXIT_OPENING);
8907 void CheckExitSP(int x, int y)
8909 if (local_player->gems_still_needed > 0)
8911 int element = Feld[x][y];
8912 int graphic = el2img(element);
8914 if (IS_ANIMATED(graphic))
8915 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8920 if (AllPlayersGone) /* do not re-open exit door closed after last player */
8923 Feld[x][y] = EL_SP_EXIT_OPENING;
8925 PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
8928 static void CloseAllOpenTimegates()
8932 SCAN_PLAYFIELD(x, y)
8934 int element = Feld[x][y];
8936 if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
8938 Feld[x][y] = EL_TIMEGATE_CLOSING;
8940 PlayLevelSoundAction(x, y, ACTION_CLOSING);
8945 void DrawTwinkleOnField(int x, int y)
8947 if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
8950 if (Feld[x][y] == EL_BD_DIAMOND)
8953 if (MovDelay[x][y] == 0) /* next animation frame */
8954 MovDelay[x][y] = 11 * !GetSimpleRandom(500);
8956 if (MovDelay[x][y] != 0) /* wait some time before next frame */
8960 DrawLevelElementAnimation(x, y, Feld[x][y]);
8962 if (MovDelay[x][y] != 0)
8964 int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
8965 10 - MovDelay[x][y]);
8967 DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
8972 void MauerWaechst(int x, int y)
8976 if (!MovDelay[x][y]) /* next animation frame */
8977 MovDelay[x][y] = 3 * delay;
8979 if (MovDelay[x][y]) /* wait some time before next frame */
8983 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8985 int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
8986 int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
8988 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
8991 if (!MovDelay[x][y])
8993 if (MovDir[x][y] == MV_LEFT)
8995 if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
8996 TEST_DrawLevelField(x - 1, y);
8998 else if (MovDir[x][y] == MV_RIGHT)
9000 if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
9001 TEST_DrawLevelField(x + 1, y);
9003 else if (MovDir[x][y] == MV_UP)
9005 if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
9006 TEST_DrawLevelField(x, y - 1);
9010 if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
9011 TEST_DrawLevelField(x, y + 1);
9014 Feld[x][y] = Store[x][y];
9016 GfxDir[x][y] = MovDir[x][y] = MV_NONE;
9017 TEST_DrawLevelField(x, y);
9022 void MauerAbleger(int ax, int ay)
9024 int element = Feld[ax][ay];
9025 int graphic = el2img(element);
9026 boolean oben_frei = FALSE, unten_frei = FALSE;
9027 boolean links_frei = FALSE, rechts_frei = FALSE;
9028 boolean oben_massiv = FALSE, unten_massiv = FALSE;
9029 boolean links_massiv = FALSE, rechts_massiv = FALSE;
9030 boolean new_wall = FALSE;
9032 if (IS_ANIMATED(graphic))
9033 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9035 if (!MovDelay[ax][ay]) /* start building new wall */
9036 MovDelay[ax][ay] = 6;
9038 if (MovDelay[ax][ay]) /* wait some time before building new wall */
9041 if (MovDelay[ax][ay])
9045 if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9047 if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9049 if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9051 if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9054 if (element == EL_EXPANDABLE_WALL_VERTICAL ||
9055 element == EL_EXPANDABLE_WALL_ANY)
9059 Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
9060 Store[ax][ay-1] = element;
9061 GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9062 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9063 DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9064 IMG_EXPANDABLE_WALL_GROWING_UP, 0);
9069 Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
9070 Store[ax][ay+1] = element;
9071 GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9072 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9073 DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9074 IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
9079 if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9080 element == EL_EXPANDABLE_WALL_ANY ||
9081 element == EL_EXPANDABLE_WALL ||
9082 element == EL_BD_EXPANDABLE_WALL)
9086 Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
9087 Store[ax-1][ay] = element;
9088 GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9089 if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9090 DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9091 IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
9097 Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
9098 Store[ax+1][ay] = element;
9099 GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9100 if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9101 DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9102 IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
9107 if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
9108 TEST_DrawLevelField(ax, ay);
9110 if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
9112 if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
9113 unten_massiv = TRUE;
9114 if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
9115 links_massiv = TRUE;
9116 if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
9117 rechts_massiv = TRUE;
9119 if (((oben_massiv && unten_massiv) ||
9120 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9121 element == EL_EXPANDABLE_WALL) &&
9122 ((links_massiv && rechts_massiv) ||
9123 element == EL_EXPANDABLE_WALL_VERTICAL))
9124 Feld[ax][ay] = EL_WALL;
9127 PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9130 void MauerAblegerStahl(int ax, int ay)
9132 int element = Feld[ax][ay];
9133 int graphic = el2img(element);
9134 boolean oben_frei = FALSE, unten_frei = FALSE;
9135 boolean links_frei = FALSE, rechts_frei = FALSE;
9136 boolean oben_massiv = FALSE, unten_massiv = FALSE;
9137 boolean links_massiv = FALSE, rechts_massiv = FALSE;
9138 boolean new_wall = FALSE;
9140 if (IS_ANIMATED(graphic))
9141 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9143 if (!MovDelay[ax][ay]) /* start building new wall */
9144 MovDelay[ax][ay] = 6;
9146 if (MovDelay[ax][ay]) /* wait some time before building new wall */
9149 if (MovDelay[ax][ay])
9153 if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9155 if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9157 if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9159 if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9162 if (element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
9163 element == EL_EXPANDABLE_STEELWALL_ANY)
9167 Feld[ax][ay-1] = EL_EXPANDABLE_STEELWALL_GROWING;
9168 Store[ax][ay-1] = element;
9169 GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9170 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9171 DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9172 IMG_EXPANDABLE_STEELWALL_GROWING_UP, 0);
9177 Feld[ax][ay+1] = EL_EXPANDABLE_STEELWALL_GROWING;
9178 Store[ax][ay+1] = element;
9179 GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9180 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9181 DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9182 IMG_EXPANDABLE_STEELWALL_GROWING_DOWN, 0);
9187 if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
9188 element == EL_EXPANDABLE_STEELWALL_ANY)
9192 Feld[ax-1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9193 Store[ax-1][ay] = element;
9194 GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9195 if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9196 DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9197 IMG_EXPANDABLE_STEELWALL_GROWING_LEFT, 0);
9203 Feld[ax+1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9204 Store[ax+1][ay] = element;
9205 GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9206 if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9207 DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9208 IMG_EXPANDABLE_STEELWALL_GROWING_RIGHT, 0);
9213 if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
9215 if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
9216 unten_massiv = TRUE;
9217 if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
9218 links_massiv = TRUE;
9219 if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
9220 rechts_massiv = TRUE;
9222 if (((oben_massiv && unten_massiv) ||
9223 element == EL_EXPANDABLE_STEELWALL_HORIZONTAL) &&
9224 ((links_massiv && rechts_massiv) ||
9225 element == EL_EXPANDABLE_STEELWALL_VERTICAL))
9226 Feld[ax][ay] = EL_STEELWALL;
9229 PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9232 void CheckForDragon(int x, int y)
9235 boolean dragon_found = FALSE;
9236 static int xy[4][2] =
9244 for (i = 0; i < NUM_DIRECTIONS; i++)
9246 for (j = 0; j < 4; j++)
9248 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9250 if (IN_LEV_FIELD(xx, yy) &&
9251 (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
9253 if (Feld[xx][yy] == EL_DRAGON)
9254 dragon_found = TRUE;
9263 for (i = 0; i < NUM_DIRECTIONS; i++)
9265 for (j = 0; j < 3; j++)
9267 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9269 if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
9271 Feld[xx][yy] = EL_EMPTY;
9272 TEST_DrawLevelField(xx, yy);
9281 static void InitBuggyBase(int x, int y)
9283 int element = Feld[x][y];
9284 int activating_delay = FRAMES_PER_SECOND / 4;
9287 (element == EL_SP_BUGGY_BASE ?
9288 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
9289 element == EL_SP_BUGGY_BASE_ACTIVATING ?
9291 element == EL_SP_BUGGY_BASE_ACTIVE ?
9292 1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
9295 static void WarnBuggyBase(int x, int y)
9298 static int xy[4][2] =
9306 for (i = 0; i < NUM_DIRECTIONS; i++)
9308 int xx = x + xy[i][0];
9309 int yy = y + xy[i][1];
9311 if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
9313 PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
9320 static void InitTrap(int x, int y)
9322 ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
9325 static void ActivateTrap(int x, int y)
9327 PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
9330 static void ChangeActiveTrap(int x, int y)
9332 int graphic = IMG_TRAP_ACTIVE;
9334 /* if new animation frame was drawn, correct crumbled sand border */
9335 if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
9336 TEST_DrawLevelFieldCrumbled(x, y);
9339 static int getSpecialActionElement(int element, int number, int base_element)
9341 return (element != EL_EMPTY ? element :
9342 number != -1 ? base_element + number - 1 :
9346 static int getModifiedActionNumber(int value_old, int operator, int operand,
9347 int value_min, int value_max)
9349 int value_new = (operator == CA_MODE_SET ? operand :
9350 operator == CA_MODE_ADD ? value_old + operand :
9351 operator == CA_MODE_SUBTRACT ? value_old - operand :
9352 operator == CA_MODE_MULTIPLY ? value_old * operand :
9353 operator == CA_MODE_DIVIDE ? value_old / MAX(1, operand) :
9354 operator == CA_MODE_MODULO ? value_old % MAX(1, operand) :
9357 return (value_new < value_min ? value_min :
9358 value_new > value_max ? value_max :
9362 static void ExecuteCustomElementAction(int x, int y, int element, int page)
9364 struct ElementInfo *ei = &element_info[element];
9365 struct ElementChangeInfo *change = &ei->change_page[page];
9366 int target_element = change->target_element;
9367 int action_type = change->action_type;
9368 int action_mode = change->action_mode;
9369 int action_arg = change->action_arg;
9370 int action_element = change->action_element;
9373 if (!change->has_action)
9376 /* ---------- determine action paramater values -------------------------- */
9378 int level_time_value =
9379 (level.time > 0 ? TimeLeft :
9382 int action_arg_element_raw =
9383 (action_arg == CA_ARG_PLAYER_TRIGGER ? change->actual_trigger_player :
9384 action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
9385 action_arg == CA_ARG_ELEMENT_TARGET ? change->target_element :
9386 action_arg == CA_ARG_ELEMENT_ACTION ? change->action_element :
9387 action_arg == CA_ARG_INVENTORY_RM_TRIGGER ? change->actual_trigger_element:
9388 action_arg == CA_ARG_INVENTORY_RM_TARGET ? change->target_element :
9389 action_arg == CA_ARG_INVENTORY_RM_ACTION ? change->action_element :
9391 int action_arg_element = GetElementFromGroupElement(action_arg_element_raw);
9393 int action_arg_direction =
9394 (action_arg >= CA_ARG_DIRECTION_LEFT &&
9395 action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
9396 action_arg == CA_ARG_DIRECTION_TRIGGER ?
9397 change->actual_trigger_side :
9398 action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
9399 MV_DIR_OPPOSITE(change->actual_trigger_side) :
9402 int action_arg_number_min =
9403 (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
9406 int action_arg_number_max =
9407 (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
9408 action_type == CA_SET_LEVEL_GEMS ? 999 :
9409 action_type == CA_SET_LEVEL_TIME ? 9999 :
9410 action_type == CA_SET_LEVEL_SCORE ? 99999 :
9411 action_type == CA_SET_CE_VALUE ? 9999 :
9412 action_type == CA_SET_CE_SCORE ? 9999 :
9415 int action_arg_number_reset =
9416 (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize[0] :
9417 action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
9418 action_type == CA_SET_LEVEL_TIME ? level.time :
9419 action_type == CA_SET_LEVEL_SCORE ? 0 :
9420 action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
9421 action_type == CA_SET_CE_SCORE ? 0 :
9424 int action_arg_number =
9425 (action_arg <= CA_ARG_MAX ? action_arg :
9426 action_arg >= CA_ARG_SPEED_NOT_MOVING &&
9427 action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
9428 action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
9429 action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
9430 action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
9431 action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
9432 action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
9433 action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
9434 action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
9435 action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
9436 action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? local_player->gems_still_needed :
9437 action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? local_player->score :
9438 action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
9439 action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
9440 action_arg == CA_ARG_ELEMENT_CV_ACTION ? GET_NEW_CE_VALUE(action_element):
9441 action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
9442 action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
9443 action_arg == CA_ARG_ELEMENT_CS_ACTION ? GET_CE_SCORE(action_element) :
9444 action_arg == CA_ARG_ELEMENT_NR_TARGET ? change->target_element :
9445 action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
9446 action_arg == CA_ARG_ELEMENT_NR_ACTION ? change->action_element :
9449 int action_arg_number_old =
9450 (action_type == CA_SET_LEVEL_GEMS ? local_player->gems_still_needed :
9451 action_type == CA_SET_LEVEL_TIME ? TimeLeft :
9452 action_type == CA_SET_LEVEL_SCORE ? local_player->score :
9453 action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
9454 action_type == CA_SET_CE_SCORE ? ei->collect_score :
9457 int action_arg_number_new =
9458 getModifiedActionNumber(action_arg_number_old,
9459 action_mode, action_arg_number,
9460 action_arg_number_min, action_arg_number_max);
9462 int trigger_player_bits =
9463 (change->actual_trigger_player_bits != CH_PLAYER_NONE ?
9464 change->actual_trigger_player_bits : change->trigger_player);
9466 int action_arg_player_bits =
9467 (action_arg >= CA_ARG_PLAYER_1 &&
9468 action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
9469 action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
9470 action_arg == CA_ARG_PLAYER_ACTION ? 1 << GET_PLAYER_NR(action_element) :
9473 /* ---------- execute action -------------------------------------------- */
9475 switch (action_type)
9482 /* ---------- level actions ------------------------------------------- */
9484 case CA_RESTART_LEVEL:
9486 game.restart_level = TRUE;
9491 case CA_SHOW_ENVELOPE:
9493 int element = getSpecialActionElement(action_arg_element,
9494 action_arg_number, EL_ENVELOPE_1);
9496 if (IS_ENVELOPE(element))
9497 local_player->show_envelope = element;
9502 case CA_SET_LEVEL_TIME:
9504 if (level.time > 0) /* only modify limited time value */
9506 TimeLeft = action_arg_number_new;
9508 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
9510 DisplayGameControlValues();
9512 if (!TimeLeft && setup.time_limit)
9513 for (i = 0; i < MAX_PLAYERS; i++)
9514 KillPlayer(&stored_player[i]);
9520 case CA_SET_LEVEL_SCORE:
9522 local_player->score = action_arg_number_new;
9524 game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
9526 DisplayGameControlValues();
9531 case CA_SET_LEVEL_GEMS:
9533 local_player->gems_still_needed = action_arg_number_new;
9535 game.snapshot.collected_item = TRUE;
9537 game_panel_controls[GAME_PANEL_GEMS].value =
9538 local_player->gems_still_needed;
9540 DisplayGameControlValues();
9545 case CA_SET_LEVEL_WIND:
9547 game.wind_direction = action_arg_direction;
9552 case CA_SET_LEVEL_RANDOM_SEED:
9554 /* ensure that setting a new random seed while playing is predictable */
9555 InitRND(action_arg_number_new ? action_arg_number_new : RND(1000000) + 1);
9560 /* ---------- player actions ------------------------------------------ */
9562 case CA_MOVE_PLAYER:
9564 /* automatically move to the next field in specified direction */
9565 for (i = 0; i < MAX_PLAYERS; i++)
9566 if (trigger_player_bits & (1 << i))
9567 stored_player[i].programmed_action = action_arg_direction;
9572 case CA_EXIT_PLAYER:
9574 for (i = 0; i < MAX_PLAYERS; i++)
9575 if (action_arg_player_bits & (1 << i))
9576 PlayerWins(&stored_player[i]);
9581 case CA_KILL_PLAYER:
9583 for (i = 0; i < MAX_PLAYERS; i++)
9584 if (action_arg_player_bits & (1 << i))
9585 KillPlayer(&stored_player[i]);
9590 case CA_SET_PLAYER_KEYS:
9592 int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
9593 int element = getSpecialActionElement(action_arg_element,
9594 action_arg_number, EL_KEY_1);
9596 if (IS_KEY(element))
9598 for (i = 0; i < MAX_PLAYERS; i++)
9600 if (trigger_player_bits & (1 << i))
9602 stored_player[i].key[KEY_NR(element)] = key_state;
9604 DrawGameDoorValues();
9612 case CA_SET_PLAYER_SPEED:
9614 for (i = 0; i < MAX_PLAYERS; i++)
9616 if (trigger_player_bits & (1 << i))
9618 int move_stepsize = TILEX / stored_player[i].move_delay_value;
9620 if (action_arg == CA_ARG_SPEED_FASTER &&
9621 stored_player[i].cannot_move)
9623 action_arg_number = STEPSIZE_VERY_SLOW;
9625 else if (action_arg == CA_ARG_SPEED_SLOWER ||
9626 action_arg == CA_ARG_SPEED_FASTER)
9628 action_arg_number = 2;
9629 action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
9632 else if (action_arg == CA_ARG_NUMBER_RESET)
9634 action_arg_number = level.initial_player_stepsize[i];
9638 getModifiedActionNumber(move_stepsize,
9641 action_arg_number_min,
9642 action_arg_number_max);
9644 SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
9651 case CA_SET_PLAYER_SHIELD:
9653 for (i = 0; i < MAX_PLAYERS; i++)
9655 if (trigger_player_bits & (1 << i))
9657 if (action_arg == CA_ARG_SHIELD_OFF)
9659 stored_player[i].shield_normal_time_left = 0;
9660 stored_player[i].shield_deadly_time_left = 0;
9662 else if (action_arg == CA_ARG_SHIELD_NORMAL)
9664 stored_player[i].shield_normal_time_left = 999999;
9666 else if (action_arg == CA_ARG_SHIELD_DEADLY)
9668 stored_player[i].shield_normal_time_left = 999999;
9669 stored_player[i].shield_deadly_time_left = 999999;
9677 case CA_SET_PLAYER_GRAVITY:
9679 for (i = 0; i < MAX_PLAYERS; i++)
9681 if (trigger_player_bits & (1 << i))
9683 stored_player[i].gravity =
9684 (action_arg == CA_ARG_GRAVITY_OFF ? FALSE :
9685 action_arg == CA_ARG_GRAVITY_ON ? TRUE :
9686 action_arg == CA_ARG_GRAVITY_TOGGLE ? !stored_player[i].gravity :
9687 stored_player[i].gravity);
9694 case CA_SET_PLAYER_ARTWORK:
9696 for (i = 0; i < MAX_PLAYERS; i++)
9698 if (trigger_player_bits & (1 << i))
9700 int artwork_element = action_arg_element;
9702 if (action_arg == CA_ARG_ELEMENT_RESET)
9704 (level.use_artwork_element[i] ? level.artwork_element[i] :
9705 stored_player[i].element_nr);
9707 if (stored_player[i].artwork_element != artwork_element)
9708 stored_player[i].Frame = 0;
9710 stored_player[i].artwork_element = artwork_element;
9712 SetPlayerWaiting(&stored_player[i], FALSE);
9714 /* set number of special actions for bored and sleeping animation */
9715 stored_player[i].num_special_action_bored =
9716 get_num_special_action(artwork_element,
9717 ACTION_BORING_1, ACTION_BORING_LAST);
9718 stored_player[i].num_special_action_sleeping =
9719 get_num_special_action(artwork_element,
9720 ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
9727 case CA_SET_PLAYER_INVENTORY:
9729 for (i = 0; i < MAX_PLAYERS; i++)
9731 struct PlayerInfo *player = &stored_player[i];
9734 if (trigger_player_bits & (1 << i))
9736 int inventory_element = action_arg_element;
9738 if (action_arg == CA_ARG_ELEMENT_TARGET ||
9739 action_arg == CA_ARG_ELEMENT_TRIGGER ||
9740 action_arg == CA_ARG_ELEMENT_ACTION)
9742 int element = inventory_element;
9743 int collect_count = element_info[element].collect_count_initial;
9745 if (!IS_CUSTOM_ELEMENT(element))
9748 if (collect_count == 0)
9749 player->inventory_infinite_element = element;
9751 for (k = 0; k < collect_count; k++)
9752 if (player->inventory_size < MAX_INVENTORY_SIZE)
9753 player->inventory_element[player->inventory_size++] =
9756 else if (action_arg == CA_ARG_INVENTORY_RM_TARGET ||
9757 action_arg == CA_ARG_INVENTORY_RM_TRIGGER ||
9758 action_arg == CA_ARG_INVENTORY_RM_ACTION)
9760 if (player->inventory_infinite_element != EL_UNDEFINED &&
9761 IS_EQUAL_OR_IN_GROUP(player->inventory_infinite_element,
9762 action_arg_element_raw))
9763 player->inventory_infinite_element = EL_UNDEFINED;
9765 for (k = 0, j = 0; j < player->inventory_size; j++)
9767 if (!IS_EQUAL_OR_IN_GROUP(player->inventory_element[j],
9768 action_arg_element_raw))
9769 player->inventory_element[k++] = player->inventory_element[j];
9772 player->inventory_size = k;
9774 else if (action_arg == CA_ARG_INVENTORY_RM_FIRST)
9776 if (player->inventory_size > 0)
9778 for (j = 0; j < player->inventory_size - 1; j++)
9779 player->inventory_element[j] = player->inventory_element[j + 1];
9781 player->inventory_size--;
9784 else if (action_arg == CA_ARG_INVENTORY_RM_LAST)
9786 if (player->inventory_size > 0)
9787 player->inventory_size--;
9789 else if (action_arg == CA_ARG_INVENTORY_RM_ALL)
9791 player->inventory_infinite_element = EL_UNDEFINED;
9792 player->inventory_size = 0;
9794 else if (action_arg == CA_ARG_INVENTORY_RESET)
9796 player->inventory_infinite_element = EL_UNDEFINED;
9797 player->inventory_size = 0;
9799 if (level.use_initial_inventory[i])
9801 for (j = 0; j < level.initial_inventory_size[i]; j++)
9803 int element = level.initial_inventory_content[i][j];
9804 int collect_count = element_info[element].collect_count_initial;
9806 if (!IS_CUSTOM_ELEMENT(element))
9809 if (collect_count == 0)
9810 player->inventory_infinite_element = element;
9812 for (k = 0; k < collect_count; k++)
9813 if (player->inventory_size < MAX_INVENTORY_SIZE)
9814 player->inventory_element[player->inventory_size++] =
9825 /* ---------- CE actions ---------------------------------------------- */
9827 case CA_SET_CE_VALUE:
9829 int last_ce_value = CustomValue[x][y];
9831 CustomValue[x][y] = action_arg_number_new;
9833 if (CustomValue[x][y] != last_ce_value)
9835 CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_CHANGES);
9836 CheckTriggeredElementChange(x, y, element, CE_VALUE_CHANGES_OF_X);
9838 if (CustomValue[x][y] == 0)
9840 CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
9841 CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
9848 case CA_SET_CE_SCORE:
9850 int last_ce_score = ei->collect_score;
9852 ei->collect_score = action_arg_number_new;
9854 if (ei->collect_score != last_ce_score)
9856 CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
9857 CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
9859 if (ei->collect_score == 0)
9863 CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
9864 CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
9867 This is a very special case that seems to be a mixture between
9868 CheckElementChange() and CheckTriggeredElementChange(): while
9869 the first one only affects single elements that are triggered
9870 directly, the second one affects multiple elements in the playfield
9871 that are triggered indirectly by another element. This is a third
9872 case: Changing the CE score always affects multiple identical CEs,
9873 so every affected CE must be checked, not only the single CE for
9874 which the CE score was changed in the first place (as every instance
9875 of that CE shares the same CE score, and therefore also can change)!
9877 SCAN_PLAYFIELD(xx, yy)
9879 if (Feld[xx][yy] == element)
9880 CheckElementChange(xx, yy, element, EL_UNDEFINED,
9881 CE_SCORE_GETS_ZERO);
9889 case CA_SET_CE_ARTWORK:
9891 int artwork_element = action_arg_element;
9892 boolean reset_frame = FALSE;
9895 if (action_arg == CA_ARG_ELEMENT_RESET)
9896 artwork_element = (ei->use_gfx_element ? ei->gfx_element_initial :
9899 if (ei->gfx_element != artwork_element)
9902 ei->gfx_element = artwork_element;
9904 SCAN_PLAYFIELD(xx, yy)
9906 if (Feld[xx][yy] == element)
9910 ResetGfxAnimation(xx, yy);
9911 ResetRandomAnimationValue(xx, yy);
9914 TEST_DrawLevelField(xx, yy);
9921 /* ---------- engine actions ------------------------------------------ */
9923 case CA_SET_ENGINE_SCAN_MODE:
9925 InitPlayfieldScanMode(action_arg);
9935 static void CreateFieldExt(int x, int y, int element, boolean is_change)
9937 int old_element = Feld[x][y];
9938 int new_element = GetElementFromGroupElement(element);
9939 int previous_move_direction = MovDir[x][y];
9940 int last_ce_value = CustomValue[x][y];
9941 boolean player_explosion_protected = PLAYER_EXPLOSION_PROTECTED(x, y);
9942 boolean new_element_is_player = ELEM_IS_PLAYER(new_element);
9943 boolean add_player_onto_element = (new_element_is_player &&
9944 new_element != EL_SOKOBAN_FIELD_PLAYER &&
9945 IS_WALKABLE(old_element));
9947 if (!add_player_onto_element)
9949 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
9950 RemoveMovingField(x, y);
9954 Feld[x][y] = new_element;
9956 if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
9957 MovDir[x][y] = previous_move_direction;
9959 if (element_info[new_element].use_last_ce_value)
9960 CustomValue[x][y] = last_ce_value;
9962 InitField_WithBug1(x, y, FALSE);
9964 new_element = Feld[x][y]; /* element may have changed */
9966 ResetGfxAnimation(x, y);
9967 ResetRandomAnimationValue(x, y);
9969 TEST_DrawLevelField(x, y);
9971 if (GFX_CRUMBLED(new_element))
9972 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
9975 /* check if element under the player changes from accessible to unaccessible
9976 (needed for special case of dropping element which then changes) */
9977 /* (must be checked after creating new element for walkable group elements) */
9978 if (IS_PLAYER(x, y) && !player_explosion_protected &&
9979 IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
9986 /* "ChangeCount" not set yet to allow "entered by player" change one time */
9987 if (new_element_is_player)
9988 RelocatePlayer(x, y, new_element);
9991 ChangeCount[x][y]++; /* count number of changes in the same frame */
9993 TestIfBadThingTouchesPlayer(x, y);
9994 TestIfPlayerTouchesCustomElement(x, y);
9995 TestIfElementTouchesCustomElement(x, y);
9998 static void CreateField(int x, int y, int element)
10000 CreateFieldExt(x, y, element, FALSE);
10003 static void CreateElementFromChange(int x, int y, int element)
10005 element = GET_VALID_RUNTIME_ELEMENT(element);
10007 if (game.engine_version >= VERSION_IDENT(3,2,0,7))
10009 int old_element = Feld[x][y];
10011 /* prevent changed element from moving in same engine frame
10012 unless both old and new element can either fall or move */
10013 if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
10014 (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
10018 CreateFieldExt(x, y, element, TRUE);
10021 static boolean ChangeElement(int x, int y, int element, int page)
10023 struct ElementInfo *ei = &element_info[element];
10024 struct ElementChangeInfo *change = &ei->change_page[page];
10025 int ce_value = CustomValue[x][y];
10026 int ce_score = ei->collect_score;
10027 int target_element;
10028 int old_element = Feld[x][y];
10030 /* always use default change event to prevent running into a loop */
10031 if (ChangeEvent[x][y] == -1)
10032 ChangeEvent[x][y] = CE_DELAY;
10034 if (ChangeEvent[x][y] == CE_DELAY)
10036 /* reset actual trigger element, trigger player and action element */
10037 change->actual_trigger_element = EL_EMPTY;
10038 change->actual_trigger_player = EL_EMPTY;
10039 change->actual_trigger_player_bits = CH_PLAYER_NONE;
10040 change->actual_trigger_side = CH_SIDE_NONE;
10041 change->actual_trigger_ce_value = 0;
10042 change->actual_trigger_ce_score = 0;
10045 /* do not change elements more than a specified maximum number of changes */
10046 if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
10049 ChangeCount[x][y]++; /* count number of changes in the same frame */
10051 if (change->explode)
10058 if (change->use_target_content)
10060 boolean complete_replace = TRUE;
10061 boolean can_replace[3][3];
10064 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10067 boolean is_walkable;
10068 boolean is_diggable;
10069 boolean is_collectible;
10070 boolean is_removable;
10071 boolean is_destructible;
10072 int ex = x + xx - 1;
10073 int ey = y + yy - 1;
10074 int content_element = change->target_content.e[xx][yy];
10077 can_replace[xx][yy] = TRUE;
10079 if (ex == x && ey == y) /* do not check changing element itself */
10082 if (content_element == EL_EMPTY_SPACE)
10084 can_replace[xx][yy] = FALSE; /* do not replace border with space */
10089 if (!IN_LEV_FIELD(ex, ey))
10091 can_replace[xx][yy] = FALSE;
10092 complete_replace = FALSE;
10099 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10100 e = MovingOrBlocked2Element(ex, ey);
10102 is_empty = (IS_FREE(ex, ey) ||
10103 (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
10105 is_walkable = (is_empty || IS_WALKABLE(e));
10106 is_diggable = (is_empty || IS_DIGGABLE(e));
10107 is_collectible = (is_empty || IS_COLLECTIBLE(e));
10108 is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
10109 is_removable = (is_diggable || is_collectible);
10111 can_replace[xx][yy] =
10112 (((change->replace_when == CP_WHEN_EMPTY && is_empty) ||
10113 (change->replace_when == CP_WHEN_WALKABLE && is_walkable) ||
10114 (change->replace_when == CP_WHEN_DIGGABLE && is_diggable) ||
10115 (change->replace_when == CP_WHEN_COLLECTIBLE && is_collectible) ||
10116 (change->replace_when == CP_WHEN_REMOVABLE && is_removable) ||
10117 (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
10118 !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
10120 if (!can_replace[xx][yy])
10121 complete_replace = FALSE;
10124 if (!change->only_if_complete || complete_replace)
10126 boolean something_has_changed = FALSE;
10128 if (change->only_if_complete && change->use_random_replace &&
10129 RND(100) < change->random_percentage)
10132 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10134 int ex = x + xx - 1;
10135 int ey = y + yy - 1;
10136 int content_element;
10138 if (can_replace[xx][yy] && (!change->use_random_replace ||
10139 RND(100) < change->random_percentage))
10141 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10142 RemoveMovingField(ex, ey);
10144 ChangeEvent[ex][ey] = ChangeEvent[x][y];
10146 content_element = change->target_content.e[xx][yy];
10147 target_element = GET_TARGET_ELEMENT(element, content_element, change,
10148 ce_value, ce_score);
10150 CreateElementFromChange(ex, ey, target_element);
10152 something_has_changed = TRUE;
10154 /* for symmetry reasons, freeze newly created border elements */
10155 if (ex != x || ey != y)
10156 Stop[ex][ey] = TRUE; /* no more moving in this frame */
10160 if (something_has_changed)
10162 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10163 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10169 target_element = GET_TARGET_ELEMENT(element, change->target_element, change,
10170 ce_value, ce_score);
10172 if (element == EL_DIAGONAL_GROWING ||
10173 element == EL_DIAGONAL_SHRINKING)
10175 target_element = Store[x][y];
10177 Store[x][y] = EL_EMPTY;
10180 CreateElementFromChange(x, y, target_element);
10182 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10183 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10186 /* this uses direct change before indirect change */
10187 CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
10192 static void HandleElementChange(int x, int y, int page)
10194 int element = MovingOrBlocked2Element(x, y);
10195 struct ElementInfo *ei = &element_info[element];
10196 struct ElementChangeInfo *change = &ei->change_page[page];
10197 boolean handle_action_before_change = FALSE;
10200 if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
10201 !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
10204 printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
10205 x, y, element, element_info[element].token_name);
10206 printf("HandleElementChange(): This should never happen!\n");
10211 /* this can happen with classic bombs on walkable, changing elements */
10212 if (!CAN_CHANGE_OR_HAS_ACTION(element))
10217 if (ChangeDelay[x][y] == 0) /* initialize element change */
10219 ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
10221 if (change->can_change)
10223 /* !!! not clear why graphic animation should be reset at all here !!! */
10224 /* !!! UPDATE: but is needed for correct Snake Bite tail animation !!! */
10225 /* !!! SOLUTION: do not reset if graphics engine set to 4 or above !!! */
10228 GRAPHICAL BUG ADDRESSED BY CHECKING GRAPHICS ENGINE VERSION:
10230 When using an animation frame delay of 1 (this only happens with
10231 "sp_zonk.moving.left/right" in the classic graphics), the default
10232 (non-moving) animation shows wrong animation frames (while the
10233 moving animation, like "sp_zonk.moving.left/right", is correct,
10234 so this graphical bug never shows up with the classic graphics).
10235 For an animation with 4 frames, this causes wrong frames 0,0,1,2
10236 be drawn instead of the correct frames 0,1,2,3. This is caused by
10237 "GfxFrame[][]" being reset *twice* (in two successive frames) after
10238 an element change: First when the change delay ("ChangeDelay[][]")
10239 counter has reached zero after decrementing, then a second time in
10240 the next frame (after "GfxFrame[][]" was already incremented) when
10241 "ChangeDelay[][]" is reset to the initial delay value again.
10243 This causes frame 0 to be drawn twice, while the last frame won't
10244 be drawn anymore, resulting in the wrong frame sequence 0,0,1,2.
10246 As some animations may already be cleverly designed around this bug
10247 (at least the "Snake Bite" snake tail animation does this), it cannot
10248 simply be fixed here without breaking such existing animations.
10249 Unfortunately, it cannot easily be detected if a graphics set was
10250 designed "before" or "after" the bug was fixed. As a workaround,
10251 a new graphics set option "game.graphics_engine_version" was added
10252 to be able to specify the game's major release version for which the
10253 graphics set was designed, which can then be used to decide if the
10254 bugfix should be used (version 4 and above) or not (version 3 or
10255 below, or if no version was specified at all, as with old sets).
10257 (The wrong/fixed animation frames can be tested with the test level set
10258 "test_gfxframe" and level "000", which contains a specially prepared
10259 custom element at level position (x/y) == (11/9) which uses the zonk
10260 animation mentioned above. Using "game.graphics_engine_version: 4"
10261 fixes the wrong animation frames, showing the correct frames 0,1,2,3.
10262 This can also be seen from the debug output for this test element.)
10265 /* when a custom element is about to change (for example by change delay),
10266 do not reset graphic animation when the custom element is moving */
10267 if (game.graphics_engine_version < 4 &&
10270 ResetGfxAnimation(x, y);
10271 ResetRandomAnimationValue(x, y);
10274 if (change->pre_change_function)
10275 change->pre_change_function(x, y);
10279 ChangeDelay[x][y]--;
10281 if (ChangeDelay[x][y] != 0) /* continue element change */
10283 if (change->can_change)
10285 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10287 if (IS_ANIMATED(graphic))
10288 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10290 if (change->change_function)
10291 change->change_function(x, y);
10294 else /* finish element change */
10296 if (ChangePage[x][y] != -1) /* remember page from delayed change */
10298 page = ChangePage[x][y];
10299 ChangePage[x][y] = -1;
10301 change = &ei->change_page[page];
10304 if (IS_MOVING(x, y)) /* never change a running system ;-) */
10306 ChangeDelay[x][y] = 1; /* try change after next move step */
10307 ChangePage[x][y] = page; /* remember page to use for change */
10312 /* special case: set new level random seed before changing element */
10313 if (change->has_action && change->action_type == CA_SET_LEVEL_RANDOM_SEED)
10314 handle_action_before_change = TRUE;
10316 if (change->has_action && handle_action_before_change)
10317 ExecuteCustomElementAction(x, y, element, page);
10319 if (change->can_change)
10321 if (ChangeElement(x, y, element, page))
10323 if (change->post_change_function)
10324 change->post_change_function(x, y);
10328 if (change->has_action && !handle_action_before_change)
10329 ExecuteCustomElementAction(x, y, element, page);
10333 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
10334 int trigger_element,
10336 int trigger_player,
10340 boolean change_done_any = FALSE;
10341 int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
10344 if (!(trigger_events[trigger_element][trigger_event]))
10347 RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
10349 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
10351 int element = EL_CUSTOM_START + i;
10352 boolean change_done = FALSE;
10355 if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
10356 !HAS_ANY_CHANGE_EVENT(element, trigger_event))
10359 for (p = 0; p < element_info[element].num_change_pages; p++)
10361 struct ElementChangeInfo *change = &element_info[element].change_page[p];
10363 if (change->can_change_or_has_action &&
10364 change->has_event[trigger_event] &&
10365 change->trigger_side & trigger_side &&
10366 change->trigger_player & trigger_player &&
10367 change->trigger_page & trigger_page_bits &&
10368 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
10370 change->actual_trigger_element = trigger_element;
10371 change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
10372 change->actual_trigger_player_bits = trigger_player;
10373 change->actual_trigger_side = trigger_side;
10374 change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
10375 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10377 if ((change->can_change && !change_done) || change->has_action)
10381 SCAN_PLAYFIELD(x, y)
10383 if (Feld[x][y] == element)
10385 if (change->can_change && !change_done)
10387 /* if element already changed in this frame, not only prevent
10388 another element change (checked in ChangeElement()), but
10389 also prevent additional element actions for this element */
10391 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
10392 !level.use_action_after_change_bug)
10395 ChangeDelay[x][y] = 1;
10396 ChangeEvent[x][y] = trigger_event;
10398 HandleElementChange(x, y, p);
10400 else if (change->has_action)
10402 /* if element already changed in this frame, not only prevent
10403 another element change (checked in ChangeElement()), but
10404 also prevent additional element actions for this element */
10406 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
10407 !level.use_action_after_change_bug)
10410 ExecuteCustomElementAction(x, y, element, p);
10411 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10416 if (change->can_change)
10418 change_done = TRUE;
10419 change_done_any = TRUE;
10426 RECURSION_LOOP_DETECTION_END();
10428 return change_done_any;
10431 static boolean CheckElementChangeExt(int x, int y,
10433 int trigger_element,
10435 int trigger_player,
10438 boolean change_done = FALSE;
10441 if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
10442 !HAS_ANY_CHANGE_EVENT(element, trigger_event))
10445 if (Feld[x][y] == EL_BLOCKED)
10447 Blocked2Moving(x, y, &x, &y);
10448 element = Feld[x][y];
10451 /* check if element has already changed or is about to change after moving */
10452 if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
10453 Feld[x][y] != element) ||
10455 (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
10456 (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
10457 ChangePage[x][y] != -1)))
10460 RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
10462 for (p = 0; p < element_info[element].num_change_pages; p++)
10464 struct ElementChangeInfo *change = &element_info[element].change_page[p];
10466 /* check trigger element for all events where the element that is checked
10467 for changing interacts with a directly adjacent element -- this is
10468 different to element changes that affect other elements to change on the
10469 whole playfield (which is handeld by CheckTriggeredElementChangeExt()) */
10470 boolean check_trigger_element =
10471 (trigger_event == CE_TOUCHING_X ||
10472 trigger_event == CE_HITTING_X ||
10473 trigger_event == CE_HIT_BY_X ||
10474 trigger_event == CE_DIGGING_X); /* this one was forgotten until 3.2.3 */
10476 if (change->can_change_or_has_action &&
10477 change->has_event[trigger_event] &&
10478 change->trigger_side & trigger_side &&
10479 change->trigger_player & trigger_player &&
10480 (!check_trigger_element ||
10481 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
10483 change->actual_trigger_element = trigger_element;
10484 change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
10485 change->actual_trigger_player_bits = trigger_player;
10486 change->actual_trigger_side = trigger_side;
10487 change->actual_trigger_ce_value = CustomValue[x][y];
10488 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10490 /* special case: trigger element not at (x,y) position for some events */
10491 if (check_trigger_element)
10503 { 0, 0 }, { 0, 0 }, { 0, 0 },
10507 int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
10508 int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
10510 change->actual_trigger_ce_value = CustomValue[xx][yy];
10511 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10514 if (change->can_change && !change_done)
10516 ChangeDelay[x][y] = 1;
10517 ChangeEvent[x][y] = trigger_event;
10519 HandleElementChange(x, y, p);
10521 change_done = TRUE;
10523 else if (change->has_action)
10525 ExecuteCustomElementAction(x, y, element, p);
10526 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10531 RECURSION_LOOP_DETECTION_END();
10533 return change_done;
10536 static void PlayPlayerSound(struct PlayerInfo *player)
10538 int jx = player->jx, jy = player->jy;
10539 int sound_element = player->artwork_element;
10540 int last_action = player->last_action_waiting;
10541 int action = player->action_waiting;
10543 if (player->is_waiting)
10545 if (action != last_action)
10546 PlayLevelSoundElementAction(jx, jy, sound_element, action);
10548 PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
10552 if (action != last_action)
10553 StopSound(element_info[sound_element].sound[last_action]);
10555 if (last_action == ACTION_SLEEPING)
10556 PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
10560 static void PlayAllPlayersSound()
10564 for (i = 0; i < MAX_PLAYERS; i++)
10565 if (stored_player[i].active)
10566 PlayPlayerSound(&stored_player[i]);
10569 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
10571 boolean last_waiting = player->is_waiting;
10572 int move_dir = player->MovDir;
10574 player->dir_waiting = move_dir;
10575 player->last_action_waiting = player->action_waiting;
10579 if (!last_waiting) /* not waiting -> waiting */
10581 player->is_waiting = TRUE;
10583 player->frame_counter_bored =
10585 game.player_boring_delay_fixed +
10586 GetSimpleRandom(game.player_boring_delay_random);
10587 player->frame_counter_sleeping =
10589 game.player_sleeping_delay_fixed +
10590 GetSimpleRandom(game.player_sleeping_delay_random);
10592 InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
10595 if (game.player_sleeping_delay_fixed +
10596 game.player_sleeping_delay_random > 0 &&
10597 player->anim_delay_counter == 0 &&
10598 player->post_delay_counter == 0 &&
10599 FrameCounter >= player->frame_counter_sleeping)
10600 player->is_sleeping = TRUE;
10601 else if (game.player_boring_delay_fixed +
10602 game.player_boring_delay_random > 0 &&
10603 FrameCounter >= player->frame_counter_bored)
10604 player->is_bored = TRUE;
10606 player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
10607 player->is_bored ? ACTION_BORING :
10610 if (player->is_sleeping && player->use_murphy)
10612 /* special case for sleeping Murphy when leaning against non-free tile */
10614 if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
10615 (Feld[player->jx - 1][player->jy] != EL_EMPTY &&
10616 !IS_MOVING(player->jx - 1, player->jy)))
10617 move_dir = MV_LEFT;
10618 else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
10619 (Feld[player->jx + 1][player->jy] != EL_EMPTY &&
10620 !IS_MOVING(player->jx + 1, player->jy)))
10621 move_dir = MV_RIGHT;
10623 player->is_sleeping = FALSE;
10625 player->dir_waiting = move_dir;
10628 if (player->is_sleeping)
10630 if (player->num_special_action_sleeping > 0)
10632 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
10634 int last_special_action = player->special_action_sleeping;
10635 int num_special_action = player->num_special_action_sleeping;
10636 int special_action =
10637 (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
10638 last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
10639 last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
10640 last_special_action + 1 : ACTION_SLEEPING);
10641 int special_graphic =
10642 el_act_dir2img(player->artwork_element, special_action, move_dir);
10644 player->anim_delay_counter =
10645 graphic_info[special_graphic].anim_delay_fixed +
10646 GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
10647 player->post_delay_counter =
10648 graphic_info[special_graphic].post_delay_fixed +
10649 GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
10651 player->special_action_sleeping = special_action;
10654 if (player->anim_delay_counter > 0)
10656 player->action_waiting = player->special_action_sleeping;
10657 player->anim_delay_counter--;
10659 else if (player->post_delay_counter > 0)
10661 player->post_delay_counter--;
10665 else if (player->is_bored)
10667 if (player->num_special_action_bored > 0)
10669 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
10671 int special_action =
10672 ACTION_BORING_1 + GetSimpleRandom(player->num_special_action_bored);
10673 int special_graphic =
10674 el_act_dir2img(player->artwork_element, special_action, move_dir);
10676 player->anim_delay_counter =
10677 graphic_info[special_graphic].anim_delay_fixed +
10678 GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
10679 player->post_delay_counter =
10680 graphic_info[special_graphic].post_delay_fixed +
10681 GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
10683 player->special_action_bored = special_action;
10686 if (player->anim_delay_counter > 0)
10688 player->action_waiting = player->special_action_bored;
10689 player->anim_delay_counter--;
10691 else if (player->post_delay_counter > 0)
10693 player->post_delay_counter--;
10698 else if (last_waiting) /* waiting -> not waiting */
10700 player->is_waiting = FALSE;
10701 player->is_bored = FALSE;
10702 player->is_sleeping = FALSE;
10704 player->frame_counter_bored = -1;
10705 player->frame_counter_sleeping = -1;
10707 player->anim_delay_counter = 0;
10708 player->post_delay_counter = 0;
10710 player->dir_waiting = player->MovDir;
10711 player->action_waiting = ACTION_DEFAULT;
10713 player->special_action_bored = ACTION_DEFAULT;
10714 player->special_action_sleeping = ACTION_DEFAULT;
10718 static void CheckSaveEngineSnapshot(struct PlayerInfo *player)
10720 if ((!player->is_moving && player->was_moving) ||
10721 (player->MovPos == 0 && player->was_moving) ||
10722 (player->is_snapping && !player->was_snapping) ||
10723 (player->is_dropping && !player->was_dropping))
10725 if (!CheckSaveEngineSnapshotToList())
10728 player->was_moving = FALSE;
10729 player->was_snapping = TRUE;
10730 player->was_dropping = TRUE;
10734 if (player->is_moving)
10735 player->was_moving = TRUE;
10737 if (!player->is_snapping)
10738 player->was_snapping = FALSE;
10740 if (!player->is_dropping)
10741 player->was_dropping = FALSE;
10745 static void CheckSingleStepMode(struct PlayerInfo *player)
10747 if (tape.single_step && tape.recording && !tape.pausing)
10749 /* as it is called "single step mode", just return to pause mode when the
10750 player stopped moving after one tile (or never starts moving at all) */
10751 if (!player->is_moving &&
10752 !player->is_pushing &&
10753 !player->is_dropping_pressed)
10755 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
10756 SnapField(player, 0, 0); /* stop snapping */
10760 CheckSaveEngineSnapshot(player);
10763 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
10765 int left = player_action & JOY_LEFT;
10766 int right = player_action & JOY_RIGHT;
10767 int up = player_action & JOY_UP;
10768 int down = player_action & JOY_DOWN;
10769 int button1 = player_action & JOY_BUTTON_1;
10770 int button2 = player_action & JOY_BUTTON_2;
10771 int dx = (left ? -1 : right ? 1 : 0);
10772 int dy = (up ? -1 : down ? 1 : 0);
10774 if (!player->active || tape.pausing)
10780 SnapField(player, dx, dy);
10784 DropElement(player);
10786 MovePlayer(player, dx, dy);
10789 CheckSingleStepMode(player);
10791 SetPlayerWaiting(player, FALSE);
10793 return player_action;
10797 /* no actions for this player (no input at player's configured device) */
10799 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
10800 SnapField(player, 0, 0);
10801 CheckGravityMovementWhenNotMoving(player);
10803 if (player->MovPos == 0)
10804 SetPlayerWaiting(player, TRUE);
10806 if (player->MovPos == 0) /* needed for tape.playing */
10807 player->is_moving = FALSE;
10809 player->is_dropping = FALSE;
10810 player->is_dropping_pressed = FALSE;
10811 player->drop_pressed_delay = 0;
10813 CheckSingleStepMode(player);
10819 static void CheckLevelTime()
10823 /* !!! SAME CODE AS IN "GameActions()" -- FIX THIS !!! */
10824 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
10826 if (level.native_em_level->lev->home == 0) /* all players at home */
10828 PlayerWins(local_player);
10830 AllPlayersGone = TRUE;
10832 level.native_em_level->lev->home = -1;
10835 if (level.native_em_level->ply[0]->alive == 0 &&
10836 level.native_em_level->ply[1]->alive == 0 &&
10837 level.native_em_level->ply[2]->alive == 0 &&
10838 level.native_em_level->ply[3]->alive == 0) /* all dead */
10839 AllPlayersGone = TRUE;
10841 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
10843 if (game_sp.LevelSolved &&
10844 !game_sp.GameOver) /* game won */
10846 PlayerWins(local_player);
10848 game_sp.GameOver = TRUE;
10850 AllPlayersGone = TRUE;
10853 if (game_sp.GameOver) /* game lost */
10854 AllPlayersGone = TRUE;
10856 else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
10858 if (game_mm.level_solved &&
10859 !game_mm.game_over) /* game won */
10861 PlayerWins(local_player);
10863 game_mm.game_over = TRUE;
10865 AllPlayersGone = TRUE;
10868 if (game_mm.game_over) /* game lost */
10869 AllPlayersGone = TRUE;
10872 if (TimeFrames >= FRAMES_PER_SECOND)
10877 for (i = 0; i < MAX_PLAYERS; i++)
10879 struct PlayerInfo *player = &stored_player[i];
10881 if (SHIELD_ON(player))
10883 player->shield_normal_time_left--;
10885 if (player->shield_deadly_time_left > 0)
10886 player->shield_deadly_time_left--;
10890 if (!local_player->LevelSolved && !level.use_step_counter)
10898 if (TimeLeft <= 10 && setup.time_limit)
10899 PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
10901 /* this does not make sense: game_panel_controls[GAME_PANEL_TIME].value
10902 is reset from other values in UpdateGameDoorValues() -- FIX THIS */
10904 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
10906 if (!TimeLeft && setup.time_limit)
10908 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
10909 level.native_em_level->lev->killed_out_of_time = TRUE;
10911 for (i = 0; i < MAX_PLAYERS; i++)
10912 KillPlayer(&stored_player[i]);
10915 else if (game.no_time_limit && !AllPlayersGone) /* level w/o time limit */
10917 game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
10920 level.native_em_level->lev->time =
10921 (game.no_time_limit ? TimePlayed : TimeLeft);
10924 if (tape.recording || tape.playing)
10925 DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
10928 if (tape.recording || tape.playing)
10929 DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
10931 UpdateAndDisplayGameControlValues();
10934 void AdvanceFrameAndPlayerCounters(int player_nr)
10938 /* advance frame counters (global frame counter and time frame counter) */
10942 /* advance player counters (counters for move delay, move animation etc.) */
10943 for (i = 0; i < MAX_PLAYERS; i++)
10945 boolean advance_player_counters = (player_nr == -1 || player_nr == i);
10946 int move_delay_value = stored_player[i].move_delay_value;
10947 int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
10949 if (!advance_player_counters) /* not all players may be affected */
10952 if (move_frames == 0) /* less than one move per game frame */
10954 int stepsize = TILEX / move_delay_value;
10955 int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
10956 int count = (stored_player[i].is_moving ?
10957 ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
10959 if (count % delay == 0)
10963 stored_player[i].Frame += move_frames;
10965 if (stored_player[i].MovPos != 0)
10966 stored_player[i].StepFrame += move_frames;
10968 if (stored_player[i].move_delay > 0)
10969 stored_player[i].move_delay--;
10971 /* due to bugs in previous versions, counter must count up, not down */
10972 if (stored_player[i].push_delay != -1)
10973 stored_player[i].push_delay++;
10975 if (stored_player[i].drop_delay > 0)
10976 stored_player[i].drop_delay--;
10978 if (stored_player[i].is_dropping_pressed)
10979 stored_player[i].drop_pressed_delay++;
10983 void StartGameActions(boolean init_network_game, boolean record_tape,
10986 unsigned int new_random_seed = InitRND(random_seed);
10989 TapeStartRecording(new_random_seed);
10991 #if defined(NETWORK_AVALIABLE)
10992 if (init_network_game)
10994 SendToServer_StartPlaying();
11003 void GameActionsExt()
11006 static unsigned int game_frame_delay = 0;
11008 unsigned int game_frame_delay_value;
11009 byte *recorded_player_action;
11010 byte summarized_player_action = 0;
11011 byte tape_action[MAX_PLAYERS];
11014 /* detect endless loops, caused by custom element programming */
11015 if (recursion_loop_detected && recursion_loop_depth == 0)
11017 char *message = getStringCat3("Internal Error! Element ",
11018 EL_NAME(recursion_loop_element),
11019 " caused endless loop! Quit the game?");
11021 Error(ERR_WARN, "element '%s' caused endless loop in game engine",
11022 EL_NAME(recursion_loop_element));
11024 RequestQuitGameExt(FALSE, level_editor_test_game, message);
11026 recursion_loop_detected = FALSE; /* if game should be continued */
11033 if (game.restart_level)
11034 StartGameActions(options.network, setup.autorecord, level.random_seed);
11036 /* !!! SAME CODE AS IN "CheckLevelTime()" -- FIX THIS !!! */
11037 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11039 if (level.native_em_level->lev->home == 0) /* all players at home */
11041 PlayerWins(local_player);
11043 AllPlayersGone = TRUE;
11045 level.native_em_level->lev->home = -1;
11048 if (level.native_em_level->ply[0]->alive == 0 &&
11049 level.native_em_level->ply[1]->alive == 0 &&
11050 level.native_em_level->ply[2]->alive == 0 &&
11051 level.native_em_level->ply[3]->alive == 0) /* all dead */
11052 AllPlayersGone = TRUE;
11054 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11056 if (game_sp.LevelSolved &&
11057 !game_sp.GameOver) /* game won */
11059 PlayerWins(local_player);
11061 game_sp.GameOver = TRUE;
11063 AllPlayersGone = TRUE;
11066 if (game_sp.GameOver) /* game lost */
11067 AllPlayersGone = TRUE;
11069 else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
11071 if (game_mm.level_solved &&
11072 !game_mm.game_over) /* game won */
11074 PlayerWins(local_player);
11076 game_mm.game_over = TRUE;
11078 AllPlayersGone = TRUE;
11081 if (game_mm.game_over) /* game lost */
11082 AllPlayersGone = TRUE;
11085 if (local_player->LevelSolved && !local_player->LevelSolved_GameEnd)
11088 if (AllPlayersGone && !TAPE_IS_STOPPED(tape))
11091 if (game_status != GAME_MODE_PLAYING) /* status might have changed */
11094 game_frame_delay_value =
11095 (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
11097 if (tape.playing && tape.warp_forward && !tape.pausing)
11098 game_frame_delay_value = 0;
11100 SetVideoFrameDelay(game_frame_delay_value);
11104 /* ---------- main game synchronization point ---------- */
11106 int skip = WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11108 printf("::: skip == %d\n", skip);
11111 /* ---------- main game synchronization point ---------- */
11113 WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11117 if (network_playing && !network_player_action_received)
11119 /* try to get network player actions in time */
11121 #if defined(NETWORK_AVALIABLE)
11122 /* last chance to get network player actions without main loop delay */
11123 HandleNetworking();
11126 /* game was quit by network peer */
11127 if (game_status != GAME_MODE_PLAYING)
11130 if (!network_player_action_received)
11131 return; /* failed to get network player actions in time */
11133 /* do not yet reset "network_player_action_received" (for tape.pausing) */
11139 /* at this point we know that we really continue executing the game */
11141 network_player_action_received = FALSE;
11143 /* when playing tape, read previously recorded player input from tape data */
11144 recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
11146 /* TapePlayAction() may return NULL when toggling to "pause before death" */
11150 if (tape.set_centered_player)
11152 game.centered_player_nr_next = tape.centered_player_nr_next;
11153 game.set_centered_player = TRUE;
11156 for (i = 0; i < MAX_PLAYERS; i++)
11158 summarized_player_action |= stored_player[i].action;
11160 if (!network_playing && (game.team_mode || tape.playing))
11161 stored_player[i].effective_action = stored_player[i].action;
11164 #if defined(NETWORK_AVALIABLE)
11165 if (network_playing)
11166 SendToServer_MovePlayer(summarized_player_action);
11169 // summarize all actions at local players mapped input device position
11170 // (this allows using different input devices in single player mode)
11171 if (!options.network && !game.team_mode)
11172 stored_player[map_player_action[local_player->index_nr]].effective_action =
11173 summarized_player_action;
11175 if (tape.recording &&
11177 setup.input_on_focus &&
11178 game.centered_player_nr != -1)
11180 for (i = 0; i < MAX_PLAYERS; i++)
11181 stored_player[i].effective_action =
11182 (i == game.centered_player_nr ? summarized_player_action : 0);
11185 if (recorded_player_action != NULL)
11186 for (i = 0; i < MAX_PLAYERS; i++)
11187 stored_player[i].effective_action = recorded_player_action[i];
11189 for (i = 0; i < MAX_PLAYERS; i++)
11191 tape_action[i] = stored_player[i].effective_action;
11193 /* (this may happen in the RND game engine if a player was not present on
11194 the playfield on level start, but appeared later from a custom element */
11195 if (setup.team_mode &&
11198 !tape.player_participates[i])
11199 tape.player_participates[i] = TRUE;
11202 /* only record actions from input devices, but not programmed actions */
11203 if (tape.recording)
11204 TapeRecordAction(tape_action);
11206 #if USE_NEW_PLAYER_ASSIGNMENTS
11207 // !!! also map player actions in single player mode !!!
11208 // if (game.team_mode)
11211 byte mapped_action[MAX_PLAYERS];
11213 #if DEBUG_PLAYER_ACTIONS
11215 for (i = 0; i < MAX_PLAYERS; i++)
11216 printf(" %d, ", stored_player[i].effective_action);
11219 for (i = 0; i < MAX_PLAYERS; i++)
11220 mapped_action[i] = stored_player[map_player_action[i]].effective_action;
11222 for (i = 0; i < MAX_PLAYERS; i++)
11223 stored_player[i].effective_action = mapped_action[i];
11225 #if DEBUG_PLAYER_ACTIONS
11227 for (i = 0; i < MAX_PLAYERS; i++)
11228 printf(" %d, ", stored_player[i].effective_action);
11232 #if DEBUG_PLAYER_ACTIONS
11236 for (i = 0; i < MAX_PLAYERS; i++)
11237 printf(" %d, ", stored_player[i].effective_action);
11243 for (i = 0; i < MAX_PLAYERS; i++)
11245 // allow engine snapshot in case of changed movement attempt
11246 if ((game.snapshot.last_action[i] & KEY_MOTION) !=
11247 (stored_player[i].effective_action & KEY_MOTION))
11248 game.snapshot.changed_action = TRUE;
11250 // allow engine snapshot in case of snapping/dropping attempt
11251 if ((game.snapshot.last_action[i] & KEY_BUTTON) == 0 &&
11252 (stored_player[i].effective_action & KEY_BUTTON) != 0)
11253 game.snapshot.changed_action = TRUE;
11255 game.snapshot.last_action[i] = stored_player[i].effective_action;
11258 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11260 GameActions_EM_Main();
11262 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11264 GameActions_SP_Main();
11266 else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
11268 GameActions_MM_Main();
11272 GameActions_RND_Main();
11275 BlitScreenToBitmap(backbuffer);
11279 AdvanceFrameAndPlayerCounters(-1); /* advance counters for all players */
11281 if (global.show_frames_per_second)
11283 static unsigned int fps_counter = 0;
11284 static int fps_frames = 0;
11285 unsigned int fps_delay_ms = Counter() - fps_counter;
11289 if (fps_delay_ms >= 500) /* calculate FPS every 0.5 seconds */
11291 global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
11294 fps_counter = Counter();
11296 /* always draw FPS to screen after FPS value was updated */
11297 redraw_mask |= REDRAW_FPS;
11300 /* only draw FPS if no screen areas are deactivated (invisible warp mode) */
11301 if (GetDrawDeactivationMask() == REDRAW_NONE)
11302 redraw_mask |= REDRAW_FPS;
11306 static void GameActions_CheckSaveEngineSnapshot()
11308 if (!game.snapshot.save_snapshot)
11311 // clear flag for saving snapshot _before_ saving snapshot
11312 game.snapshot.save_snapshot = FALSE;
11314 SaveEngineSnapshotToList();
11321 GameActions_CheckSaveEngineSnapshot();
11324 void GameActions_EM_Main()
11326 byte effective_action[MAX_PLAYERS];
11327 boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11330 for (i = 0; i < MAX_PLAYERS; i++)
11331 effective_action[i] = stored_player[i].effective_action;
11333 GameActions_EM(effective_action, warp_mode);
11336 void GameActions_SP_Main()
11338 byte effective_action[MAX_PLAYERS];
11339 boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11342 for (i = 0; i < MAX_PLAYERS; i++)
11343 effective_action[i] = stored_player[i].effective_action;
11345 GameActions_SP(effective_action, warp_mode);
11347 for (i = 0; i < MAX_PLAYERS; i++)
11349 if (stored_player[i].force_dropping)
11350 stored_player[i].action |= KEY_BUTTON_DROP;
11352 stored_player[i].force_dropping = FALSE;
11356 void GameActions_MM_Main()
11358 byte effective_action[MAX_PLAYERS];
11359 boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11362 for (i = 0; i < MAX_PLAYERS; i++)
11363 effective_action[i] = stored_player[i].effective_action;
11365 GameActions_MM(effective_action, warp_mode);
11368 void GameActions_RND_Main()
11373 void GameActions_RND()
11375 int magic_wall_x = 0, magic_wall_y = 0;
11376 int i, x, y, element, graphic, last_gfx_frame;
11378 InitPlayfieldScanModeVars();
11380 if (game.engine_version >= VERSION_IDENT(3,2,0,7))
11382 SCAN_PLAYFIELD(x, y)
11384 ChangeCount[x][y] = 0;
11385 ChangeEvent[x][y] = -1;
11389 if (game.set_centered_player)
11391 boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
11393 /* switching to "all players" only possible if all players fit to screen */
11394 if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
11396 game.centered_player_nr_next = game.centered_player_nr;
11397 game.set_centered_player = FALSE;
11400 /* do not switch focus to non-existing (or non-active) player */
11401 if (game.centered_player_nr_next >= 0 &&
11402 !stored_player[game.centered_player_nr_next].active)
11404 game.centered_player_nr_next = game.centered_player_nr;
11405 game.set_centered_player = FALSE;
11409 if (game.set_centered_player &&
11410 ScreenMovPos == 0) /* screen currently aligned at tile position */
11414 if (game.centered_player_nr_next == -1)
11416 setScreenCenteredToAllPlayers(&sx, &sy);
11420 sx = stored_player[game.centered_player_nr_next].jx;
11421 sy = stored_player[game.centered_player_nr_next].jy;
11424 game.centered_player_nr = game.centered_player_nr_next;
11425 game.set_centered_player = FALSE;
11427 DrawRelocateScreen(0, 0, sx, sy, MV_NONE, TRUE, setup.quick_switch);
11428 DrawGameDoorValues();
11431 for (i = 0; i < MAX_PLAYERS; i++)
11433 int actual_player_action = stored_player[i].effective_action;
11436 /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
11437 - rnd_equinox_tetrachloride 048
11438 - rnd_equinox_tetrachloride_ii 096
11439 - rnd_emanuel_schmieg 002
11440 - doctor_sloan_ww 001, 020
11442 if (stored_player[i].MovPos == 0)
11443 CheckGravityMovement(&stored_player[i]);
11446 /* overwrite programmed action with tape action */
11447 if (stored_player[i].programmed_action)
11448 actual_player_action = stored_player[i].programmed_action;
11450 PlayerActions(&stored_player[i], actual_player_action);
11452 ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
11455 ScrollScreen(NULL, SCROLL_GO_ON);
11457 /* for backwards compatibility, the following code emulates a fixed bug that
11458 occured when pushing elements (causing elements that just made their last
11459 pushing step to already (if possible) make their first falling step in the
11460 same game frame, which is bad); this code is also needed to use the famous
11461 "spring push bug" which is used in older levels and might be wanted to be
11462 used also in newer levels, but in this case the buggy pushing code is only
11463 affecting the "spring" element and no other elements */
11465 if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
11467 for (i = 0; i < MAX_PLAYERS; i++)
11469 struct PlayerInfo *player = &stored_player[i];
11470 int x = player->jx;
11471 int y = player->jy;
11473 if (player->active && player->is_pushing && player->is_moving &&
11475 (game.engine_version < VERSION_IDENT(2,2,0,7) ||
11476 Feld[x][y] == EL_SPRING))
11478 ContinueMoving(x, y);
11480 /* continue moving after pushing (this is actually a bug) */
11481 if (!IS_MOVING(x, y))
11482 Stop[x][y] = FALSE;
11487 SCAN_PLAYFIELD(x, y)
11489 ChangeCount[x][y] = 0;
11490 ChangeEvent[x][y] = -1;
11492 /* this must be handled before main playfield loop */
11493 if (Feld[x][y] == EL_PLAYER_IS_LEAVING)
11496 if (MovDelay[x][y] <= 0)
11500 if (Feld[x][y] == EL_ELEMENT_SNAPPING)
11503 if (MovDelay[x][y] <= 0)
11506 TEST_DrawLevelField(x, y);
11508 TestIfElementTouchesCustomElement(x, y); /* for empty space */
11513 if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
11515 printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
11516 printf("GameActions(): This should never happen!\n");
11518 ChangePage[x][y] = -1;
11522 Stop[x][y] = FALSE;
11523 if (WasJustMoving[x][y] > 0)
11524 WasJustMoving[x][y]--;
11525 if (WasJustFalling[x][y] > 0)
11526 WasJustFalling[x][y]--;
11527 if (CheckCollision[x][y] > 0)
11528 CheckCollision[x][y]--;
11529 if (CheckImpact[x][y] > 0)
11530 CheckImpact[x][y]--;
11534 /* reset finished pushing action (not done in ContinueMoving() to allow
11535 continuous pushing animation for elements with zero push delay) */
11536 if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
11538 ResetGfxAnimation(x, y);
11539 TEST_DrawLevelField(x, y);
11543 if (IS_BLOCKED(x, y))
11547 Blocked2Moving(x, y, &oldx, &oldy);
11548 if (!IS_MOVING(oldx, oldy))
11550 printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
11551 printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
11552 printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
11553 printf("GameActions(): This should never happen!\n");
11559 SCAN_PLAYFIELD(x, y)
11561 element = Feld[x][y];
11562 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11563 last_gfx_frame = GfxFrame[x][y];
11565 ResetGfxFrame(x, y);
11567 if (GfxFrame[x][y] != last_gfx_frame && !Stop[x][y])
11568 DrawLevelGraphicAnimation(x, y, graphic);
11570 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
11571 IS_NEXT_FRAME(GfxFrame[x][y], graphic))
11572 ResetRandomAnimationValue(x, y);
11574 SetRandomAnimationValue(x, y);
11576 PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
11578 if (IS_INACTIVE(element))
11580 if (IS_ANIMATED(graphic))
11581 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11586 /* this may take place after moving, so 'element' may have changed */
11587 if (IS_CHANGING(x, y) &&
11588 (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
11590 int page = element_info[element].event_page_nr[CE_DELAY];
11592 HandleElementChange(x, y, page);
11594 element = Feld[x][y];
11595 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11598 if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
11602 element = Feld[x][y];
11603 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11605 if (IS_ANIMATED(graphic) &&
11606 !IS_MOVING(x, y) &&
11608 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11610 if (IS_GEM(element) || element == EL_SP_INFOTRON)
11611 TEST_DrawTwinkleOnField(x, y);
11613 else if (element == EL_ACID)
11616 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11618 else if ((element == EL_EXIT_OPEN ||
11619 element == EL_EM_EXIT_OPEN ||
11620 element == EL_SP_EXIT_OPEN ||
11621 element == EL_STEEL_EXIT_OPEN ||
11622 element == EL_EM_STEEL_EXIT_OPEN ||
11623 element == EL_SP_TERMINAL ||
11624 element == EL_SP_TERMINAL_ACTIVE ||
11625 element == EL_EXTRA_TIME ||
11626 element == EL_SHIELD_NORMAL ||
11627 element == EL_SHIELD_DEADLY) &&
11628 IS_ANIMATED(graphic))
11629 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11630 else if (IS_MOVING(x, y))
11631 ContinueMoving(x, y);
11632 else if (IS_ACTIVE_BOMB(element))
11633 CheckDynamite(x, y);
11634 else if (element == EL_AMOEBA_GROWING)
11635 AmoebeWaechst(x, y);
11636 else if (element == EL_AMOEBA_SHRINKING)
11637 AmoebaDisappearing(x, y);
11639 #if !USE_NEW_AMOEBA_CODE
11640 else if (IS_AMOEBALIVE(element))
11641 AmoebeAbleger(x, y);
11644 else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
11646 else if (element == EL_EXIT_CLOSED)
11648 else if (element == EL_EM_EXIT_CLOSED)
11650 else if (element == EL_STEEL_EXIT_CLOSED)
11651 CheckExitSteel(x, y);
11652 else if (element == EL_EM_STEEL_EXIT_CLOSED)
11653 CheckExitSteelEM(x, y);
11654 else if (element == EL_SP_EXIT_CLOSED)
11656 else if (element == EL_EXPANDABLE_WALL_GROWING ||
11657 element == EL_EXPANDABLE_STEELWALL_GROWING)
11658 MauerWaechst(x, y);
11659 else if (element == EL_EXPANDABLE_WALL ||
11660 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
11661 element == EL_EXPANDABLE_WALL_VERTICAL ||
11662 element == EL_EXPANDABLE_WALL_ANY ||
11663 element == EL_BD_EXPANDABLE_WALL)
11664 MauerAbleger(x, y);
11665 else if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
11666 element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
11667 element == EL_EXPANDABLE_STEELWALL_ANY)
11668 MauerAblegerStahl(x, y);
11669 else if (element == EL_FLAMES)
11670 CheckForDragon(x, y);
11671 else if (element == EL_EXPLOSION)
11672 ; /* drawing of correct explosion animation is handled separately */
11673 else if (element == EL_ELEMENT_SNAPPING ||
11674 element == EL_DIAGONAL_SHRINKING ||
11675 element == EL_DIAGONAL_GROWING)
11677 graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
11679 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11681 else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
11682 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11684 if (IS_BELT_ACTIVE(element))
11685 PlayLevelSoundAction(x, y, ACTION_ACTIVE);
11687 if (game.magic_wall_active)
11689 int jx = local_player->jx, jy = local_player->jy;
11691 /* play the element sound at the position nearest to the player */
11692 if ((element == EL_MAGIC_WALL_FULL ||
11693 element == EL_MAGIC_WALL_ACTIVE ||
11694 element == EL_MAGIC_WALL_EMPTYING ||
11695 element == EL_BD_MAGIC_WALL_FULL ||
11696 element == EL_BD_MAGIC_WALL_ACTIVE ||
11697 element == EL_BD_MAGIC_WALL_EMPTYING ||
11698 element == EL_DC_MAGIC_WALL_FULL ||
11699 element == EL_DC_MAGIC_WALL_ACTIVE ||
11700 element == EL_DC_MAGIC_WALL_EMPTYING) &&
11701 ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
11709 #if USE_NEW_AMOEBA_CODE
11710 /* new experimental amoeba growth stuff */
11711 if (!(FrameCounter % 8))
11713 static unsigned int random = 1684108901;
11715 for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
11717 x = RND(lev_fieldx);
11718 y = RND(lev_fieldy);
11719 element = Feld[x][y];
11721 if (!IS_PLAYER(x,y) &&
11722 (element == EL_EMPTY ||
11723 CAN_GROW_INTO(element) ||
11724 element == EL_QUICKSAND_EMPTY ||
11725 element == EL_QUICKSAND_FAST_EMPTY ||
11726 element == EL_ACID_SPLASH_LEFT ||
11727 element == EL_ACID_SPLASH_RIGHT))
11729 if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
11730 (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
11731 (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
11732 (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
11733 Feld[x][y] = EL_AMOEBA_DROP;
11736 random = random * 129 + 1;
11741 game.explosions_delayed = FALSE;
11743 SCAN_PLAYFIELD(x, y)
11745 element = Feld[x][y];
11747 if (ExplodeField[x][y])
11748 Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
11749 else if (element == EL_EXPLOSION)
11750 Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
11752 ExplodeField[x][y] = EX_TYPE_NONE;
11755 game.explosions_delayed = TRUE;
11757 if (game.magic_wall_active)
11759 if (!(game.magic_wall_time_left % 4))
11761 int element = Feld[magic_wall_x][magic_wall_y];
11763 if (element == EL_BD_MAGIC_WALL_FULL ||
11764 element == EL_BD_MAGIC_WALL_ACTIVE ||
11765 element == EL_BD_MAGIC_WALL_EMPTYING)
11766 PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
11767 else if (element == EL_DC_MAGIC_WALL_FULL ||
11768 element == EL_DC_MAGIC_WALL_ACTIVE ||
11769 element == EL_DC_MAGIC_WALL_EMPTYING)
11770 PlayLevelSound(magic_wall_x, magic_wall_y, SND_DC_MAGIC_WALL_ACTIVE);
11772 PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
11775 if (game.magic_wall_time_left > 0)
11777 game.magic_wall_time_left--;
11779 if (!game.magic_wall_time_left)
11781 SCAN_PLAYFIELD(x, y)
11783 element = Feld[x][y];
11785 if (element == EL_MAGIC_WALL_ACTIVE ||
11786 element == EL_MAGIC_WALL_FULL)
11788 Feld[x][y] = EL_MAGIC_WALL_DEAD;
11789 TEST_DrawLevelField(x, y);
11791 else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
11792 element == EL_BD_MAGIC_WALL_FULL)
11794 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
11795 TEST_DrawLevelField(x, y);
11797 else if (element == EL_DC_MAGIC_WALL_ACTIVE ||
11798 element == EL_DC_MAGIC_WALL_FULL)
11800 Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
11801 TEST_DrawLevelField(x, y);
11805 game.magic_wall_active = FALSE;
11810 if (game.light_time_left > 0)
11812 game.light_time_left--;
11814 if (game.light_time_left == 0)
11815 RedrawAllLightSwitchesAndInvisibleElements();
11818 if (game.timegate_time_left > 0)
11820 game.timegate_time_left--;
11822 if (game.timegate_time_left == 0)
11823 CloseAllOpenTimegates();
11826 if (game.lenses_time_left > 0)
11828 game.lenses_time_left--;
11830 if (game.lenses_time_left == 0)
11831 RedrawAllInvisibleElementsForLenses();
11834 if (game.magnify_time_left > 0)
11836 game.magnify_time_left--;
11838 if (game.magnify_time_left == 0)
11839 RedrawAllInvisibleElementsForMagnifier();
11842 for (i = 0; i < MAX_PLAYERS; i++)
11844 struct PlayerInfo *player = &stored_player[i];
11846 if (SHIELD_ON(player))
11848 if (player->shield_deadly_time_left)
11849 PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
11850 else if (player->shield_normal_time_left)
11851 PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
11855 #if USE_DELAYED_GFX_REDRAW
11856 SCAN_PLAYFIELD(x, y)
11858 if (GfxRedraw[x][y] != GFX_REDRAW_NONE)
11860 /* !!! PROBLEM: THIS REDRAWS THE PLAYFIELD _AFTER_ THE SCAN, BUT TILES
11861 !!! MAY HAVE CHANGED AFTER BEING DRAWN DURING PLAYFIELD SCAN !!! */
11863 if (GfxRedraw[x][y] & GFX_REDRAW_TILE)
11864 DrawLevelField(x, y);
11866 if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED)
11867 DrawLevelFieldCrumbled(x, y);
11869 if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS)
11870 DrawLevelFieldCrumbledNeighbours(x, y);
11872 if (GfxRedraw[x][y] & GFX_REDRAW_TILE_TWINKLED)
11873 DrawTwinkleOnField(x, y);
11876 GfxRedraw[x][y] = GFX_REDRAW_NONE;
11881 PlayAllPlayersSound();
11883 if (local_player->show_envelope != 0 && local_player->MovPos == 0)
11885 ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
11887 local_player->show_envelope = 0;
11890 /* use random number generator in every frame to make it less predictable */
11891 if (game.engine_version >= VERSION_IDENT(3,1,1,0))
11895 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
11897 int min_x = x, min_y = y, max_x = x, max_y = y;
11900 for (i = 0; i < MAX_PLAYERS; i++)
11902 int jx = stored_player[i].jx, jy = stored_player[i].jy;
11904 if (!stored_player[i].active || &stored_player[i] == player)
11907 min_x = MIN(min_x, jx);
11908 min_y = MIN(min_y, jy);
11909 max_x = MAX(max_x, jx);
11910 max_y = MAX(max_y, jy);
11913 return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
11916 static boolean AllPlayersInVisibleScreen()
11920 for (i = 0; i < MAX_PLAYERS; i++)
11922 int jx = stored_player[i].jx, jy = stored_player[i].jy;
11924 if (!stored_player[i].active)
11927 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
11934 void ScrollLevel(int dx, int dy)
11936 int scroll_offset = 2 * TILEX_VAR;
11939 BlitBitmap(drawto_field, drawto_field,
11940 FX + TILEX_VAR * (dx == -1) - scroll_offset,
11941 FY + TILEY_VAR * (dy == -1) - scroll_offset,
11942 SXSIZE - TILEX_VAR * (dx != 0) + 2 * scroll_offset,
11943 SYSIZE - TILEY_VAR * (dy != 0) + 2 * scroll_offset,
11944 FX + TILEX_VAR * (dx == 1) - scroll_offset,
11945 FY + TILEY_VAR * (dy == 1) - scroll_offset);
11949 x = (dx == 1 ? BX1 : BX2);
11950 for (y = BY1; y <= BY2; y++)
11951 DrawScreenField(x, y);
11956 y = (dy == 1 ? BY1 : BY2);
11957 for (x = BX1; x <= BX2; x++)
11958 DrawScreenField(x, y);
11961 redraw_mask |= REDRAW_FIELD;
11964 static boolean canFallDown(struct PlayerInfo *player)
11966 int jx = player->jx, jy = player->jy;
11968 return (IN_LEV_FIELD(jx, jy + 1) &&
11969 (IS_FREE(jx, jy + 1) ||
11970 (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
11971 IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
11972 !IS_WALKABLE_INSIDE(Feld[jx][jy]));
11975 static boolean canPassField(int x, int y, int move_dir)
11977 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
11978 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
11979 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
11980 int nextx = x + dx;
11981 int nexty = y + dy;
11982 int element = Feld[x][y];
11984 return (IS_PASSABLE_FROM(element, opposite_dir) &&
11985 !CAN_MOVE(element) &&
11986 IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
11987 IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
11988 (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
11991 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
11993 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
11994 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
11995 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
11999 return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
12000 IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
12001 (IS_DIGGABLE(Feld[newx][newy]) ||
12002 IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
12003 canPassField(newx, newy, move_dir)));
12006 static void CheckGravityMovement(struct PlayerInfo *player)
12008 if (player->gravity && !player->programmed_action)
12010 int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
12011 int move_dir_vertical = player->effective_action & MV_VERTICAL;
12012 boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
12013 int jx = player->jx, jy = player->jy;
12014 boolean player_is_moving_to_valid_field =
12015 (!player_is_snapping &&
12016 (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
12017 canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
12018 boolean player_can_fall_down = canFallDown(player);
12020 if (player_can_fall_down &&
12021 !player_is_moving_to_valid_field)
12022 player->programmed_action = MV_DOWN;
12026 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
12028 return CheckGravityMovement(player);
12030 if (player->gravity && !player->programmed_action)
12032 int jx = player->jx, jy = player->jy;
12033 boolean field_under_player_is_free =
12034 (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
12035 boolean player_is_standing_on_valid_field =
12036 (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
12037 (IS_WALKABLE(Feld[jx][jy]) &&
12038 !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
12040 if (field_under_player_is_free && !player_is_standing_on_valid_field)
12041 player->programmed_action = MV_DOWN;
12046 MovePlayerOneStep()
12047 -----------------------------------------------------------------------------
12048 dx, dy: direction (non-diagonal) to try to move the player to
12049 real_dx, real_dy: direction as read from input device (can be diagonal)
12052 boolean MovePlayerOneStep(struct PlayerInfo *player,
12053 int dx, int dy, int real_dx, int real_dy)
12055 int jx = player->jx, jy = player->jy;
12056 int new_jx = jx + dx, new_jy = jy + dy;
12058 boolean player_can_move = !player->cannot_move;
12060 if (!player->active || (!dx && !dy))
12061 return MP_NO_ACTION;
12063 player->MovDir = (dx < 0 ? MV_LEFT :
12064 dx > 0 ? MV_RIGHT :
12066 dy > 0 ? MV_DOWN : MV_NONE);
12068 if (!IN_LEV_FIELD(new_jx, new_jy))
12069 return MP_NO_ACTION;
12071 if (!player_can_move)
12073 if (player->MovPos == 0)
12075 player->is_moving = FALSE;
12076 player->is_digging = FALSE;
12077 player->is_collecting = FALSE;
12078 player->is_snapping = FALSE;
12079 player->is_pushing = FALSE;
12083 if (!options.network && game.centered_player_nr == -1 &&
12084 !AllPlayersInSight(player, new_jx, new_jy))
12085 return MP_NO_ACTION;
12087 can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
12088 if (can_move != MP_MOVING)
12091 /* check if DigField() has caused relocation of the player */
12092 if (player->jx != jx || player->jy != jy)
12093 return MP_NO_ACTION; /* <-- !!! CHECK THIS [-> MP_ACTION ?] !!! */
12095 StorePlayer[jx][jy] = 0;
12096 player->last_jx = jx;
12097 player->last_jy = jy;
12098 player->jx = new_jx;
12099 player->jy = new_jy;
12100 StorePlayer[new_jx][new_jy] = player->element_nr;
12102 if (player->move_delay_value_next != -1)
12104 player->move_delay_value = player->move_delay_value_next;
12105 player->move_delay_value_next = -1;
12109 (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
12111 player->step_counter++;
12113 PlayerVisit[jx][jy] = FrameCounter;
12115 player->is_moving = TRUE;
12118 /* should better be called in MovePlayer(), but this breaks some tapes */
12119 ScrollPlayer(player, SCROLL_INIT);
12125 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
12127 int jx = player->jx, jy = player->jy;
12128 int old_jx = jx, old_jy = jy;
12129 int moved = MP_NO_ACTION;
12131 if (!player->active)
12136 if (player->MovPos == 0)
12138 player->is_moving = FALSE;
12139 player->is_digging = FALSE;
12140 player->is_collecting = FALSE;
12141 player->is_snapping = FALSE;
12142 player->is_pushing = FALSE;
12148 if (player->move_delay > 0)
12151 player->move_delay = -1; /* set to "uninitialized" value */
12153 /* store if player is automatically moved to next field */
12154 player->is_auto_moving = (player->programmed_action != MV_NONE);
12156 /* remove the last programmed player action */
12157 player->programmed_action = 0;
12159 if (player->MovPos)
12161 /* should only happen if pre-1.2 tape recordings are played */
12162 /* this is only for backward compatibility */
12164 int original_move_delay_value = player->move_delay_value;
12167 printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%d]\n",
12171 /* scroll remaining steps with finest movement resolution */
12172 player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
12174 while (player->MovPos)
12176 ScrollPlayer(player, SCROLL_GO_ON);
12177 ScrollScreen(NULL, SCROLL_GO_ON);
12179 AdvanceFrameAndPlayerCounters(player->index_nr);
12182 BackToFront_WithFrameDelay(0);
12185 player->move_delay_value = original_move_delay_value;
12188 player->is_active = FALSE;
12190 if (player->last_move_dir & MV_HORIZONTAL)
12192 if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
12193 moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
12197 if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
12198 moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
12201 if (!moved && !player->is_active)
12203 player->is_moving = FALSE;
12204 player->is_digging = FALSE;
12205 player->is_collecting = FALSE;
12206 player->is_snapping = FALSE;
12207 player->is_pushing = FALSE;
12213 if (moved & MP_MOVING && !ScreenMovPos &&
12214 (player->index_nr == game.centered_player_nr ||
12215 game.centered_player_nr == -1))
12217 int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
12218 int offset = game.scroll_delay_value;
12220 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12222 /* actual player has left the screen -- scroll in that direction */
12223 if (jx != old_jx) /* player has moved horizontally */
12224 scroll_x += (jx - old_jx);
12225 else /* player has moved vertically */
12226 scroll_y += (jy - old_jy);
12230 if (jx != old_jx) /* player has moved horizontally */
12232 if ((player->MovDir == MV_LEFT && scroll_x > jx - MIDPOSX + offset) ||
12233 (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
12234 scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
12236 /* don't scroll over playfield boundaries */
12237 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
12238 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
12240 /* don't scroll more than one field at a time */
12241 scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
12243 /* don't scroll against the player's moving direction */
12244 if ((player->MovDir == MV_LEFT && scroll_x > old_scroll_x) ||
12245 (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
12246 scroll_x = old_scroll_x;
12248 else /* player has moved vertically */
12250 if ((player->MovDir == MV_UP && scroll_y > jy - MIDPOSY + offset) ||
12251 (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
12252 scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
12254 /* don't scroll over playfield boundaries */
12255 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
12256 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
12258 /* don't scroll more than one field at a time */
12259 scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
12261 /* don't scroll against the player's moving direction */
12262 if ((player->MovDir == MV_UP && scroll_y > old_scroll_y) ||
12263 (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
12264 scroll_y = old_scroll_y;
12268 if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
12270 if (!options.network && game.centered_player_nr == -1 &&
12271 !AllPlayersInVisibleScreen())
12273 scroll_x = old_scroll_x;
12274 scroll_y = old_scroll_y;
12278 ScrollScreen(player, SCROLL_INIT);
12279 ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
12284 player->StepFrame = 0;
12286 if (moved & MP_MOVING)
12288 if (old_jx != jx && old_jy == jy)
12289 player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
12290 else if (old_jx == jx && old_jy != jy)
12291 player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
12293 TEST_DrawLevelField(jx, jy); /* for "crumbled sand" */
12295 player->last_move_dir = player->MovDir;
12296 player->is_moving = TRUE;
12297 player->is_snapping = FALSE;
12298 player->is_switching = FALSE;
12299 player->is_dropping = FALSE;
12300 player->is_dropping_pressed = FALSE;
12301 player->drop_pressed_delay = 0;
12304 /* should better be called here than above, but this breaks some tapes */
12305 ScrollPlayer(player, SCROLL_INIT);
12310 CheckGravityMovementWhenNotMoving(player);
12312 player->is_moving = FALSE;
12314 /* at this point, the player is allowed to move, but cannot move right now
12315 (e.g. because of something blocking the way) -- ensure that the player
12316 is also allowed to move in the next frame (in old versions before 3.1.1,
12317 the player was forced to wait again for eight frames before next try) */
12319 if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12320 player->move_delay = 0; /* allow direct movement in the next frame */
12323 if (player->move_delay == -1) /* not yet initialized by DigField() */
12324 player->move_delay = player->move_delay_value;
12326 if (game.engine_version < VERSION_IDENT(3,0,7,0))
12328 TestIfPlayerTouchesBadThing(jx, jy);
12329 TestIfPlayerTouchesCustomElement(jx, jy);
12332 if (!player->active)
12333 RemovePlayer(player);
12338 void ScrollPlayer(struct PlayerInfo *player, int mode)
12340 int jx = player->jx, jy = player->jy;
12341 int last_jx = player->last_jx, last_jy = player->last_jy;
12342 int move_stepsize = TILEX / player->move_delay_value;
12344 if (!player->active)
12347 if (player->MovPos == 0 && mode == SCROLL_GO_ON) /* player not moving */
12350 if (mode == SCROLL_INIT)
12352 player->actual_frame_counter = FrameCounter;
12353 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
12355 if ((player->block_last_field || player->block_delay_adjustment > 0) &&
12356 Feld[last_jx][last_jy] == EL_EMPTY)
12358 int last_field_block_delay = 0; /* start with no blocking at all */
12359 int block_delay_adjustment = player->block_delay_adjustment;
12361 /* if player blocks last field, add delay for exactly one move */
12362 if (player->block_last_field)
12364 last_field_block_delay += player->move_delay_value;
12366 /* when blocking enabled, prevent moving up despite gravity */
12367 if (player->gravity && player->MovDir == MV_UP)
12368 block_delay_adjustment = -1;
12371 /* add block delay adjustment (also possible when not blocking) */
12372 last_field_block_delay += block_delay_adjustment;
12374 Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
12375 MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
12378 if (player->MovPos != 0) /* player has not yet reached destination */
12381 else if (!FrameReached(&player->actual_frame_counter, 1))
12384 if (player->MovPos != 0)
12386 player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
12387 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
12389 /* before DrawPlayer() to draw correct player graphic for this case */
12390 if (player->MovPos == 0)
12391 CheckGravityMovement(player);
12394 if (player->MovPos == 0) /* player reached destination field */
12396 if (player->move_delay_reset_counter > 0)
12398 player->move_delay_reset_counter--;
12400 if (player->move_delay_reset_counter == 0)
12402 /* continue with normal speed after quickly moving through gate */
12403 HALVE_PLAYER_SPEED(player);
12405 /* be able to make the next move without delay */
12406 player->move_delay = 0;
12410 player->last_jx = jx;
12411 player->last_jy = jy;
12413 if (Feld[jx][jy] == EL_EXIT_OPEN ||
12414 Feld[jx][jy] == EL_EM_EXIT_OPEN ||
12415 Feld[jx][jy] == EL_EM_EXIT_OPENING ||
12416 Feld[jx][jy] == EL_STEEL_EXIT_OPEN ||
12417 Feld[jx][jy] == EL_EM_STEEL_EXIT_OPEN ||
12418 Feld[jx][jy] == EL_EM_STEEL_EXIT_OPENING ||
12419 Feld[jx][jy] == EL_SP_EXIT_OPEN ||
12420 Feld[jx][jy] == EL_SP_EXIT_OPENING) /* <-- special case */
12422 DrawPlayer(player); /* needed here only to cleanup last field */
12423 RemovePlayer(player);
12425 if (local_player->friends_still_needed == 0 ||
12426 IS_SP_ELEMENT(Feld[jx][jy]))
12427 PlayerWins(player);
12430 /* this breaks one level: "machine", level 000 */
12432 int move_direction = player->MovDir;
12433 int enter_side = MV_DIR_OPPOSITE(move_direction);
12434 int leave_side = move_direction;
12435 int old_jx = last_jx;
12436 int old_jy = last_jy;
12437 int old_element = Feld[old_jx][old_jy];
12438 int new_element = Feld[jx][jy];
12440 if (IS_CUSTOM_ELEMENT(old_element))
12441 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
12443 player->index_bit, leave_side);
12445 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
12446 CE_PLAYER_LEAVES_X,
12447 player->index_bit, leave_side);
12449 if (IS_CUSTOM_ELEMENT(new_element))
12450 CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
12451 player->index_bit, enter_side);
12453 CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
12454 CE_PLAYER_ENTERS_X,
12455 player->index_bit, enter_side);
12457 CheckTriggeredElementChangeBySide(jx, jy, player->initial_element,
12458 CE_MOVE_OF_X, move_direction);
12461 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
12463 TestIfPlayerTouchesBadThing(jx, jy);
12464 TestIfPlayerTouchesCustomElement(jx, jy);
12466 /* needed because pushed element has not yet reached its destination,
12467 so it would trigger a change event at its previous field location */
12468 if (!player->is_pushing)
12469 TestIfElementTouchesCustomElement(jx, jy); /* for empty space */
12471 if (!player->active)
12472 RemovePlayer(player);
12475 if (!local_player->LevelSolved && level.use_step_counter)
12485 if (TimeLeft <= 10 && setup.time_limit)
12486 PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
12488 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
12490 DisplayGameControlValues();
12492 if (!TimeLeft && setup.time_limit)
12493 for (i = 0; i < MAX_PLAYERS; i++)
12494 KillPlayer(&stored_player[i]);
12496 else if (game.no_time_limit && !AllPlayersGone) /* level w/o time limit */
12498 game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
12500 DisplayGameControlValues();
12504 if (tape.single_step && tape.recording && !tape.pausing &&
12505 !player->programmed_action)
12506 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
12508 if (!player->programmed_action)
12509 CheckSaveEngineSnapshot(player);
12513 void ScrollScreen(struct PlayerInfo *player, int mode)
12515 static unsigned int screen_frame_counter = 0;
12517 if (mode == SCROLL_INIT)
12519 /* set scrolling step size according to actual player's moving speed */
12520 ScrollStepSize = TILEX / player->move_delay_value;
12522 screen_frame_counter = FrameCounter;
12523 ScreenMovDir = player->MovDir;
12524 ScreenMovPos = player->MovPos;
12525 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
12528 else if (!FrameReached(&screen_frame_counter, 1))
12533 ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
12534 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
12535 redraw_mask |= REDRAW_FIELD;
12538 ScreenMovDir = MV_NONE;
12541 void TestIfPlayerTouchesCustomElement(int x, int y)
12543 static int xy[4][2] =
12550 static int trigger_sides[4][2] =
12552 /* center side border side */
12553 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
12554 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
12555 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
12556 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
12558 static int touch_dir[4] =
12560 MV_LEFT | MV_RIGHT,
12565 int center_element = Feld[x][y]; /* should always be non-moving! */
12568 for (i = 0; i < NUM_DIRECTIONS; i++)
12570 int xx = x + xy[i][0];
12571 int yy = y + xy[i][1];
12572 int center_side = trigger_sides[i][0];
12573 int border_side = trigger_sides[i][1];
12574 int border_element;
12576 if (!IN_LEV_FIELD(xx, yy))
12579 if (IS_PLAYER(x, y)) /* player found at center element */
12581 struct PlayerInfo *player = PLAYERINFO(x, y);
12583 if (game.engine_version < VERSION_IDENT(3,0,7,0))
12584 border_element = Feld[xx][yy]; /* may be moving! */
12585 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
12586 border_element = Feld[xx][yy];
12587 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
12588 border_element = MovingOrBlocked2Element(xx, yy);
12590 continue; /* center and border element do not touch */
12592 CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
12593 player->index_bit, border_side);
12594 CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
12595 CE_PLAYER_TOUCHES_X,
12596 player->index_bit, border_side);
12599 /* use player element that is initially defined in the level playfield,
12600 not the player element that corresponds to the runtime player number
12601 (example: a level that contains EL_PLAYER_3 as the only player would
12602 incorrectly give EL_PLAYER_1 for "player->element_nr") */
12603 int player_element = PLAYERINFO(x, y)->initial_element;
12605 CheckElementChangeBySide(xx, yy, border_element, player_element,
12606 CE_TOUCHING_X, border_side);
12609 else if (IS_PLAYER(xx, yy)) /* player found at border element */
12611 struct PlayerInfo *player = PLAYERINFO(xx, yy);
12613 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
12615 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
12616 continue; /* center and border element do not touch */
12619 CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
12620 player->index_bit, center_side);
12621 CheckTriggeredElementChangeByPlayer(x, y, center_element,
12622 CE_PLAYER_TOUCHES_X,
12623 player->index_bit, center_side);
12626 /* use player element that is initially defined in the level playfield,
12627 not the player element that corresponds to the runtime player number
12628 (example: a level that contains EL_PLAYER_3 as the only player would
12629 incorrectly give EL_PLAYER_1 for "player->element_nr") */
12630 int player_element = PLAYERINFO(xx, yy)->initial_element;
12632 CheckElementChangeBySide(x, y, center_element, player_element,
12633 CE_TOUCHING_X, center_side);
12641 void TestIfElementTouchesCustomElement(int x, int y)
12643 static int xy[4][2] =
12650 static int trigger_sides[4][2] =
12652 /* center side border side */
12653 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
12654 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
12655 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
12656 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
12658 static int touch_dir[4] =
12660 MV_LEFT | MV_RIGHT,
12665 boolean change_center_element = FALSE;
12666 int center_element = Feld[x][y]; /* should always be non-moving! */
12667 int border_element_old[NUM_DIRECTIONS];
12670 for (i = 0; i < NUM_DIRECTIONS; i++)
12672 int xx = x + xy[i][0];
12673 int yy = y + xy[i][1];
12674 int border_element;
12676 border_element_old[i] = -1;
12678 if (!IN_LEV_FIELD(xx, yy))
12681 if (game.engine_version < VERSION_IDENT(3,0,7,0))
12682 border_element = Feld[xx][yy]; /* may be moving! */
12683 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
12684 border_element = Feld[xx][yy];
12685 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
12686 border_element = MovingOrBlocked2Element(xx, yy);
12688 continue; /* center and border element do not touch */
12690 border_element_old[i] = border_element;
12693 for (i = 0; i < NUM_DIRECTIONS; i++)
12695 int xx = x + xy[i][0];
12696 int yy = y + xy[i][1];
12697 int center_side = trigger_sides[i][0];
12698 int border_element = border_element_old[i];
12700 if (border_element == -1)
12703 /* check for change of border element */
12704 CheckElementChangeBySide(xx, yy, border_element, center_element,
12705 CE_TOUCHING_X, center_side);
12707 /* (center element cannot be player, so we dont have to check this here) */
12710 for (i = 0; i < NUM_DIRECTIONS; i++)
12712 int xx = x + xy[i][0];
12713 int yy = y + xy[i][1];
12714 int border_side = trigger_sides[i][1];
12715 int border_element = border_element_old[i];
12717 if (border_element == -1)
12720 /* check for change of center element (but change it only once) */
12721 if (!change_center_element)
12722 change_center_element =
12723 CheckElementChangeBySide(x, y, center_element, border_element,
12724 CE_TOUCHING_X, border_side);
12726 if (IS_PLAYER(xx, yy))
12728 /* use player element that is initially defined in the level playfield,
12729 not the player element that corresponds to the runtime player number
12730 (example: a level that contains EL_PLAYER_3 as the only player would
12731 incorrectly give EL_PLAYER_1 for "player->element_nr") */
12732 int player_element = PLAYERINFO(xx, yy)->initial_element;
12734 CheckElementChangeBySide(x, y, center_element, player_element,
12735 CE_TOUCHING_X, border_side);
12740 void TestIfElementHitsCustomElement(int x, int y, int direction)
12742 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
12743 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
12744 int hitx = x + dx, hity = y + dy;
12745 int hitting_element = Feld[x][y];
12746 int touched_element;
12748 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
12751 touched_element = (IN_LEV_FIELD(hitx, hity) ?
12752 MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
12754 if (IN_LEV_FIELD(hitx, hity))
12756 int opposite_direction = MV_DIR_OPPOSITE(direction);
12757 int hitting_side = direction;
12758 int touched_side = opposite_direction;
12759 boolean object_hit = (!IS_MOVING(hitx, hity) ||
12760 MovDir[hitx][hity] != direction ||
12761 ABS(MovPos[hitx][hity]) <= TILEY / 2);
12767 CheckElementChangeBySide(x, y, hitting_element, touched_element,
12768 CE_HITTING_X, touched_side);
12770 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
12771 CE_HIT_BY_X, hitting_side);
12773 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
12774 CE_HIT_BY_SOMETHING, opposite_direction);
12776 if (IS_PLAYER(hitx, hity))
12778 /* use player element that is initially defined in the level playfield,
12779 not the player element that corresponds to the runtime player number
12780 (example: a level that contains EL_PLAYER_3 as the only player would
12781 incorrectly give EL_PLAYER_1 for "player->element_nr") */
12782 int player_element = PLAYERINFO(hitx, hity)->initial_element;
12784 CheckElementChangeBySide(x, y, hitting_element, player_element,
12785 CE_HITTING_X, touched_side);
12790 /* "hitting something" is also true when hitting the playfield border */
12791 CheckElementChangeBySide(x, y, hitting_element, touched_element,
12792 CE_HITTING_SOMETHING, direction);
12795 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
12797 int i, kill_x = -1, kill_y = -1;
12799 int bad_element = -1;
12800 static int test_xy[4][2] =
12807 static int test_dir[4] =
12815 for (i = 0; i < NUM_DIRECTIONS; i++)
12817 int test_x, test_y, test_move_dir, test_element;
12819 test_x = good_x + test_xy[i][0];
12820 test_y = good_y + test_xy[i][1];
12822 if (!IN_LEV_FIELD(test_x, test_y))
12826 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
12828 test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
12830 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
12831 2nd case: DONT_TOUCH style bad thing does not move away from good thing
12833 if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
12834 (DONT_TOUCH(test_element) && test_move_dir != test_dir[i]))
12838 bad_element = test_element;
12844 if (kill_x != -1 || kill_y != -1)
12846 if (IS_PLAYER(good_x, good_y))
12848 struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
12850 if (player->shield_deadly_time_left > 0 &&
12851 !IS_INDESTRUCTIBLE(bad_element))
12852 Bang(kill_x, kill_y);
12853 else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
12854 KillPlayer(player);
12857 Bang(good_x, good_y);
12861 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
12863 int i, kill_x = -1, kill_y = -1;
12864 int bad_element = Feld[bad_x][bad_y];
12865 static int test_xy[4][2] =
12872 static int touch_dir[4] =
12874 MV_LEFT | MV_RIGHT,
12879 static int test_dir[4] =
12887 if (bad_element == EL_EXPLOSION) /* skip just exploding bad things */
12890 for (i = 0; i < NUM_DIRECTIONS; i++)
12892 int test_x, test_y, test_move_dir, test_element;
12894 test_x = bad_x + test_xy[i][0];
12895 test_y = bad_y + test_xy[i][1];
12897 if (!IN_LEV_FIELD(test_x, test_y))
12901 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
12903 test_element = Feld[test_x][test_y];
12905 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
12906 2nd case: DONT_TOUCH style bad thing does not move away from good thing
12908 if ((DONT_RUN_INTO(bad_element) && bad_move_dir == test_dir[i]) ||
12909 (DONT_TOUCH(bad_element) && test_move_dir != test_dir[i]))
12911 /* good thing is player or penguin that does not move away */
12912 if (IS_PLAYER(test_x, test_y))
12914 struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
12916 if (bad_element == EL_ROBOT && player->is_moving)
12917 continue; /* robot does not kill player if he is moving */
12919 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
12921 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
12922 continue; /* center and border element do not touch */
12930 else if (test_element == EL_PENGUIN)
12940 if (kill_x != -1 || kill_y != -1)
12942 if (IS_PLAYER(kill_x, kill_y))
12944 struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
12946 if (player->shield_deadly_time_left > 0 &&
12947 !IS_INDESTRUCTIBLE(bad_element))
12948 Bang(bad_x, bad_y);
12949 else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
12950 KillPlayer(player);
12953 Bang(kill_x, kill_y);
12957 void TestIfGoodThingGetsHitByBadThing(int bad_x, int bad_y, int bad_move_dir)
12959 int bad_element = Feld[bad_x][bad_y];
12960 int dx = (bad_move_dir == MV_LEFT ? -1 : bad_move_dir == MV_RIGHT ? +1 : 0);
12961 int dy = (bad_move_dir == MV_UP ? -1 : bad_move_dir == MV_DOWN ? +1 : 0);
12962 int test_x = bad_x + dx, test_y = bad_y + dy;
12963 int test_move_dir, test_element;
12964 int kill_x = -1, kill_y = -1;
12966 if (!IN_LEV_FIELD(test_x, test_y))
12970 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
12972 test_element = Feld[test_x][test_y];
12974 if (test_move_dir != bad_move_dir)
12976 /* good thing can be player or penguin that does not move away */
12977 if (IS_PLAYER(test_x, test_y))
12979 struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
12981 /* (note: in comparison to DONT_RUN_TO and DONT_TOUCH, also handle the
12982 player as being hit when he is moving towards the bad thing, because
12983 the "get hit by" condition would be lost after the player stops) */
12984 if (player->MovPos != 0 && player->MovDir == bad_move_dir)
12985 return; /* player moves away from bad thing */
12990 else if (test_element == EL_PENGUIN)
12997 if (kill_x != -1 || kill_y != -1)
12999 if (IS_PLAYER(kill_x, kill_y))
13001 struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
13003 if (player->shield_deadly_time_left > 0 &&
13004 !IS_INDESTRUCTIBLE(bad_element))
13005 Bang(bad_x, bad_y);
13006 else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
13007 KillPlayer(player);
13010 Bang(kill_x, kill_y);
13014 void TestIfPlayerTouchesBadThing(int x, int y)
13016 TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13019 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
13021 TestIfGoodThingHitsBadThing(x, y, move_dir);
13024 void TestIfBadThingTouchesPlayer(int x, int y)
13026 TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13029 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
13031 TestIfBadThingHitsGoodThing(x, y, move_dir);
13034 void TestIfFriendTouchesBadThing(int x, int y)
13036 TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13039 void TestIfBadThingTouchesFriend(int x, int y)
13041 TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13044 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
13046 int i, kill_x = bad_x, kill_y = bad_y;
13047 static int xy[4][2] =
13055 for (i = 0; i < NUM_DIRECTIONS; i++)
13059 x = bad_x + xy[i][0];
13060 y = bad_y + xy[i][1];
13061 if (!IN_LEV_FIELD(x, y))
13064 element = Feld[x][y];
13065 if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
13066 element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
13074 if (kill_x != bad_x || kill_y != bad_y)
13075 Bang(bad_x, bad_y);
13078 void KillPlayer(struct PlayerInfo *player)
13080 int jx = player->jx, jy = player->jy;
13082 if (!player->active)
13086 printf("::: 0: killed == %d, active == %d, reanimated == %d\n",
13087 player->killed, player->active, player->reanimated);
13090 /* the following code was introduced to prevent an infinite loop when calling
13092 -> CheckTriggeredElementChangeExt()
13093 -> ExecuteCustomElementAction()
13095 -> (infinitely repeating the above sequence of function calls)
13096 which occurs when killing the player while having a CE with the setting
13097 "kill player X when explosion of <player X>"; the solution using a new
13098 field "player->killed" was chosen for backwards compatibility, although
13099 clever use of the fields "player->active" etc. would probably also work */
13101 if (player->killed)
13105 player->killed = TRUE;
13107 /* remove accessible field at the player's position */
13108 Feld[jx][jy] = EL_EMPTY;
13110 /* deactivate shield (else Bang()/Explode() would not work right) */
13111 player->shield_normal_time_left = 0;
13112 player->shield_deadly_time_left = 0;
13115 printf("::: 1: killed == %d, active == %d, reanimated == %d\n",
13116 player->killed, player->active, player->reanimated);
13122 printf("::: 2: killed == %d, active == %d, reanimated == %d\n",
13123 player->killed, player->active, player->reanimated);
13126 if (player->reanimated) /* killed player may have been reanimated */
13127 player->killed = player->reanimated = FALSE;
13129 BuryPlayer(player);
13132 static void KillPlayerUnlessEnemyProtected(int x, int y)
13134 if (!PLAYER_ENEMY_PROTECTED(x, y))
13135 KillPlayer(PLAYERINFO(x, y));
13138 static void KillPlayerUnlessExplosionProtected(int x, int y)
13140 if (!PLAYER_EXPLOSION_PROTECTED(x, y))
13141 KillPlayer(PLAYERINFO(x, y));
13144 void BuryPlayer(struct PlayerInfo *player)
13146 int jx = player->jx, jy = player->jy;
13148 if (!player->active)
13151 PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
13152 PlayLevelSound(jx, jy, SND_GAME_LOSING);
13154 player->GameOver = TRUE;
13155 RemovePlayer(player);
13158 void RemovePlayer(struct PlayerInfo *player)
13160 int jx = player->jx, jy = player->jy;
13161 int i, found = FALSE;
13163 player->present = FALSE;
13164 player->active = FALSE;
13166 if (!ExplodeField[jx][jy])
13167 StorePlayer[jx][jy] = 0;
13169 if (player->is_moving)
13170 TEST_DrawLevelField(player->last_jx, player->last_jy);
13172 for (i = 0; i < MAX_PLAYERS; i++)
13173 if (stored_player[i].active)
13177 AllPlayersGone = TRUE;
13183 static void setFieldForSnapping(int x, int y, int element, int direction)
13185 struct ElementInfo *ei = &element_info[element];
13186 int direction_bit = MV_DIR_TO_BIT(direction);
13187 int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
13188 int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
13189 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
13191 Feld[x][y] = EL_ELEMENT_SNAPPING;
13192 MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
13194 ResetGfxAnimation(x, y);
13196 GfxElement[x][y] = element;
13197 GfxAction[x][y] = action;
13198 GfxDir[x][y] = direction;
13199 GfxFrame[x][y] = -1;
13203 =============================================================================
13204 checkDiagonalPushing()
13205 -----------------------------------------------------------------------------
13206 check if diagonal input device direction results in pushing of object
13207 (by checking if the alternative direction is walkable, diggable, ...)
13208 =============================================================================
13211 static boolean checkDiagonalPushing(struct PlayerInfo *player,
13212 int x, int y, int real_dx, int real_dy)
13214 int jx, jy, dx, dy, xx, yy;
13216 if (real_dx == 0 || real_dy == 0) /* no diagonal direction => push */
13219 /* diagonal direction: check alternative direction */
13224 xx = jx + (dx == 0 ? real_dx : 0);
13225 yy = jy + (dy == 0 ? real_dy : 0);
13227 return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
13231 =============================================================================
13233 -----------------------------------------------------------------------------
13234 x, y: field next to player (non-diagonal) to try to dig to
13235 real_dx, real_dy: direction as read from input device (can be diagonal)
13236 =============================================================================
13239 static int DigField(struct PlayerInfo *player,
13240 int oldx, int oldy, int x, int y,
13241 int real_dx, int real_dy, int mode)
13243 boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
13244 boolean player_was_pushing = player->is_pushing;
13245 boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
13246 boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
13247 int jx = oldx, jy = oldy;
13248 int dx = x - jx, dy = y - jy;
13249 int nextx = x + dx, nexty = y + dy;
13250 int move_direction = (dx == -1 ? MV_LEFT :
13251 dx == +1 ? MV_RIGHT :
13253 dy == +1 ? MV_DOWN : MV_NONE);
13254 int opposite_direction = MV_DIR_OPPOSITE(move_direction);
13255 int dig_side = MV_DIR_OPPOSITE(move_direction);
13256 int old_element = Feld[jx][jy];
13257 int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
13260 if (is_player) /* function can also be called by EL_PENGUIN */
13262 if (player->MovPos == 0)
13264 player->is_digging = FALSE;
13265 player->is_collecting = FALSE;
13268 if (player->MovPos == 0) /* last pushing move finished */
13269 player->is_pushing = FALSE;
13271 if (mode == DF_NO_PUSH) /* player just stopped pushing */
13273 player->is_switching = FALSE;
13274 player->push_delay = -1;
13276 return MP_NO_ACTION;
13280 if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
13281 old_element = Back[jx][jy];
13283 /* in case of element dropped at player position, check background */
13284 else if (Back[jx][jy] != EL_EMPTY &&
13285 game.engine_version >= VERSION_IDENT(2,2,0,0))
13286 old_element = Back[jx][jy];
13288 if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
13289 return MP_NO_ACTION; /* field has no opening in this direction */
13291 if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
13292 return MP_NO_ACTION; /* field has no opening in this direction */
13294 if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
13298 Feld[jx][jy] = player->artwork_element;
13299 InitMovingField(jx, jy, MV_DOWN);
13300 Store[jx][jy] = EL_ACID;
13301 ContinueMoving(jx, jy);
13302 BuryPlayer(player);
13304 return MP_DONT_RUN_INTO;
13307 if (player_can_move && DONT_RUN_INTO(element))
13309 TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
13311 return MP_DONT_RUN_INTO;
13314 if (IS_MOVING(x, y) || IS_PLAYER(x, y))
13315 return MP_NO_ACTION;
13317 collect_count = element_info[element].collect_count_initial;
13319 if (!is_player && !IS_COLLECTIBLE(element)) /* penguin cannot collect it */
13320 return MP_NO_ACTION;
13322 if (game.engine_version < VERSION_IDENT(2,2,0,0))
13323 player_can_move = player_can_move_or_snap;
13325 if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
13326 game.engine_version >= VERSION_IDENT(2,2,0,0))
13328 CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
13329 player->index_bit, dig_side);
13330 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13331 player->index_bit, dig_side);
13333 if (element == EL_DC_LANDMINE)
13336 if (Feld[x][y] != element) /* field changed by snapping */
13339 return MP_NO_ACTION;
13342 if (player->gravity && is_player && !player->is_auto_moving &&
13343 canFallDown(player) && move_direction != MV_DOWN &&
13344 !canMoveToValidFieldWithGravity(jx, jy, move_direction))
13345 return MP_NO_ACTION; /* player cannot walk here due to gravity */
13347 if (player_can_move &&
13348 IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
13350 int sound_element = SND_ELEMENT(element);
13351 int sound_action = ACTION_WALKING;
13353 if (IS_RND_GATE(element))
13355 if (!player->key[RND_GATE_NR(element)])
13356 return MP_NO_ACTION;
13358 else if (IS_RND_GATE_GRAY(element))
13360 if (!player->key[RND_GATE_GRAY_NR(element)])
13361 return MP_NO_ACTION;
13363 else if (IS_RND_GATE_GRAY_ACTIVE(element))
13365 if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
13366 return MP_NO_ACTION;
13368 else if (element == EL_EXIT_OPEN ||
13369 element == EL_EM_EXIT_OPEN ||
13370 element == EL_EM_EXIT_OPENING ||
13371 element == EL_STEEL_EXIT_OPEN ||
13372 element == EL_EM_STEEL_EXIT_OPEN ||
13373 element == EL_EM_STEEL_EXIT_OPENING ||
13374 element == EL_SP_EXIT_OPEN ||
13375 element == EL_SP_EXIT_OPENING)
13377 sound_action = ACTION_PASSING; /* player is passing exit */
13379 else if (element == EL_EMPTY)
13381 sound_action = ACTION_MOVING; /* nothing to walk on */
13384 /* play sound from background or player, whatever is available */
13385 if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
13386 PlayLevelSoundElementAction(x, y, sound_element, sound_action);
13388 PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
13390 else if (player_can_move &&
13391 IS_PASSABLE(element) && canPassField(x, y, move_direction))
13393 if (!ACCESS_FROM(element, opposite_direction))
13394 return MP_NO_ACTION; /* field not accessible from this direction */
13396 if (CAN_MOVE(element)) /* only fixed elements can be passed! */
13397 return MP_NO_ACTION;
13399 if (IS_EM_GATE(element))
13401 if (!player->key[EM_GATE_NR(element)])
13402 return MP_NO_ACTION;
13404 else if (IS_EM_GATE_GRAY(element))
13406 if (!player->key[EM_GATE_GRAY_NR(element)])
13407 return MP_NO_ACTION;
13409 else if (IS_EM_GATE_GRAY_ACTIVE(element))
13411 if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
13412 return MP_NO_ACTION;
13414 else if (IS_EMC_GATE(element))
13416 if (!player->key[EMC_GATE_NR(element)])
13417 return MP_NO_ACTION;
13419 else if (IS_EMC_GATE_GRAY(element))
13421 if (!player->key[EMC_GATE_GRAY_NR(element)])
13422 return MP_NO_ACTION;
13424 else if (IS_EMC_GATE_GRAY_ACTIVE(element))
13426 if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
13427 return MP_NO_ACTION;
13429 else if (element == EL_DC_GATE_WHITE ||
13430 element == EL_DC_GATE_WHITE_GRAY ||
13431 element == EL_DC_GATE_WHITE_GRAY_ACTIVE)
13433 if (player->num_white_keys == 0)
13434 return MP_NO_ACTION;
13436 player->num_white_keys--;
13438 else if (IS_SP_PORT(element))
13440 if (element == EL_SP_GRAVITY_PORT_LEFT ||
13441 element == EL_SP_GRAVITY_PORT_RIGHT ||
13442 element == EL_SP_GRAVITY_PORT_UP ||
13443 element == EL_SP_GRAVITY_PORT_DOWN)
13444 player->gravity = !player->gravity;
13445 else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
13446 element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
13447 element == EL_SP_GRAVITY_ON_PORT_UP ||
13448 element == EL_SP_GRAVITY_ON_PORT_DOWN)
13449 player->gravity = TRUE;
13450 else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
13451 element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
13452 element == EL_SP_GRAVITY_OFF_PORT_UP ||
13453 element == EL_SP_GRAVITY_OFF_PORT_DOWN)
13454 player->gravity = FALSE;
13457 /* automatically move to the next field with double speed */
13458 player->programmed_action = move_direction;
13460 if (player->move_delay_reset_counter == 0)
13462 player->move_delay_reset_counter = 2; /* two double speed steps */
13464 DOUBLE_PLAYER_SPEED(player);
13467 PlayLevelSoundAction(x, y, ACTION_PASSING);
13469 else if (player_can_move_or_snap && IS_DIGGABLE(element))
13473 if (mode != DF_SNAP)
13475 GfxElement[x][y] = GFX_ELEMENT(element);
13476 player->is_digging = TRUE;
13479 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
13481 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
13482 player->index_bit, dig_side);
13484 if (mode == DF_SNAP)
13486 if (level.block_snap_field)
13487 setFieldForSnapping(x, y, element, move_direction);
13489 TestIfElementTouchesCustomElement(x, y); /* for empty space */
13491 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13492 player->index_bit, dig_side);
13495 else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
13499 if (is_player && mode != DF_SNAP)
13501 GfxElement[x][y] = element;
13502 player->is_collecting = TRUE;
13505 if (element == EL_SPEED_PILL)
13507 player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
13509 else if (element == EL_EXTRA_TIME && level.time > 0)
13511 TimeLeft += level.extra_time;
13513 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
13515 DisplayGameControlValues();
13517 else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
13519 player->shield_normal_time_left += level.shield_normal_time;
13520 if (element == EL_SHIELD_DEADLY)
13521 player->shield_deadly_time_left += level.shield_deadly_time;
13523 else if (element == EL_DYNAMITE ||
13524 element == EL_EM_DYNAMITE ||
13525 element == EL_SP_DISK_RED)
13527 if (player->inventory_size < MAX_INVENTORY_SIZE)
13528 player->inventory_element[player->inventory_size++] = element;
13530 DrawGameDoorValues();
13532 else if (element == EL_DYNABOMB_INCREASE_NUMBER)
13534 player->dynabomb_count++;
13535 player->dynabombs_left++;
13537 else if (element == EL_DYNABOMB_INCREASE_SIZE)
13539 player->dynabomb_size++;
13541 else if (element == EL_DYNABOMB_INCREASE_POWER)
13543 player->dynabomb_xl = TRUE;
13545 else if (IS_KEY(element))
13547 player->key[KEY_NR(element)] = TRUE;
13549 DrawGameDoorValues();
13551 else if (element == EL_DC_KEY_WHITE)
13553 player->num_white_keys++;
13555 /* display white keys? */
13556 /* DrawGameDoorValues(); */
13558 else if (IS_ENVELOPE(element))
13560 player->show_envelope = element;
13562 else if (element == EL_EMC_LENSES)
13564 game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
13566 RedrawAllInvisibleElementsForLenses();
13568 else if (element == EL_EMC_MAGNIFIER)
13570 game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
13572 RedrawAllInvisibleElementsForMagnifier();
13574 else if (IS_DROPPABLE(element) ||
13575 IS_THROWABLE(element)) /* can be collected and dropped */
13579 if (collect_count == 0)
13580 player->inventory_infinite_element = element;
13582 for (i = 0; i < collect_count; i++)
13583 if (player->inventory_size < MAX_INVENTORY_SIZE)
13584 player->inventory_element[player->inventory_size++] = element;
13586 DrawGameDoorValues();
13588 else if (collect_count > 0)
13590 local_player->gems_still_needed -= collect_count;
13591 if (local_player->gems_still_needed < 0)
13592 local_player->gems_still_needed = 0;
13594 game.snapshot.collected_item = TRUE;
13596 game_panel_controls[GAME_PANEL_GEMS].value = local_player->gems_still_needed;
13598 DisplayGameControlValues();
13601 RaiseScoreElement(element);
13602 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
13605 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
13606 player->index_bit, dig_side);
13608 if (mode == DF_SNAP)
13610 if (level.block_snap_field)
13611 setFieldForSnapping(x, y, element, move_direction);
13613 TestIfElementTouchesCustomElement(x, y); /* for empty space */
13615 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13616 player->index_bit, dig_side);
13619 else if (player_can_move_or_snap && IS_PUSHABLE(element))
13621 if (mode == DF_SNAP && element != EL_BD_ROCK)
13622 return MP_NO_ACTION;
13624 if (CAN_FALL(element) && dy)
13625 return MP_NO_ACTION;
13627 if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
13628 !(element == EL_SPRING && level.use_spring_bug))
13629 return MP_NO_ACTION;
13631 if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
13632 ((move_direction & MV_VERTICAL &&
13633 ((element_info[element].move_pattern & MV_LEFT &&
13634 IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
13635 (element_info[element].move_pattern & MV_RIGHT &&
13636 IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
13637 (move_direction & MV_HORIZONTAL &&
13638 ((element_info[element].move_pattern & MV_UP &&
13639 IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
13640 (element_info[element].move_pattern & MV_DOWN &&
13641 IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
13642 return MP_NO_ACTION;
13644 /* do not push elements already moving away faster than player */
13645 if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
13646 ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
13647 return MP_NO_ACTION;
13649 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
13651 if (player->push_delay_value == -1 || !player_was_pushing)
13652 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13654 else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
13656 if (player->push_delay_value == -1)
13657 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13659 else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
13661 if (!player->is_pushing)
13662 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13665 player->is_pushing = TRUE;
13666 player->is_active = TRUE;
13668 if (!(IN_LEV_FIELD(nextx, nexty) &&
13669 (IS_FREE(nextx, nexty) ||
13670 (IS_SB_ELEMENT(element) &&
13671 Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY) ||
13672 (IS_CUSTOM_ELEMENT(element) &&
13673 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty)))))
13674 return MP_NO_ACTION;
13676 if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
13677 return MP_NO_ACTION;
13679 if (player->push_delay == -1) /* new pushing; restart delay */
13680 player->push_delay = 0;
13682 if (player->push_delay < player->push_delay_value &&
13683 !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
13684 element != EL_SPRING && element != EL_BALLOON)
13686 /* make sure that there is no move delay before next try to push */
13687 if (game.engine_version >= VERSION_IDENT(3,0,7,1))
13688 player->move_delay = 0;
13690 return MP_NO_ACTION;
13693 if (IS_CUSTOM_ELEMENT(element) &&
13694 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty))
13696 if (!DigFieldByCE(nextx, nexty, element))
13697 return MP_NO_ACTION;
13700 if (IS_SB_ELEMENT(element))
13702 if (element == EL_SOKOBAN_FIELD_FULL)
13704 Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
13705 local_player->sokobanfields_still_needed++;
13708 if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
13710 Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
13711 local_player->sokobanfields_still_needed--;
13714 Feld[x][y] = EL_SOKOBAN_OBJECT;
13716 if (Back[x][y] == Back[nextx][nexty])
13717 PlayLevelSoundAction(x, y, ACTION_PUSHING);
13718 else if (Back[x][y] != 0)
13719 PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
13722 PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
13725 if (local_player->sokobanfields_still_needed == 0 &&
13726 (game.emulation == EMU_SOKOBAN || level.auto_exit_sokoban))
13728 PlayerWins(player);
13730 PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
13734 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
13736 InitMovingField(x, y, move_direction);
13737 GfxAction[x][y] = ACTION_PUSHING;
13739 if (mode == DF_SNAP)
13740 ContinueMoving(x, y);
13742 MovPos[x][y] = (dx != 0 ? dx : dy);
13744 Pushed[x][y] = TRUE;
13745 Pushed[nextx][nexty] = TRUE;
13747 if (game.engine_version < VERSION_IDENT(2,2,0,7))
13748 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13750 player->push_delay_value = -1; /* get new value later */
13752 /* check for element change _after_ element has been pushed */
13753 if (game.use_change_when_pushing_bug)
13755 CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
13756 player->index_bit, dig_side);
13757 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
13758 player->index_bit, dig_side);
13761 else if (IS_SWITCHABLE(element))
13763 if (PLAYER_SWITCHING(player, x, y))
13765 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
13766 player->index_bit, dig_side);
13771 player->is_switching = TRUE;
13772 player->switch_x = x;
13773 player->switch_y = y;
13775 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
13777 if (element == EL_ROBOT_WHEEL)
13779 Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
13783 game.robot_wheel_active = TRUE;
13785 TEST_DrawLevelField(x, y);
13787 else if (element == EL_SP_TERMINAL)
13791 SCAN_PLAYFIELD(xx, yy)
13793 if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
13797 else if (Feld[xx][yy] == EL_SP_TERMINAL)
13799 Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
13801 ResetGfxAnimation(xx, yy);
13802 TEST_DrawLevelField(xx, yy);
13806 else if (IS_BELT_SWITCH(element))
13808 ToggleBeltSwitch(x, y);
13810 else if (element == EL_SWITCHGATE_SWITCH_UP ||
13811 element == EL_SWITCHGATE_SWITCH_DOWN ||
13812 element == EL_DC_SWITCHGATE_SWITCH_UP ||
13813 element == EL_DC_SWITCHGATE_SWITCH_DOWN)
13815 ToggleSwitchgateSwitch(x, y);
13817 else if (element == EL_LIGHT_SWITCH ||
13818 element == EL_LIGHT_SWITCH_ACTIVE)
13820 ToggleLightSwitch(x, y);
13822 else if (element == EL_TIMEGATE_SWITCH ||
13823 element == EL_DC_TIMEGATE_SWITCH)
13825 ActivateTimegateSwitch(x, y);
13827 else if (element == EL_BALLOON_SWITCH_LEFT ||
13828 element == EL_BALLOON_SWITCH_RIGHT ||
13829 element == EL_BALLOON_SWITCH_UP ||
13830 element == EL_BALLOON_SWITCH_DOWN ||
13831 element == EL_BALLOON_SWITCH_NONE ||
13832 element == EL_BALLOON_SWITCH_ANY)
13834 game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT ? MV_LEFT :
13835 element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
13836 element == EL_BALLOON_SWITCH_UP ? MV_UP :
13837 element == EL_BALLOON_SWITCH_DOWN ? MV_DOWN :
13838 element == EL_BALLOON_SWITCH_NONE ? MV_NONE :
13841 else if (element == EL_LAMP)
13843 Feld[x][y] = EL_LAMP_ACTIVE;
13844 local_player->lights_still_needed--;
13846 ResetGfxAnimation(x, y);
13847 TEST_DrawLevelField(x, y);
13849 else if (element == EL_TIME_ORB_FULL)
13851 Feld[x][y] = EL_TIME_ORB_EMPTY;
13853 if (level.time > 0 || level.use_time_orb_bug)
13855 TimeLeft += level.time_orb_time;
13856 game.no_time_limit = FALSE;
13858 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
13860 DisplayGameControlValues();
13863 ResetGfxAnimation(x, y);
13864 TEST_DrawLevelField(x, y);
13866 else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
13867 element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
13871 game.ball_state = !game.ball_state;
13873 SCAN_PLAYFIELD(xx, yy)
13875 int e = Feld[xx][yy];
13877 if (game.ball_state)
13879 if (e == EL_EMC_MAGIC_BALL)
13880 CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
13881 else if (e == EL_EMC_MAGIC_BALL_SWITCH)
13882 CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
13886 if (e == EL_EMC_MAGIC_BALL_ACTIVE)
13887 CreateField(xx, yy, EL_EMC_MAGIC_BALL);
13888 else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
13889 CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
13894 CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
13895 player->index_bit, dig_side);
13897 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
13898 player->index_bit, dig_side);
13900 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
13901 player->index_bit, dig_side);
13907 if (!PLAYER_SWITCHING(player, x, y))
13909 player->is_switching = TRUE;
13910 player->switch_x = x;
13911 player->switch_y = y;
13913 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
13914 player->index_bit, dig_side);
13915 CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
13916 player->index_bit, dig_side);
13918 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
13919 player->index_bit, dig_side);
13920 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
13921 player->index_bit, dig_side);
13924 CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
13925 player->index_bit, dig_side);
13926 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
13927 player->index_bit, dig_side);
13929 return MP_NO_ACTION;
13932 player->push_delay = -1;
13934 if (is_player) /* function can also be called by EL_PENGUIN */
13936 if (Feld[x][y] != element) /* really digged/collected something */
13938 player->is_collecting = !player->is_digging;
13939 player->is_active = TRUE;
13946 static boolean DigFieldByCE(int x, int y, int digging_element)
13948 int element = Feld[x][y];
13950 if (!IS_FREE(x, y))
13952 int action = (IS_DIGGABLE(element) ? ACTION_DIGGING :
13953 IS_COLLECTIBLE(element) ? ACTION_COLLECTING :
13956 /* no element can dig solid indestructible elements */
13957 if (IS_INDESTRUCTIBLE(element) &&
13958 !IS_DIGGABLE(element) &&
13959 !IS_COLLECTIBLE(element))
13962 if (AmoebaNr[x][y] &&
13963 (element == EL_AMOEBA_FULL ||
13964 element == EL_BD_AMOEBA ||
13965 element == EL_AMOEBA_GROWING))
13967 AmoebaCnt[AmoebaNr[x][y]]--;
13968 AmoebaCnt2[AmoebaNr[x][y]]--;
13971 if (IS_MOVING(x, y))
13972 RemoveMovingField(x, y);
13976 TEST_DrawLevelField(x, y);
13979 /* if digged element was about to explode, prevent the explosion */
13980 ExplodeField[x][y] = EX_TYPE_NONE;
13982 PlayLevelSoundAction(x, y, action);
13985 Store[x][y] = EL_EMPTY;
13987 /* this makes it possible to leave the removed element again */
13988 if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element)))
13989 Store[x][y] = element;
13994 static boolean SnapField(struct PlayerInfo *player, int dx, int dy)
13996 int jx = player->jx, jy = player->jy;
13997 int x = jx + dx, y = jy + dy;
13998 int snap_direction = (dx == -1 ? MV_LEFT :
13999 dx == +1 ? MV_RIGHT :
14001 dy == +1 ? MV_DOWN : MV_NONE);
14002 boolean can_continue_snapping = (level.continuous_snapping &&
14003 WasJustFalling[x][y] < CHECK_DELAY_FALLING);
14005 if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
14008 if (!player->active || !IN_LEV_FIELD(x, y))
14016 if (player->MovPos == 0)
14017 player->is_pushing = FALSE;
14019 player->is_snapping = FALSE;
14021 if (player->MovPos == 0)
14023 player->is_moving = FALSE;
14024 player->is_digging = FALSE;
14025 player->is_collecting = FALSE;
14031 /* prevent snapping with already pressed snap key when not allowed */
14032 if (player->is_snapping && !can_continue_snapping)
14035 player->MovDir = snap_direction;
14037 if (player->MovPos == 0)
14039 player->is_moving = FALSE;
14040 player->is_digging = FALSE;
14041 player->is_collecting = FALSE;
14044 player->is_dropping = FALSE;
14045 player->is_dropping_pressed = FALSE;
14046 player->drop_pressed_delay = 0;
14048 if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
14051 player->is_snapping = TRUE;
14052 player->is_active = TRUE;
14054 if (player->MovPos == 0)
14056 player->is_moving = FALSE;
14057 player->is_digging = FALSE;
14058 player->is_collecting = FALSE;
14061 if (player->MovPos != 0) /* prevent graphic bugs in versions < 2.2.0 */
14062 TEST_DrawLevelField(player->last_jx, player->last_jy);
14064 TEST_DrawLevelField(x, y);
14069 static boolean DropElement(struct PlayerInfo *player)
14071 int old_element, new_element;
14072 int dropx = player->jx, dropy = player->jy;
14073 int drop_direction = player->MovDir;
14074 int drop_side = drop_direction;
14075 int drop_element = get_next_dropped_element(player);
14077 /* do not drop an element on top of another element; when holding drop key
14078 pressed without moving, dropped element must move away before the next
14079 element can be dropped (this is especially important if the next element
14080 is dynamite, which can be placed on background for historical reasons) */
14081 if (PLAYER_DROPPING(player, dropx, dropy) && Feld[dropx][dropy] != EL_EMPTY)
14084 if (IS_THROWABLE(drop_element))
14086 dropx += GET_DX_FROM_DIR(drop_direction);
14087 dropy += GET_DY_FROM_DIR(drop_direction);
14089 if (!IN_LEV_FIELD(dropx, dropy))
14093 old_element = Feld[dropx][dropy]; /* old element at dropping position */
14094 new_element = drop_element; /* default: no change when dropping */
14096 /* check if player is active, not moving and ready to drop */
14097 if (!player->active || player->MovPos || player->drop_delay > 0)
14100 /* check if player has anything that can be dropped */
14101 if (new_element == EL_UNDEFINED)
14104 /* only set if player has anything that can be dropped */
14105 player->is_dropping_pressed = TRUE;
14107 /* check if drop key was pressed long enough for EM style dynamite */
14108 if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
14111 /* check if anything can be dropped at the current position */
14112 if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
14115 /* collected custom elements can only be dropped on empty fields */
14116 if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
14119 if (old_element != EL_EMPTY)
14120 Back[dropx][dropy] = old_element; /* store old element on this field */
14122 ResetGfxAnimation(dropx, dropy);
14123 ResetRandomAnimationValue(dropx, dropy);
14125 if (player->inventory_size > 0 ||
14126 player->inventory_infinite_element != EL_UNDEFINED)
14128 if (player->inventory_size > 0)
14130 player->inventory_size--;
14132 DrawGameDoorValues();
14134 if (new_element == EL_DYNAMITE)
14135 new_element = EL_DYNAMITE_ACTIVE;
14136 else if (new_element == EL_EM_DYNAMITE)
14137 new_element = EL_EM_DYNAMITE_ACTIVE;
14138 else if (new_element == EL_SP_DISK_RED)
14139 new_element = EL_SP_DISK_RED_ACTIVE;
14142 Feld[dropx][dropy] = new_element;
14144 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
14145 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
14146 el2img(Feld[dropx][dropy]), 0);
14148 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
14150 /* needed if previous element just changed to "empty" in the last frame */
14151 ChangeCount[dropx][dropy] = 0; /* allow at least one more change */
14153 CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
14154 player->index_bit, drop_side);
14155 CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
14157 player->index_bit, drop_side);
14159 TestIfElementTouchesCustomElement(dropx, dropy);
14161 else /* player is dropping a dyna bomb */
14163 player->dynabombs_left--;
14165 Feld[dropx][dropy] = new_element;
14167 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
14168 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
14169 el2img(Feld[dropx][dropy]), 0);
14171 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
14174 if (Feld[dropx][dropy] == new_element) /* uninitialized unless CE change */
14175 InitField_WithBug1(dropx, dropy, FALSE);
14177 new_element = Feld[dropx][dropy]; /* element might have changed */
14179 if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
14180 element_info[new_element].move_pattern == MV_WHEN_DROPPED)
14182 if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
14183 MovDir[dropx][dropy] = drop_direction;
14185 ChangeCount[dropx][dropy] = 0; /* allow at least one more change */
14187 /* do not cause impact style collision by dropping elements that can fall */
14188 CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
14191 player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
14192 player->is_dropping = TRUE;
14194 player->drop_pressed_delay = 0;
14195 player->is_dropping_pressed = FALSE;
14197 player->drop_x = dropx;
14198 player->drop_y = dropy;
14203 /* ------------------------------------------------------------------------- */
14204 /* game sound playing functions */
14205 /* ------------------------------------------------------------------------- */
14207 static int *loop_sound_frame = NULL;
14208 static int *loop_sound_volume = NULL;
14210 void InitPlayLevelSound()
14212 int num_sounds = getSoundListSize();
14214 checked_free(loop_sound_frame);
14215 checked_free(loop_sound_volume);
14217 loop_sound_frame = checked_calloc(num_sounds * sizeof(int));
14218 loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
14221 static void PlayLevelSound(int x, int y, int nr)
14223 int sx = SCREENX(x), sy = SCREENY(y);
14224 int volume, stereo_position;
14225 int max_distance = 8;
14226 int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
14228 if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
14229 (!setup.sound_loops && IS_LOOP_SOUND(nr)))
14232 if (!IN_LEV_FIELD(x, y) ||
14233 sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
14234 sy < -max_distance || sy >= SCR_FIELDY + max_distance)
14237 volume = SOUND_MAX_VOLUME;
14239 if (!IN_SCR_FIELD(sx, sy))
14241 int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
14242 int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
14244 volume -= volume * (dx > dy ? dx : dy) / max_distance;
14247 stereo_position = (SOUND_MAX_LEFT +
14248 (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
14249 (SCR_FIELDX + 2 * max_distance));
14251 if (IS_LOOP_SOUND(nr))
14253 /* This assures that quieter loop sounds do not overwrite louder ones,
14254 while restarting sound volume comparison with each new game frame. */
14256 if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
14259 loop_sound_volume[nr] = volume;
14260 loop_sound_frame[nr] = FrameCounter;
14263 PlaySoundExt(nr, volume, stereo_position, type);
14266 static void PlayLevelSoundNearest(int x, int y, int sound_action)
14268 PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
14269 x > LEVELX(BX2) ? LEVELX(BX2) : x,
14270 y < LEVELY(BY1) ? LEVELY(BY1) :
14271 y > LEVELY(BY2) ? LEVELY(BY2) : y,
14275 static void PlayLevelSoundAction(int x, int y, int action)
14277 PlayLevelSoundElementAction(x, y, Feld[x][y], action);
14280 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
14282 int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
14284 if (sound_effect != SND_UNDEFINED)
14285 PlayLevelSound(x, y, sound_effect);
14288 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
14291 int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
14293 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14294 PlayLevelSound(x, y, sound_effect);
14297 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
14299 int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
14301 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14302 PlayLevelSound(x, y, sound_effect);
14305 static void StopLevelSoundActionIfLoop(int x, int y, int action)
14307 int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
14309 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14310 StopSound(sound_effect);
14313 static int getLevelMusicNr()
14315 if (levelset.music[level_nr] != MUS_UNDEFINED)
14316 return levelset.music[level_nr]; /* from config file */
14318 return MAP_NOCONF_MUSIC(level_nr); /* from music dir */
14321 static void FadeLevelSounds()
14326 static void FadeLevelMusic()
14328 int music_nr = getLevelMusicNr();
14329 char *curr_music = getCurrentlyPlayingMusicFilename();
14330 char *next_music = getMusicInfoEntryFilename(music_nr);
14332 if (!strEqual(curr_music, next_music))
14336 void FadeLevelSoundsAndMusic()
14342 static void PlayLevelMusic()
14344 int music_nr = getLevelMusicNr();
14345 char *curr_music = getCurrentlyPlayingMusicFilename();
14346 char *next_music = getMusicInfoEntryFilename(music_nr);
14348 if (!strEqual(curr_music, next_music))
14349 PlayMusic(music_nr);
14352 void PlayLevelSound_EM(int xx, int yy, int element_em, int sample)
14354 int element = (element_em > -1 ? map_element_EM_to_RND(element_em) : 0);
14355 int offset = (BorderElement == EL_STEELWALL ? 1 : 0);
14356 int x = xx - 1 - offset;
14357 int y = yy - 1 - offset;
14362 PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
14366 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14370 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14374 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14378 PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
14382 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14386 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14389 case SAMPLE_android_clone:
14390 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14393 case SAMPLE_android_move:
14394 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14397 case SAMPLE_spring:
14398 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14402 PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
14406 PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
14409 case SAMPLE_eater_eat:
14410 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14414 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14417 case SAMPLE_collect:
14418 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
14421 case SAMPLE_diamond:
14422 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14425 case SAMPLE_squash:
14426 /* !!! CHECK THIS !!! */
14428 PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
14430 PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
14434 case SAMPLE_wonderfall:
14435 PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
14439 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14443 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14447 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14451 PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
14455 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14459 PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
14462 case SAMPLE_wonder:
14463 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14467 PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
14470 case SAMPLE_exit_open:
14471 PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
14474 case SAMPLE_exit_leave:
14475 PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
14478 case SAMPLE_dynamite:
14479 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14483 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14487 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
14491 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14495 PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
14499 PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
14503 PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
14507 PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
14512 void PlayLevelSound_SP(int xx, int yy, int element_sp, int action_sp)
14514 int element = map_element_SP_to_RND(element_sp);
14515 int action = map_action_SP_to_RND(action_sp);
14516 int offset = (setup.sp_show_border_elements ? 0 : 1);
14517 int x = xx - offset;
14518 int y = yy - offset;
14520 PlayLevelSoundElementAction(x, y, element, action);
14523 void RaiseScore(int value)
14525 local_player->score += value;
14527 game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
14529 DisplayGameControlValues();
14532 void RaiseScoreElement(int element)
14537 case EL_BD_DIAMOND:
14538 case EL_EMERALD_YELLOW:
14539 case EL_EMERALD_RED:
14540 case EL_EMERALD_PURPLE:
14541 case EL_SP_INFOTRON:
14542 RaiseScore(level.score[SC_EMERALD]);
14545 RaiseScore(level.score[SC_DIAMOND]);
14548 RaiseScore(level.score[SC_CRYSTAL]);
14551 RaiseScore(level.score[SC_PEARL]);
14554 case EL_BD_BUTTERFLY:
14555 case EL_SP_ELECTRON:
14556 RaiseScore(level.score[SC_BUG]);
14559 case EL_BD_FIREFLY:
14560 case EL_SP_SNIKSNAK:
14561 RaiseScore(level.score[SC_SPACESHIP]);
14564 case EL_DARK_YAMYAM:
14565 RaiseScore(level.score[SC_YAMYAM]);
14568 RaiseScore(level.score[SC_ROBOT]);
14571 RaiseScore(level.score[SC_PACMAN]);
14574 RaiseScore(level.score[SC_NUT]);
14577 case EL_EM_DYNAMITE:
14578 case EL_SP_DISK_RED:
14579 case EL_DYNABOMB_INCREASE_NUMBER:
14580 case EL_DYNABOMB_INCREASE_SIZE:
14581 case EL_DYNABOMB_INCREASE_POWER:
14582 RaiseScore(level.score[SC_DYNAMITE]);
14584 case EL_SHIELD_NORMAL:
14585 case EL_SHIELD_DEADLY:
14586 RaiseScore(level.score[SC_SHIELD]);
14588 case EL_EXTRA_TIME:
14589 RaiseScore(level.extra_time_score);
14603 case EL_DC_KEY_WHITE:
14604 RaiseScore(level.score[SC_KEY]);
14607 RaiseScore(element_info[element].collect_score);
14612 void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message)
14614 if (skip_request || Request(message, REQ_ASK | REQ_STAY_CLOSED))
14616 /* closing door required in case of envelope style request dialogs */
14618 CloseDoor(DOOR_CLOSE_1);
14620 #if defined(NETWORK_AVALIABLE)
14621 if (options.network)
14622 SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
14627 FadeSkipNextFadeIn();
14629 SetGameStatus(GAME_MODE_MAIN);
14634 else /* continue playing the game */
14636 if (tape.playing && tape.deactivate_display)
14637 TapeDeactivateDisplayOff(TRUE);
14639 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
14641 if (tape.playing && tape.deactivate_display)
14642 TapeDeactivateDisplayOn();
14646 void RequestQuitGame(boolean ask_if_really_quit)
14648 boolean quick_quit = (!ask_if_really_quit || level_editor_test_game);
14649 boolean skip_request = AllPlayersGone || quick_quit;
14651 RequestQuitGameExt(skip_request, quick_quit,
14652 "Do you really want to quit the game?");
14656 /* ------------------------------------------------------------------------- */
14657 /* random generator functions */
14658 /* ------------------------------------------------------------------------- */
14660 unsigned int InitEngineRandom_RND(int seed)
14662 game.num_random_calls = 0;
14664 return InitEngineRandom(seed);
14667 unsigned int RND(int max)
14671 game.num_random_calls++;
14673 return GetEngineRandom(max);
14680 /* ------------------------------------------------------------------------- */
14681 /* game engine snapshot handling functions */
14682 /* ------------------------------------------------------------------------- */
14684 struct EngineSnapshotInfo
14686 /* runtime values for custom element collect score */
14687 int collect_score[NUM_CUSTOM_ELEMENTS];
14689 /* runtime values for group element choice position */
14690 int choice_pos[NUM_GROUP_ELEMENTS];
14692 /* runtime values for belt position animations */
14693 int belt_graphic[4][NUM_BELT_PARTS];
14694 int belt_anim_mode[4][NUM_BELT_PARTS];
14697 static struct EngineSnapshotInfo engine_snapshot_rnd;
14698 static char *snapshot_level_identifier = NULL;
14699 static int snapshot_level_nr = -1;
14701 static void SaveEngineSnapshotValues_RND()
14703 static int belt_base_active_element[4] =
14705 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
14706 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
14707 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
14708 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
14712 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
14714 int element = EL_CUSTOM_START + i;
14716 engine_snapshot_rnd.collect_score[i] = element_info[element].collect_score;
14719 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
14721 int element = EL_GROUP_START + i;
14723 engine_snapshot_rnd.choice_pos[i] = element_info[element].group->choice_pos;
14726 for (i = 0; i < 4; i++)
14728 for (j = 0; j < NUM_BELT_PARTS; j++)
14730 int element = belt_base_active_element[i] + j;
14731 int graphic = el2img(element);
14732 int anim_mode = graphic_info[graphic].anim_mode;
14734 engine_snapshot_rnd.belt_graphic[i][j] = graphic;
14735 engine_snapshot_rnd.belt_anim_mode[i][j] = anim_mode;
14740 static void LoadEngineSnapshotValues_RND()
14742 unsigned int num_random_calls = game.num_random_calls;
14745 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
14747 int element = EL_CUSTOM_START + i;
14749 element_info[element].collect_score = engine_snapshot_rnd.collect_score[i];
14752 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
14754 int element = EL_GROUP_START + i;
14756 element_info[element].group->choice_pos = engine_snapshot_rnd.choice_pos[i];
14759 for (i = 0; i < 4; i++)
14761 for (j = 0; j < NUM_BELT_PARTS; j++)
14763 int graphic = engine_snapshot_rnd.belt_graphic[i][j];
14764 int anim_mode = engine_snapshot_rnd.belt_anim_mode[i][j];
14766 graphic_info[graphic].anim_mode = anim_mode;
14770 if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
14772 InitRND(tape.random_seed);
14773 for (i = 0; i < num_random_calls; i++)
14777 if (game.num_random_calls != num_random_calls)
14779 Error(ERR_INFO, "number of random calls out of sync");
14780 Error(ERR_INFO, "number of random calls should be %d", num_random_calls);
14781 Error(ERR_INFO, "number of random calls is %d", game.num_random_calls);
14782 Error(ERR_EXIT, "this should not happen -- please debug");
14786 void FreeEngineSnapshotSingle()
14788 FreeSnapshotSingle();
14790 setString(&snapshot_level_identifier, NULL);
14791 snapshot_level_nr = -1;
14794 void FreeEngineSnapshotList()
14796 FreeSnapshotList();
14799 ListNode *SaveEngineSnapshotBuffers()
14801 ListNode *buffers = NULL;
14803 /* copy some special values to a structure better suited for the snapshot */
14805 if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
14806 SaveEngineSnapshotValues_RND();
14807 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
14808 SaveEngineSnapshotValues_EM();
14809 if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
14810 SaveEngineSnapshotValues_SP(&buffers);
14811 if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
14812 SaveEngineSnapshotValues_MM(&buffers);
14814 /* save values stored in special snapshot structure */
14816 if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
14817 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
14818 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
14819 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
14820 if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
14821 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_sp));
14822 if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
14823 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_mm));
14825 /* save further RND engine values */
14827 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(stored_player));
14828 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(game));
14829 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(tape));
14831 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ZX));
14832 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ZY));
14833 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExitX));
14834 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExitY));
14836 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(FrameCounter));
14837 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeFrames));
14838 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimePlayed));
14839 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeLeft));
14840 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TapeTime));
14842 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovDir));
14843 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovPos));
14844 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenGfxPos));
14846 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScrollStepSize));
14848 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AllPlayersGone));
14850 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt));
14851 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2));
14853 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Feld));
14854 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovPos));
14855 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDir));
14856 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDelay));
14857 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeDelay));
14858 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangePage));
14859 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CustomValue));
14860 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store));
14861 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store2));
14862 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(StorePlayer));
14863 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Back));
14864 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaNr));
14865 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustMoving));
14866 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustFalling));
14867 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckCollision));
14868 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckImpact));
14869 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Stop));
14870 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Pushed));
14872 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeCount));
14873 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeEvent));
14875 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodePhase));
14876 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeDelay));
14877 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeField));
14879 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(RunnerVisit));
14880 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(PlayerVisit));
14882 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxFrame));
14883 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxRandom));
14884 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxElement));
14885 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxAction));
14886 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxDir));
14888 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_x));
14889 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_y));
14892 ListNode *node = engine_snapshot_list_rnd;
14895 while (node != NULL)
14897 num_bytes += ((struct EngineSnapshotNodeInfo *)node->content)->size;
14902 printf("::: size of engine snapshot: %d bytes\n", num_bytes);
14908 void SaveEngineSnapshotSingle()
14910 ListNode *buffers = SaveEngineSnapshotBuffers();
14912 /* finally save all snapshot buffers to single snapshot */
14913 SaveSnapshotSingle(buffers);
14915 /* save level identification information */
14916 setString(&snapshot_level_identifier, leveldir_current->identifier);
14917 snapshot_level_nr = level_nr;
14920 boolean CheckSaveEngineSnapshotToList()
14922 boolean save_snapshot =
14923 ((game.snapshot.mode == SNAPSHOT_MODE_EVERY_STEP) ||
14924 (game.snapshot.mode == SNAPSHOT_MODE_EVERY_MOVE &&
14925 game.snapshot.changed_action) ||
14926 (game.snapshot.mode == SNAPSHOT_MODE_EVERY_COLLECT &&
14927 game.snapshot.collected_item));
14929 game.snapshot.changed_action = FALSE;
14930 game.snapshot.collected_item = FALSE;
14931 game.snapshot.save_snapshot = save_snapshot;
14933 return save_snapshot;
14936 void SaveEngineSnapshotToList()
14938 if (game.snapshot.mode == SNAPSHOT_MODE_OFF ||
14942 ListNode *buffers = SaveEngineSnapshotBuffers();
14944 /* finally save all snapshot buffers to snapshot list */
14945 SaveSnapshotToList(buffers);
14948 void SaveEngineSnapshotToListInitial()
14950 FreeEngineSnapshotList();
14952 SaveEngineSnapshotToList();
14955 void LoadEngineSnapshotValues()
14957 /* restore special values from snapshot structure */
14959 if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
14960 LoadEngineSnapshotValues_RND();
14961 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
14962 LoadEngineSnapshotValues_EM();
14963 if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
14964 LoadEngineSnapshotValues_SP();
14965 if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
14966 LoadEngineSnapshotValues_MM();
14969 void LoadEngineSnapshotSingle()
14971 LoadSnapshotSingle();
14973 LoadEngineSnapshotValues();
14976 void LoadEngineSnapshot_Undo(int steps)
14978 LoadSnapshotFromList_Older(steps);
14980 LoadEngineSnapshotValues();
14983 void LoadEngineSnapshot_Redo(int steps)
14985 LoadSnapshotFromList_Newer(steps);
14987 LoadEngineSnapshotValues();
14990 boolean CheckEngineSnapshotSingle()
14992 return (strEqual(snapshot_level_identifier, leveldir_current->identifier) &&
14993 snapshot_level_nr == level_nr);
14996 boolean CheckEngineSnapshotList()
14998 return CheckSnapshotList();
15002 /* ---------- new game button stuff ---------------------------------------- */
15010 } gamebutton_info[NUM_GAME_BUTTONS] =
15013 IMG_GFX_GAME_BUTTON_STOP, &game.button.stop,
15014 GAME_CTRL_ID_STOP, "stop game"
15017 IMG_GFX_GAME_BUTTON_PAUSE, &game.button.pause,
15018 GAME_CTRL_ID_PAUSE, "pause game"
15021 IMG_GFX_GAME_BUTTON_PLAY, &game.button.play,
15022 GAME_CTRL_ID_PLAY, "play game"
15025 IMG_GFX_GAME_BUTTON_UNDO, &game.button.undo,
15026 GAME_CTRL_ID_UNDO, "undo step"
15029 IMG_GFX_GAME_BUTTON_REDO, &game.button.redo,
15030 GAME_CTRL_ID_REDO, "redo step"
15033 IMG_GFX_GAME_BUTTON_SAVE, &game.button.save,
15034 GAME_CTRL_ID_SAVE, "save game"
15037 IMG_GFX_GAME_BUTTON_PAUSE2, &game.button.pause2,
15038 GAME_CTRL_ID_PAUSE2, "pause game"
15041 IMG_GFX_GAME_BUTTON_LOAD, &game.button.load,
15042 GAME_CTRL_ID_LOAD, "load game"
15045 IMG_GFX_GAME_BUTTON_SOUND_MUSIC, &game.button.sound_music,
15046 SOUND_CTRL_ID_MUSIC, "background music on/off"
15049 IMG_GFX_GAME_BUTTON_SOUND_LOOPS, &game.button.sound_loops,
15050 SOUND_CTRL_ID_LOOPS, "sound loops on/off"
15053 IMG_GFX_GAME_BUTTON_SOUND_SIMPLE, &game.button.sound_simple,
15054 SOUND_CTRL_ID_SIMPLE, "normal sounds on/off"
15058 void CreateGameButtons()
15062 for (i = 0; i < NUM_GAME_BUTTONS; i++)
15064 struct GraphicInfo *gfx = &graphic_info[gamebutton_info[i].graphic];
15065 struct XY *pos = gamebutton_info[i].pos;
15066 struct GadgetInfo *gi;
15069 unsigned int event_mask;
15070 int base_x = (tape.show_game_buttons ? VX : DX);
15071 int base_y = (tape.show_game_buttons ? VY : DY);
15072 int gd_x = gfx->src_x;
15073 int gd_y = gfx->src_y;
15074 int gd_xp = gfx->src_x + gfx->pressed_xoffset;
15075 int gd_yp = gfx->src_y + gfx->pressed_yoffset;
15076 int gd_xa = gfx->src_x + gfx->active_xoffset;
15077 int gd_ya = gfx->src_y + gfx->active_yoffset;
15078 int gd_xap = gfx->src_x + gfx->active_xoffset + gfx->pressed_xoffset;
15079 int gd_yap = gfx->src_y + gfx->active_yoffset + gfx->pressed_yoffset;
15082 if (gfx->bitmap == NULL)
15084 game_gadget[id] = NULL;
15089 if (id == GAME_CTRL_ID_STOP ||
15090 id == GAME_CTRL_ID_PLAY ||
15091 id == GAME_CTRL_ID_SAVE ||
15092 id == GAME_CTRL_ID_LOAD)
15094 button_type = GD_TYPE_NORMAL_BUTTON;
15096 event_mask = GD_EVENT_RELEASED;
15098 else if (id == GAME_CTRL_ID_UNDO ||
15099 id == GAME_CTRL_ID_REDO)
15101 button_type = GD_TYPE_NORMAL_BUTTON;
15103 event_mask = GD_EVENT_PRESSED | GD_EVENT_REPEATED;
15107 button_type = GD_TYPE_CHECK_BUTTON;
15109 ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
15110 (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
15111 (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
15112 event_mask = GD_EVENT_PRESSED;
15115 gi = CreateGadget(GDI_CUSTOM_ID, id,
15116 GDI_INFO_TEXT, gamebutton_info[i].infotext,
15117 GDI_X, base_x + GDI_ACTIVE_POS(pos->x),
15118 GDI_Y, base_y + GDI_ACTIVE_POS(pos->y),
15119 GDI_WIDTH, gfx->width,
15120 GDI_HEIGHT, gfx->height,
15121 GDI_TYPE, button_type,
15122 GDI_STATE, GD_BUTTON_UNPRESSED,
15123 GDI_CHECKED, checked,
15124 GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
15125 GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
15126 GDI_ALT_DESIGN_UNPRESSED, gfx->bitmap, gd_xa, gd_ya,
15127 GDI_ALT_DESIGN_PRESSED, gfx->bitmap, gd_xap, gd_yap,
15128 GDI_DIRECT_DRAW, FALSE,
15129 GDI_EVENT_MASK, event_mask,
15130 GDI_CALLBACK_ACTION, HandleGameButtons,
15134 Error(ERR_EXIT, "cannot create gadget");
15136 game_gadget[id] = gi;
15140 void FreeGameButtons()
15144 for (i = 0; i < NUM_GAME_BUTTONS; i++)
15145 FreeGadget(game_gadget[i]);
15148 static void UnmapGameButtonsAtSamePosition(int id)
15152 for (i = 0; i < NUM_GAME_BUTTONS; i++)
15154 gamebutton_info[i].pos->x == gamebutton_info[id].pos->x &&
15155 gamebutton_info[i].pos->y == gamebutton_info[id].pos->y)
15156 UnmapGadget(game_gadget[i]);
15159 static void UnmapGameButtonsAtSamePosition_All()
15161 if (setup.show_snapshot_buttons)
15163 UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_SAVE);
15164 UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE2);
15165 UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_LOAD);
15169 UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_STOP);
15170 UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE);
15171 UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PLAY);
15175 static void MapGameButtonsAtSamePosition(int id)
15179 for (i = 0; i < NUM_GAME_BUTTONS; i++)
15181 gamebutton_info[i].pos->x == gamebutton_info[id].pos->x &&
15182 gamebutton_info[i].pos->y == gamebutton_info[id].pos->y)
15183 MapGadget(game_gadget[i]);
15185 UnmapGameButtonsAtSamePosition_All();
15188 void MapUndoRedoButtons()
15190 UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
15191 UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
15193 MapGadget(game_gadget[GAME_CTRL_ID_UNDO]);
15194 MapGadget(game_gadget[GAME_CTRL_ID_REDO]);
15196 ModifyGadget(game_gadget[GAME_CTRL_ID_PAUSE2], GDI_CHECKED, TRUE, GDI_END);
15199 void UnmapUndoRedoButtons()
15201 UnmapGadget(game_gadget[GAME_CTRL_ID_UNDO]);
15202 UnmapGadget(game_gadget[GAME_CTRL_ID_REDO]);
15204 MapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
15205 MapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
15207 ModifyGadget(game_gadget[GAME_CTRL_ID_PAUSE2], GDI_CHECKED, FALSE, GDI_END);
15210 void MapGameButtons()
15214 for (i = 0; i < NUM_GAME_BUTTONS; i++)
15215 if (i != GAME_CTRL_ID_UNDO &&
15216 i != GAME_CTRL_ID_REDO)
15217 MapGadget(game_gadget[i]);
15219 UnmapGameButtonsAtSamePosition_All();
15221 RedrawGameButtons();
15224 void UnmapGameButtons()
15228 for (i = 0; i < NUM_GAME_BUTTONS; i++)
15229 UnmapGadget(game_gadget[i]);
15232 void RedrawGameButtons()
15236 for (i = 0; i < NUM_GAME_BUTTONS; i++)
15237 RedrawGadget(game_gadget[i]);
15239 // RedrawGadget() may have set REDRAW_ALL if buttons are defined off-area
15240 redraw_mask &= ~REDRAW_ALL;
15243 void GameUndoRedoExt()
15245 ClearPlayerAction();
15247 tape.pausing = TRUE;
15250 UpdateAndDisplayGameControlValues();
15252 DrawCompleteVideoDisplay();
15253 DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
15254 DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
15255 DrawVideoDisplay(VIDEO_STATE_1STEP(tape.single_step), 0);
15260 void GameUndo(int steps)
15262 if (!CheckEngineSnapshotList())
15265 LoadEngineSnapshot_Undo(steps);
15270 void GameRedo(int steps)
15272 if (!CheckEngineSnapshotList())
15275 LoadEngineSnapshot_Redo(steps);
15280 static void HandleGameButtonsExt(int id, int button)
15282 static boolean game_undo_executed = FALSE;
15283 int steps = BUTTON_STEPSIZE(button);
15284 boolean handle_game_buttons =
15285 (game_status == GAME_MODE_PLAYING ||
15286 (game_status == GAME_MODE_MAIN && tape.show_game_buttons));
15288 if (!handle_game_buttons)
15293 case GAME_CTRL_ID_STOP:
15294 if (game_status == GAME_MODE_MAIN)
15300 RequestQuitGame(TRUE);
15304 case GAME_CTRL_ID_PAUSE:
15305 case GAME_CTRL_ID_PAUSE2:
15306 if (options.network && game_status == GAME_MODE_PLAYING)
15308 #if defined(NETWORK_AVALIABLE)
15310 SendToServer_ContinuePlaying();
15312 SendToServer_PausePlaying();
15316 TapeTogglePause(TAPE_TOGGLE_MANUAL);
15318 game_undo_executed = FALSE;
15322 case GAME_CTRL_ID_PLAY:
15323 if (game_status == GAME_MODE_MAIN)
15325 StartGameActions(options.network, setup.autorecord, level.random_seed);
15327 else if (tape.pausing)
15329 #if defined(NETWORK_AVALIABLE)
15330 if (options.network)
15331 SendToServer_ContinuePlaying();
15334 TapeTogglePause(TAPE_TOGGLE_MANUAL | TAPE_TOGGLE_PLAY_PAUSE);
15338 case GAME_CTRL_ID_UNDO:
15339 // Important: When using "save snapshot when collecting an item" mode,
15340 // load last (current) snapshot for first "undo" after pressing "pause"
15341 // (else the last-but-one snapshot would be loaded, because the snapshot
15342 // pointer already points to the last snapshot when pressing "pause",
15343 // which is fine for "every step/move" mode, but not for "every collect")
15344 if (game.snapshot.mode == SNAPSHOT_MODE_EVERY_COLLECT &&
15345 !game_undo_executed)
15348 game_undo_executed = TRUE;
15353 case GAME_CTRL_ID_REDO:
15357 case GAME_CTRL_ID_SAVE:
15361 case GAME_CTRL_ID_LOAD:
15365 case SOUND_CTRL_ID_MUSIC:
15366 if (setup.sound_music)
15368 setup.sound_music = FALSE;
15372 else if (audio.music_available)
15374 setup.sound = setup.sound_music = TRUE;
15376 SetAudioMode(setup.sound);
15382 case SOUND_CTRL_ID_LOOPS:
15383 if (setup.sound_loops)
15384 setup.sound_loops = FALSE;
15385 else if (audio.loops_available)
15387 setup.sound = setup.sound_loops = TRUE;
15389 SetAudioMode(setup.sound);
15393 case SOUND_CTRL_ID_SIMPLE:
15394 if (setup.sound_simple)
15395 setup.sound_simple = FALSE;
15396 else if (audio.sound_available)
15398 setup.sound = setup.sound_simple = TRUE;
15400 SetAudioMode(setup.sound);
15409 static void HandleGameButtons(struct GadgetInfo *gi)
15411 HandleGameButtonsExt(gi->custom_id, gi->event.button);
15414 void HandleSoundButtonKeys(Key key)
15417 if (key == setup.shortcut.sound_simple)
15418 ClickOnGadget(game_gadget[SOUND_CTRL_ID_SIMPLE], MB_LEFTBUTTON);
15419 else if (key == setup.shortcut.sound_loops)
15420 ClickOnGadget(game_gadget[SOUND_CTRL_ID_LOOPS], MB_LEFTBUTTON);
15421 else if (key == setup.shortcut.sound_music)
15422 ClickOnGadget(game_gadget[SOUND_CTRL_ID_MUSIC], MB_LEFTBUTTON);