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();
1056 static void HandleGameButtons(struct GadgetInfo *);
1058 int AmoebeNachbarNr(int, int);
1059 void AmoebeUmwandeln(int, int);
1060 void ContinueMoving(int, int);
1061 void Bang(int, int);
1062 void InitMovDir(int, int);
1063 void InitAmoebaNr(int, int);
1064 int NewHiScore(void);
1066 void TestIfGoodThingHitsBadThing(int, int, int);
1067 void TestIfBadThingHitsGoodThing(int, int, int);
1068 void TestIfPlayerTouchesBadThing(int, int);
1069 void TestIfPlayerRunsIntoBadThing(int, int, int);
1070 void TestIfBadThingTouchesPlayer(int, int);
1071 void TestIfBadThingRunsIntoPlayer(int, int, int);
1072 void TestIfFriendTouchesBadThing(int, int);
1073 void TestIfBadThingTouchesFriend(int, int);
1074 void TestIfBadThingTouchesOtherBadThing(int, int);
1075 void TestIfGoodThingGetsHitByBadThing(int, int, int);
1077 void KillPlayer(struct PlayerInfo *);
1078 void BuryPlayer(struct PlayerInfo *);
1079 void RemovePlayer(struct PlayerInfo *);
1081 static int getInvisibleActiveFromInvisibleElement(int);
1082 static int getInvisibleFromInvisibleActiveElement(int);
1084 static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
1086 /* for detection of endless loops, caused by custom element programming */
1087 /* (using maximal playfield width x 10 is just a rough approximation) */
1088 #define MAX_ELEMENT_CHANGE_RECURSION_DEPTH (MAX_PLAYFIELD_WIDTH * 10)
1090 #define RECURSION_LOOP_DETECTION_START(e, rc) \
1092 if (recursion_loop_detected) \
1095 if (recursion_loop_depth > MAX_ELEMENT_CHANGE_RECURSION_DEPTH) \
1097 recursion_loop_detected = TRUE; \
1098 recursion_loop_element = (e); \
1101 recursion_loop_depth++; \
1104 #define RECURSION_LOOP_DETECTION_END() \
1106 recursion_loop_depth--; \
1109 static int recursion_loop_depth;
1110 static boolean recursion_loop_detected;
1111 static boolean recursion_loop_element;
1113 static int map_player_action[MAX_PLAYERS];
1116 /* ------------------------------------------------------------------------- */
1117 /* definition of elements that automatically change to other elements after */
1118 /* a specified time, eventually calling a function when changing */
1119 /* ------------------------------------------------------------------------- */
1121 /* forward declaration for changer functions */
1122 static void InitBuggyBase(int, int);
1123 static void WarnBuggyBase(int, int);
1125 static void InitTrap(int, int);
1126 static void ActivateTrap(int, int);
1127 static void ChangeActiveTrap(int, int);
1129 static void InitRobotWheel(int, int);
1130 static void RunRobotWheel(int, int);
1131 static void StopRobotWheel(int, int);
1133 static void InitTimegateWheel(int, int);
1134 static void RunTimegateWheel(int, int);
1136 static void InitMagicBallDelay(int, int);
1137 static void ActivateMagicBall(int, int);
1139 struct ChangingElementInfo
1144 void (*pre_change_function)(int x, int y);
1145 void (*change_function)(int x, int y);
1146 void (*post_change_function)(int x, int y);
1149 static struct ChangingElementInfo change_delay_list[] =
1184 EL_STEEL_EXIT_OPENING,
1192 EL_STEEL_EXIT_CLOSING,
1193 EL_STEEL_EXIT_CLOSED,
1216 EL_EM_STEEL_EXIT_OPENING,
1217 EL_EM_STEEL_EXIT_OPEN,
1224 EL_EM_STEEL_EXIT_CLOSING,
1248 EL_SWITCHGATE_OPENING,
1256 EL_SWITCHGATE_CLOSING,
1257 EL_SWITCHGATE_CLOSED,
1264 EL_TIMEGATE_OPENING,
1272 EL_TIMEGATE_CLOSING,
1281 EL_ACID_SPLASH_LEFT,
1289 EL_ACID_SPLASH_RIGHT,
1298 EL_SP_BUGGY_BASE_ACTIVATING,
1305 EL_SP_BUGGY_BASE_ACTIVATING,
1306 EL_SP_BUGGY_BASE_ACTIVE,
1313 EL_SP_BUGGY_BASE_ACTIVE,
1337 EL_ROBOT_WHEEL_ACTIVE,
1345 EL_TIMEGATE_SWITCH_ACTIVE,
1353 EL_DC_TIMEGATE_SWITCH_ACTIVE,
1354 EL_DC_TIMEGATE_SWITCH,
1361 EL_EMC_MAGIC_BALL_ACTIVE,
1362 EL_EMC_MAGIC_BALL_ACTIVE,
1369 EL_EMC_SPRING_BUMPER_ACTIVE,
1370 EL_EMC_SPRING_BUMPER,
1377 EL_DIAGONAL_SHRINKING,
1385 EL_DIAGONAL_GROWING,
1406 int push_delay_fixed, push_delay_random;
1410 { EL_SPRING, 0, 0 },
1411 { EL_BALLOON, 0, 0 },
1413 { EL_SOKOBAN_OBJECT, 2, 0 },
1414 { EL_SOKOBAN_FIELD_FULL, 2, 0 },
1415 { EL_SATELLITE, 2, 0 },
1416 { EL_SP_DISK_YELLOW, 2, 0 },
1418 { EL_UNDEFINED, 0, 0 },
1426 move_stepsize_list[] =
1428 { EL_AMOEBA_DROP, 2 },
1429 { EL_AMOEBA_DROPPING, 2 },
1430 { EL_QUICKSAND_FILLING, 1 },
1431 { EL_QUICKSAND_EMPTYING, 1 },
1432 { EL_QUICKSAND_FAST_FILLING, 2 },
1433 { EL_QUICKSAND_FAST_EMPTYING, 2 },
1434 { EL_MAGIC_WALL_FILLING, 2 },
1435 { EL_MAGIC_WALL_EMPTYING, 2 },
1436 { EL_BD_MAGIC_WALL_FILLING, 2 },
1437 { EL_BD_MAGIC_WALL_EMPTYING, 2 },
1438 { EL_DC_MAGIC_WALL_FILLING, 2 },
1439 { EL_DC_MAGIC_WALL_EMPTYING, 2 },
1441 { EL_UNDEFINED, 0 },
1449 collect_count_list[] =
1452 { EL_BD_DIAMOND, 1 },
1453 { EL_EMERALD_YELLOW, 1 },
1454 { EL_EMERALD_RED, 1 },
1455 { EL_EMERALD_PURPLE, 1 },
1457 { EL_SP_INFOTRON, 1 },
1461 { EL_UNDEFINED, 0 },
1469 access_direction_list[] =
1471 { EL_TUBE_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1472 { EL_TUBE_VERTICAL, MV_UP | MV_DOWN },
1473 { EL_TUBE_HORIZONTAL, MV_LEFT | MV_RIGHT },
1474 { EL_TUBE_VERTICAL_LEFT, MV_LEFT | MV_UP | MV_DOWN },
1475 { EL_TUBE_VERTICAL_RIGHT, MV_RIGHT | MV_UP | MV_DOWN },
1476 { EL_TUBE_HORIZONTAL_UP, MV_LEFT | MV_RIGHT | MV_UP },
1477 { EL_TUBE_HORIZONTAL_DOWN, MV_LEFT | MV_RIGHT | MV_DOWN },
1478 { EL_TUBE_LEFT_UP, MV_LEFT | MV_UP },
1479 { EL_TUBE_LEFT_DOWN, MV_LEFT | MV_DOWN },
1480 { EL_TUBE_RIGHT_UP, MV_RIGHT | MV_UP },
1481 { EL_TUBE_RIGHT_DOWN, MV_RIGHT | MV_DOWN },
1483 { EL_SP_PORT_LEFT, MV_RIGHT },
1484 { EL_SP_PORT_RIGHT, MV_LEFT },
1485 { EL_SP_PORT_UP, MV_DOWN },
1486 { EL_SP_PORT_DOWN, MV_UP },
1487 { EL_SP_PORT_HORIZONTAL, MV_LEFT | MV_RIGHT },
1488 { EL_SP_PORT_VERTICAL, MV_UP | MV_DOWN },
1489 { EL_SP_PORT_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1490 { EL_SP_GRAVITY_PORT_LEFT, MV_RIGHT },
1491 { EL_SP_GRAVITY_PORT_RIGHT, MV_LEFT },
1492 { EL_SP_GRAVITY_PORT_UP, MV_DOWN },
1493 { EL_SP_GRAVITY_PORT_DOWN, MV_UP },
1494 { EL_SP_GRAVITY_ON_PORT_LEFT, MV_RIGHT },
1495 { EL_SP_GRAVITY_ON_PORT_RIGHT, MV_LEFT },
1496 { EL_SP_GRAVITY_ON_PORT_UP, MV_DOWN },
1497 { EL_SP_GRAVITY_ON_PORT_DOWN, MV_UP },
1498 { EL_SP_GRAVITY_OFF_PORT_LEFT, MV_RIGHT },
1499 { EL_SP_GRAVITY_OFF_PORT_RIGHT, MV_LEFT },
1500 { EL_SP_GRAVITY_OFF_PORT_UP, MV_DOWN },
1501 { EL_SP_GRAVITY_OFF_PORT_DOWN, MV_UP },
1503 { EL_UNDEFINED, MV_NONE }
1506 static boolean trigger_events[MAX_NUM_ELEMENTS][NUM_CHANGE_EVENTS];
1508 #define IS_AUTO_CHANGING(e) (element_info[e].has_change_event[CE_DELAY])
1509 #define IS_JUST_CHANGING(x, y) (ChangeDelay[x][y] != 0)
1510 #define IS_CHANGING(x, y) (IS_AUTO_CHANGING(Feld[x][y]) || \
1511 IS_JUST_CHANGING(x, y))
1513 #define CE_PAGE(e, ce) (element_info[e].event_page[ce])
1515 /* static variables for playfield scan mode (scanning forward or backward) */
1516 static int playfield_scan_start_x = 0;
1517 static int playfield_scan_start_y = 0;
1518 static int playfield_scan_delta_x = 1;
1519 static int playfield_scan_delta_y = 1;
1521 #define SCAN_PLAYFIELD(x, y) for ((y) = playfield_scan_start_y; \
1522 (y) >= 0 && (y) <= lev_fieldy - 1; \
1523 (y) += playfield_scan_delta_y) \
1524 for ((x) = playfield_scan_start_x; \
1525 (x) >= 0 && (x) <= lev_fieldx - 1; \
1526 (x) += playfield_scan_delta_x)
1529 void DEBUG_SetMaximumDynamite()
1533 for (i = 0; i < MAX_INVENTORY_SIZE; i++)
1534 if (local_player->inventory_size < MAX_INVENTORY_SIZE)
1535 local_player->inventory_element[local_player->inventory_size++] =
1540 static void InitPlayfieldScanModeVars()
1542 if (game.use_reverse_scan_direction)
1544 playfield_scan_start_x = lev_fieldx - 1;
1545 playfield_scan_start_y = lev_fieldy - 1;
1547 playfield_scan_delta_x = -1;
1548 playfield_scan_delta_y = -1;
1552 playfield_scan_start_x = 0;
1553 playfield_scan_start_y = 0;
1555 playfield_scan_delta_x = 1;
1556 playfield_scan_delta_y = 1;
1560 static void InitPlayfieldScanMode(int mode)
1562 game.use_reverse_scan_direction =
1563 (mode == CA_ARG_SCAN_MODE_REVERSE ? TRUE : FALSE);
1565 InitPlayfieldScanModeVars();
1568 static int get_move_delay_from_stepsize(int move_stepsize)
1571 MIN(MAX(MOVE_STEPSIZE_MIN, move_stepsize), MOVE_STEPSIZE_MAX);
1573 /* make sure that stepsize value is always a power of 2 */
1574 move_stepsize = (1 << log_2(move_stepsize));
1576 return TILEX / move_stepsize;
1579 static void SetPlayerMoveSpeed(struct PlayerInfo *player, int move_stepsize,
1582 int player_nr = player->index_nr;
1583 int move_delay = get_move_delay_from_stepsize(move_stepsize);
1584 boolean cannot_move = (move_stepsize == STEPSIZE_NOT_MOVING ? TRUE : FALSE);
1586 /* do no immediately change move delay -- the player might just be moving */
1587 player->move_delay_value_next = move_delay;
1589 /* information if player can move must be set separately */
1590 player->cannot_move = cannot_move;
1594 player->move_delay = game.initial_move_delay[player_nr];
1595 player->move_delay_value = game.initial_move_delay_value[player_nr];
1597 player->move_delay_value_next = -1;
1599 player->move_delay_reset_counter = 0;
1603 void GetPlayerConfig()
1605 GameFrameDelay = setup.game_frame_delay;
1607 if (!audio.sound_available)
1608 setup.sound_simple = FALSE;
1610 if (!audio.loops_available)
1611 setup.sound_loops = FALSE;
1613 if (!audio.music_available)
1614 setup.sound_music = FALSE;
1616 if (!video.fullscreen_available)
1617 setup.fullscreen = FALSE;
1619 setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
1621 SetAudioMode(setup.sound);
1624 int GetElementFromGroupElement(int element)
1626 if (IS_GROUP_ELEMENT(element))
1628 struct ElementGroupInfo *group = element_info[element].group;
1629 int last_anim_random_frame = gfx.anim_random_frame;
1632 if (group->choice_mode == ANIM_RANDOM)
1633 gfx.anim_random_frame = RND(group->num_elements_resolved);
1635 element_pos = getAnimationFrame(group->num_elements_resolved, 1,
1636 group->choice_mode, 0,
1639 if (group->choice_mode == ANIM_RANDOM)
1640 gfx.anim_random_frame = last_anim_random_frame;
1642 group->choice_pos++;
1644 element = group->element_resolved[element_pos];
1650 static void InitPlayerField(int x, int y, int element, boolean init_game)
1652 if (element == EL_SP_MURPHY)
1656 if (stored_player[0].present)
1658 Feld[x][y] = EL_SP_MURPHY_CLONE;
1664 stored_player[0].initial_element = element;
1665 stored_player[0].use_murphy = TRUE;
1667 if (!level.use_artwork_element[0])
1668 stored_player[0].artwork_element = EL_SP_MURPHY;
1671 Feld[x][y] = EL_PLAYER_1;
1677 struct PlayerInfo *player = &stored_player[Feld[x][y] - EL_PLAYER_1];
1678 int jx = player->jx, jy = player->jy;
1680 player->present = TRUE;
1682 player->block_last_field = (element == EL_SP_MURPHY ?
1683 level.sp_block_last_field :
1684 level.block_last_field);
1686 /* ---------- initialize player's last field block delay --------------- */
1688 /* always start with reliable default value (no adjustment needed) */
1689 player->block_delay_adjustment = 0;
1691 /* special case 1: in Supaplex, Murphy blocks last field one more frame */
1692 if (player->block_last_field && element == EL_SP_MURPHY)
1693 player->block_delay_adjustment = 1;
1695 /* special case 2: in game engines before 3.1.1, blocking was different */
1696 if (game.use_block_last_field_bug)
1697 player->block_delay_adjustment = (player->block_last_field ? -1 : 1);
1699 if (!options.network || player->connected)
1701 player->active = TRUE;
1703 /* remove potentially duplicate players */
1704 if (StorePlayer[jx][jy] == Feld[x][y])
1705 StorePlayer[jx][jy] = 0;
1707 StorePlayer[x][y] = Feld[x][y];
1709 #if DEBUG_INIT_PLAYER
1712 printf("- player element %d activated", player->element_nr);
1713 printf(" (local player is %d and currently %s)\n",
1714 local_player->element_nr,
1715 local_player->active ? "active" : "not active");
1720 Feld[x][y] = EL_EMPTY;
1722 player->jx = player->last_jx = x;
1723 player->jy = player->last_jy = y;
1728 int player_nr = GET_PLAYER_NR(element);
1729 struct PlayerInfo *player = &stored_player[player_nr];
1731 if (player->active && player->killed)
1732 player->reanimated = TRUE; /* if player was just killed, reanimate him */
1736 static void InitField(int x, int y, boolean init_game)
1738 int element = Feld[x][y];
1747 InitPlayerField(x, y, element, init_game);
1750 case EL_SOKOBAN_FIELD_PLAYER:
1751 element = Feld[x][y] = EL_PLAYER_1;
1752 InitField(x, y, init_game);
1754 element = Feld[x][y] = EL_SOKOBAN_FIELD_EMPTY;
1755 InitField(x, y, init_game);
1758 case EL_SOKOBAN_FIELD_EMPTY:
1759 local_player->sokobanfields_still_needed++;
1763 if (x < lev_fieldx-1 && Feld[x+1][y] == EL_ACID)
1764 Feld[x][y] = EL_ACID_POOL_TOPLEFT;
1765 else if (x > 0 && Feld[x-1][y] == EL_ACID)
1766 Feld[x][y] = EL_ACID_POOL_TOPRIGHT;
1767 else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPLEFT)
1768 Feld[x][y] = EL_ACID_POOL_BOTTOMLEFT;
1769 else if (y > 0 && Feld[x][y-1] == EL_ACID)
1770 Feld[x][y] = EL_ACID_POOL_BOTTOM;
1771 else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPRIGHT)
1772 Feld[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
1781 case EL_SPACESHIP_RIGHT:
1782 case EL_SPACESHIP_UP:
1783 case EL_SPACESHIP_LEFT:
1784 case EL_SPACESHIP_DOWN:
1785 case EL_BD_BUTTERFLY:
1786 case EL_BD_BUTTERFLY_RIGHT:
1787 case EL_BD_BUTTERFLY_UP:
1788 case EL_BD_BUTTERFLY_LEFT:
1789 case EL_BD_BUTTERFLY_DOWN:
1791 case EL_BD_FIREFLY_RIGHT:
1792 case EL_BD_FIREFLY_UP:
1793 case EL_BD_FIREFLY_LEFT:
1794 case EL_BD_FIREFLY_DOWN:
1795 case EL_PACMAN_RIGHT:
1797 case EL_PACMAN_LEFT:
1798 case EL_PACMAN_DOWN:
1800 case EL_YAMYAM_LEFT:
1801 case EL_YAMYAM_RIGHT:
1803 case EL_YAMYAM_DOWN:
1804 case EL_DARK_YAMYAM:
1807 case EL_SP_SNIKSNAK:
1808 case EL_SP_ELECTRON:
1817 case EL_AMOEBA_FULL:
1822 case EL_AMOEBA_DROP:
1823 if (y == lev_fieldy - 1)
1825 Feld[x][y] = EL_AMOEBA_GROWING;
1826 Store[x][y] = EL_AMOEBA_WET;
1830 case EL_DYNAMITE_ACTIVE:
1831 case EL_SP_DISK_RED_ACTIVE:
1832 case EL_DYNABOMB_PLAYER_1_ACTIVE:
1833 case EL_DYNABOMB_PLAYER_2_ACTIVE:
1834 case EL_DYNABOMB_PLAYER_3_ACTIVE:
1835 case EL_DYNABOMB_PLAYER_4_ACTIVE:
1836 MovDelay[x][y] = 96;
1839 case EL_EM_DYNAMITE_ACTIVE:
1840 MovDelay[x][y] = 32;
1844 local_player->lights_still_needed++;
1848 local_player->friends_still_needed++;
1853 GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
1856 case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
1857 case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
1858 case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
1859 case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
1860 case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
1861 case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
1862 case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
1863 case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
1864 case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
1865 case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
1866 case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
1867 case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
1870 int belt_nr = getBeltNrFromBeltSwitchElement(Feld[x][y]);
1871 int belt_dir = getBeltDirFromBeltSwitchElement(Feld[x][y]);
1872 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Feld[x][y]);
1874 if (game.belt_dir_nr[belt_nr] == 3) /* initial value */
1876 game.belt_dir[belt_nr] = belt_dir;
1877 game.belt_dir_nr[belt_nr] = belt_dir_nr;
1879 else /* more than one switch -- set it like the first switch */
1881 Feld[x][y] = Feld[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
1886 case EL_LIGHT_SWITCH_ACTIVE:
1888 game.light_time_left = level.time_light * FRAMES_PER_SECOND;
1891 case EL_INVISIBLE_STEELWALL:
1892 case EL_INVISIBLE_WALL:
1893 case EL_INVISIBLE_SAND:
1894 if (game.light_time_left > 0 ||
1895 game.lenses_time_left > 0)
1896 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
1899 case EL_EMC_MAGIC_BALL:
1900 if (game.ball_state)
1901 Feld[x][y] = EL_EMC_MAGIC_BALL_ACTIVE;
1904 case EL_EMC_MAGIC_BALL_SWITCH:
1905 if (game.ball_state)
1906 Feld[x][y] = EL_EMC_MAGIC_BALL_SWITCH_ACTIVE;
1909 case EL_TRIGGER_PLAYER:
1910 case EL_TRIGGER_ELEMENT:
1911 case EL_TRIGGER_CE_VALUE:
1912 case EL_TRIGGER_CE_SCORE:
1914 case EL_ANY_ELEMENT:
1915 case EL_CURRENT_CE_VALUE:
1916 case EL_CURRENT_CE_SCORE:
1933 /* reference elements should not be used on the playfield */
1934 Feld[x][y] = EL_EMPTY;
1938 if (IS_CUSTOM_ELEMENT(element))
1940 if (CAN_MOVE(element))
1943 if (!element_info[element].use_last_ce_value || init_game)
1944 CustomValue[x][y] = GET_NEW_CE_VALUE(Feld[x][y]);
1946 else if (IS_GROUP_ELEMENT(element))
1948 Feld[x][y] = GetElementFromGroupElement(element);
1950 InitField(x, y, init_game);
1957 CheckTriggeredElementChange(x, y, element, CE_CREATION_OF_X);
1960 inline static void InitField_WithBug1(int x, int y, boolean init_game)
1962 InitField(x, y, init_game);
1964 /* not needed to call InitMovDir() -- already done by InitField()! */
1965 if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1966 CAN_MOVE(Feld[x][y]))
1970 inline static void InitField_WithBug2(int x, int y, boolean init_game)
1972 int old_element = Feld[x][y];
1974 InitField(x, y, init_game);
1976 /* not needed to call InitMovDir() -- already done by InitField()! */
1977 if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1978 CAN_MOVE(old_element) &&
1979 (old_element < EL_MOLE_LEFT || old_element > EL_MOLE_DOWN))
1982 /* this case is in fact a combination of not less than three bugs:
1983 first, it calls InitMovDir() for elements that can move, although this is
1984 already done by InitField(); then, it checks the element that was at this
1985 field _before_ the call to InitField() (which can change it); lastly, it
1986 was not called for "mole with direction" elements, which were treated as
1987 "cannot move" due to (fixed) wrong element initialization in "src/init.c"
1991 static int get_key_element_from_nr(int key_nr)
1993 int key_base_element = (key_nr >= STD_NUM_KEYS ? EL_EMC_KEY_5 - STD_NUM_KEYS :
1994 level.game_engine_type == GAME_ENGINE_TYPE_EM ?
1995 EL_EM_KEY_1 : EL_KEY_1);
1997 return key_base_element + key_nr;
2000 static int get_next_dropped_element(struct PlayerInfo *player)
2002 return (player->inventory_size > 0 ?
2003 player->inventory_element[player->inventory_size - 1] :
2004 player->inventory_infinite_element != EL_UNDEFINED ?
2005 player->inventory_infinite_element :
2006 player->dynabombs_left > 0 ?
2007 EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
2011 static int get_inventory_element_from_pos(struct PlayerInfo *player, int pos)
2013 /* pos >= 0: get element from bottom of the stack;
2014 pos < 0: get element from top of the stack */
2018 int min_inventory_size = -pos;
2019 int inventory_pos = player->inventory_size - min_inventory_size;
2020 int min_dynabombs_left = min_inventory_size - player->inventory_size;
2022 return (player->inventory_size >= min_inventory_size ?
2023 player->inventory_element[inventory_pos] :
2024 player->inventory_infinite_element != EL_UNDEFINED ?
2025 player->inventory_infinite_element :
2026 player->dynabombs_left >= min_dynabombs_left ?
2027 EL_DYNABOMB_PLAYER_1 + player->index_nr :
2032 int min_dynabombs_left = pos + 1;
2033 int min_inventory_size = pos + 1 - player->dynabombs_left;
2034 int inventory_pos = pos - player->dynabombs_left;
2036 return (player->inventory_infinite_element != EL_UNDEFINED ?
2037 player->inventory_infinite_element :
2038 player->dynabombs_left >= min_dynabombs_left ?
2039 EL_DYNABOMB_PLAYER_1 + player->index_nr :
2040 player->inventory_size >= min_inventory_size ?
2041 player->inventory_element[inventory_pos] :
2046 static int compareGamePanelOrderInfo(const void *object1, const void *object2)
2048 const struct GamePanelOrderInfo *gpo1 = (struct GamePanelOrderInfo *)object1;
2049 const struct GamePanelOrderInfo *gpo2 = (struct GamePanelOrderInfo *)object2;
2052 if (gpo1->sort_priority != gpo2->sort_priority)
2053 compare_result = gpo1->sort_priority - gpo2->sort_priority;
2055 compare_result = gpo1->nr - gpo2->nr;
2057 return compare_result;
2060 int getPlayerInventorySize(int player_nr)
2062 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2063 return level.native_em_level->ply[player_nr]->dynamite;
2064 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
2065 return level.native_sp_level->game_sp->red_disk_count;
2067 return stored_player[player_nr].inventory_size;
2070 void InitGameControlValues()
2074 for (i = 0; game_panel_controls[i].nr != -1; i++)
2076 struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2077 struct GamePanelOrderInfo *gpo = &game_panel_order[i];
2078 struct TextPosInfo *pos = gpc->pos;
2080 int type = gpc->type;
2084 Error(ERR_INFO, "'game_panel_controls' structure corrupted at %d", i);
2085 Error(ERR_EXIT, "this should not happen -- please debug");
2088 /* force update of game controls after initialization */
2089 gpc->value = gpc->last_value = -1;
2090 gpc->frame = gpc->last_frame = -1;
2091 gpc->gfx_frame = -1;
2093 /* determine panel value width for later calculation of alignment */
2094 if (type == TYPE_INTEGER || type == TYPE_STRING)
2096 pos->width = pos->size * getFontWidth(pos->font);
2097 pos->height = getFontHeight(pos->font);
2099 else if (type == TYPE_ELEMENT)
2101 pos->width = pos->size;
2102 pos->height = pos->size;
2105 /* fill structure for game panel draw order */
2107 gpo->sort_priority = pos->sort_priority;
2110 /* sort game panel controls according to sort_priority and control number */
2111 qsort(game_panel_order, NUM_GAME_PANEL_CONTROLS,
2112 sizeof(struct GamePanelOrderInfo), compareGamePanelOrderInfo);
2115 void UpdatePlayfieldElementCount()
2117 boolean use_element_count = FALSE;
2120 /* first check if it is needed at all to calculate playfield element count */
2121 for (i = GAME_PANEL_ELEMENT_COUNT_1; i <= GAME_PANEL_ELEMENT_COUNT_8; i++)
2122 if (!PANEL_DEACTIVATED(game_panel_controls[i].pos))
2123 use_element_count = TRUE;
2125 if (!use_element_count)
2128 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2129 element_info[i].element_count = 0;
2131 SCAN_PLAYFIELD(x, y)
2133 element_info[Feld[x][y]].element_count++;
2136 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
2137 for (j = 0; j < MAX_NUM_ELEMENTS; j++)
2138 if (IS_IN_GROUP(j, i))
2139 element_info[EL_GROUP_START + i].element_count +=
2140 element_info[j].element_count;
2143 void UpdateGameControlValues()
2146 int time = (local_player->LevelSolved ?
2147 local_player->LevelSolved_CountingTime :
2148 level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2149 level.native_em_level->lev->time :
2150 level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2151 level.native_sp_level->game_sp->time_played :
2152 game.no_time_limit ? TimePlayed : TimeLeft);
2153 int score = (local_player->LevelSolved ?
2154 local_player->LevelSolved_CountingScore :
2155 level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2156 level.native_em_level->lev->score :
2157 level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2158 level.native_sp_level->game_sp->score :
2159 local_player->score);
2160 int gems = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2161 level.native_em_level->lev->required :
2162 level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2163 level.native_sp_level->game_sp->infotrons_still_needed :
2164 local_player->gems_still_needed);
2165 int exit_closed = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2166 level.native_em_level->lev->required > 0 :
2167 level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2168 level.native_sp_level->game_sp->infotrons_still_needed > 0 :
2169 local_player->gems_still_needed > 0 ||
2170 local_player->sokobanfields_still_needed > 0 ||
2171 local_player->lights_still_needed > 0);
2173 UpdatePlayfieldElementCount();
2175 /* update game panel control values */
2177 game_panel_controls[GAME_PANEL_LEVEL_NUMBER].value = level_nr;
2178 game_panel_controls[GAME_PANEL_GEMS].value = gems;
2180 game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value = 0;
2181 for (i = 0; i < MAX_NUM_KEYS; i++)
2182 game_panel_controls[GAME_PANEL_KEY_1 + i].value = EL_EMPTY;
2183 game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_EMPTY;
2184 game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value = 0;
2186 if (game.centered_player_nr == -1)
2188 for (i = 0; i < MAX_PLAYERS; i++)
2190 /* only one player in Supaplex game engine */
2191 if (level.game_engine_type == GAME_ENGINE_TYPE_SP && i > 0)
2194 for (k = 0; k < MAX_NUM_KEYS; k++)
2196 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2198 if (level.native_em_level->ply[i]->keys & (1 << k))
2199 game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2200 get_key_element_from_nr(k);
2202 else if (stored_player[i].key[k])
2203 game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2204 get_key_element_from_nr(k);
2207 game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2208 getPlayerInventorySize(i);
2210 if (stored_player[i].num_white_keys > 0)
2211 game_panel_controls[GAME_PANEL_KEY_WHITE].value =
2214 game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2215 stored_player[i].num_white_keys;
2220 int player_nr = game.centered_player_nr;
2222 for (k = 0; k < MAX_NUM_KEYS; k++)
2224 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2226 if (level.native_em_level->ply[player_nr]->keys & (1 << k))
2227 game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2228 get_key_element_from_nr(k);
2230 else if (stored_player[player_nr].key[k])
2231 game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2232 get_key_element_from_nr(k);
2235 game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2236 getPlayerInventorySize(player_nr);
2238 if (stored_player[player_nr].num_white_keys > 0)
2239 game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_DC_KEY_WHITE;
2241 game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2242 stored_player[player_nr].num_white_keys;
2245 for (i = 0; i < NUM_PANEL_INVENTORY; i++)
2247 game_panel_controls[GAME_PANEL_INVENTORY_FIRST_1 + i].value =
2248 get_inventory_element_from_pos(local_player, i);
2249 game_panel_controls[GAME_PANEL_INVENTORY_LAST_1 + i].value =
2250 get_inventory_element_from_pos(local_player, -i - 1);
2253 game_panel_controls[GAME_PANEL_SCORE].value = score;
2254 game_panel_controls[GAME_PANEL_HIGHSCORE].value = highscore[0].Score;
2256 game_panel_controls[GAME_PANEL_TIME].value = time;
2258 game_panel_controls[GAME_PANEL_TIME_HH].value = time / 3600;
2259 game_panel_controls[GAME_PANEL_TIME_MM].value = (time / 60) % 60;
2260 game_panel_controls[GAME_PANEL_TIME_SS].value = time % 60;
2262 game_panel_controls[GAME_PANEL_FRAME].value = FrameCounter;
2264 game_panel_controls[GAME_PANEL_SHIELD_NORMAL].value =
2265 (local_player->shield_normal_time_left > 0 ? EL_SHIELD_NORMAL_ACTIVE :
2267 game_panel_controls[GAME_PANEL_SHIELD_NORMAL_TIME].value =
2268 local_player->shield_normal_time_left;
2269 game_panel_controls[GAME_PANEL_SHIELD_DEADLY].value =
2270 (local_player->shield_deadly_time_left > 0 ? EL_SHIELD_DEADLY_ACTIVE :
2272 game_panel_controls[GAME_PANEL_SHIELD_DEADLY_TIME].value =
2273 local_player->shield_deadly_time_left;
2275 game_panel_controls[GAME_PANEL_EXIT].value =
2276 (exit_closed ? EL_EXIT_CLOSED : EL_EXIT_OPEN);
2278 game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL].value =
2279 (game.ball_state ? EL_EMC_MAGIC_BALL_ACTIVE : EL_EMC_MAGIC_BALL);
2280 game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL_SWITCH].value =
2281 (game.ball_state ? EL_EMC_MAGIC_BALL_SWITCH_ACTIVE :
2282 EL_EMC_MAGIC_BALL_SWITCH);
2284 game_panel_controls[GAME_PANEL_LIGHT_SWITCH].value =
2285 (game.light_time_left > 0 ? EL_LIGHT_SWITCH_ACTIVE : EL_LIGHT_SWITCH);
2286 game_panel_controls[GAME_PANEL_LIGHT_SWITCH_TIME].value =
2287 game.light_time_left;
2289 game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH].value =
2290 (game.timegate_time_left > 0 ? EL_TIMEGATE_OPEN : EL_TIMEGATE_CLOSED);
2291 game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH_TIME].value =
2292 game.timegate_time_left;
2294 game_panel_controls[GAME_PANEL_SWITCHGATE_SWITCH].value =
2295 EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
2297 game_panel_controls[GAME_PANEL_EMC_LENSES].value =
2298 (game.lenses_time_left > 0 ? EL_EMC_LENSES : EL_EMPTY);
2299 game_panel_controls[GAME_PANEL_EMC_LENSES_TIME].value =
2300 game.lenses_time_left;
2302 game_panel_controls[GAME_PANEL_EMC_MAGNIFIER].value =
2303 (game.magnify_time_left > 0 ? EL_EMC_MAGNIFIER : EL_EMPTY);
2304 game_panel_controls[GAME_PANEL_EMC_MAGNIFIER_TIME].value =
2305 game.magnify_time_left;
2307 game_panel_controls[GAME_PANEL_BALLOON_SWITCH].value =
2308 (game.wind_direction == MV_LEFT ? EL_BALLOON_SWITCH_LEFT :
2309 game.wind_direction == MV_RIGHT ? EL_BALLOON_SWITCH_RIGHT :
2310 game.wind_direction == MV_UP ? EL_BALLOON_SWITCH_UP :
2311 game.wind_direction == MV_DOWN ? EL_BALLOON_SWITCH_DOWN :
2312 EL_BALLOON_SWITCH_NONE);
2314 game_panel_controls[GAME_PANEL_DYNABOMB_NUMBER].value =
2315 local_player->dynabomb_count;
2316 game_panel_controls[GAME_PANEL_DYNABOMB_SIZE].value =
2317 local_player->dynabomb_size;
2318 game_panel_controls[GAME_PANEL_DYNABOMB_POWER].value =
2319 (local_player->dynabomb_xl ? EL_DYNABOMB_INCREASE_POWER : EL_EMPTY);
2321 game_panel_controls[GAME_PANEL_PENGUINS].value =
2322 local_player->friends_still_needed;
2324 game_panel_controls[GAME_PANEL_SOKOBAN_OBJECTS].value =
2325 local_player->sokobanfields_still_needed;
2326 game_panel_controls[GAME_PANEL_SOKOBAN_FIELDS].value =
2327 local_player->sokobanfields_still_needed;
2329 game_panel_controls[GAME_PANEL_ROBOT_WHEEL].value =
2330 (game.robot_wheel_active ? EL_ROBOT_WHEEL_ACTIVE : EL_ROBOT_WHEEL);
2332 for (i = 0; i < NUM_BELTS; i++)
2334 game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1 + i].value =
2335 (game.belt_dir[i] != MV_NONE ? EL_CONVEYOR_BELT_1_MIDDLE_ACTIVE :
2336 EL_CONVEYOR_BELT_1_MIDDLE) + i;
2337 game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1_SWITCH + i].value =
2338 getBeltSwitchElementFromBeltNrAndBeltDir(i, game.belt_dir[i]);
2341 game_panel_controls[GAME_PANEL_MAGIC_WALL].value =
2342 (game.magic_wall_active ? EL_MAGIC_WALL_ACTIVE : EL_MAGIC_WALL);
2343 game_panel_controls[GAME_PANEL_MAGIC_WALL_TIME].value =
2344 game.magic_wall_time_left;
2346 game_panel_controls[GAME_PANEL_GRAVITY_STATE].value =
2347 local_player->gravity;
2349 for (i = 0; i < NUM_PANEL_GRAPHICS; i++)
2350 game_panel_controls[GAME_PANEL_GRAPHIC_1 + i].value = EL_GRAPHIC_1 + i;
2352 for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2353 game_panel_controls[GAME_PANEL_ELEMENT_1 + i].value =
2354 (IS_DRAWABLE_ELEMENT(game.panel.element[i].id) ?
2355 game.panel.element[i].id : EL_UNDEFINED);
2357 for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2358 game_panel_controls[GAME_PANEL_ELEMENT_COUNT_1 + i].value =
2359 (IS_VALID_ELEMENT(game.panel.element_count[i].id) ?
2360 element_info[game.panel.element_count[i].id].element_count : 0);
2362 for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2363 game_panel_controls[GAME_PANEL_CE_SCORE_1 + i].value =
2364 (IS_CUSTOM_ELEMENT(game.panel.ce_score[i].id) ?
2365 element_info[game.panel.ce_score[i].id].collect_score : 0);
2367 for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2368 game_panel_controls[GAME_PANEL_CE_SCORE_1_ELEMENT + i].value =
2369 (IS_CUSTOM_ELEMENT(game.panel.ce_score_element[i].id) ?
2370 element_info[game.panel.ce_score_element[i].id].collect_score :
2373 game_panel_controls[GAME_PANEL_PLAYER_NAME].value = 0;
2374 game_panel_controls[GAME_PANEL_LEVEL_NAME].value = 0;
2375 game_panel_controls[GAME_PANEL_LEVEL_AUTHOR].value = 0;
2377 /* update game panel control frames */
2379 for (i = 0; game_panel_controls[i].nr != -1; i++)
2381 struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2383 if (gpc->type == TYPE_ELEMENT)
2385 if (gpc->value != EL_UNDEFINED && gpc->value != EL_EMPTY)
2387 int last_anim_random_frame = gfx.anim_random_frame;
2388 int element = gpc->value;
2389 int graphic = el2panelimg(element);
2391 if (gpc->value != gpc->last_value)
2394 gpc->gfx_random = INIT_GFX_RANDOM();
2400 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2401 IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2402 gpc->gfx_random = INIT_GFX_RANDOM();
2405 if (ANIM_MODE(graphic) == ANIM_RANDOM)
2406 gfx.anim_random_frame = gpc->gfx_random;
2408 if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
2409 gpc->gfx_frame = element_info[element].collect_score;
2411 gpc->frame = getGraphicAnimationFrame(el2panelimg(gpc->value),
2414 if (ANIM_MODE(graphic) == ANIM_RANDOM)
2415 gfx.anim_random_frame = last_anim_random_frame;
2421 void DisplayGameControlValues()
2423 boolean redraw_panel = FALSE;
2426 for (i = 0; game_panel_controls[i].nr != -1; i++)
2428 struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2430 if (PANEL_DEACTIVATED(gpc->pos))
2433 if (gpc->value == gpc->last_value &&
2434 gpc->frame == gpc->last_frame)
2437 redraw_panel = TRUE;
2443 /* copy default game door content to main double buffer */
2445 /* !!! CHECK AGAIN !!! */
2446 SetPanelBackground();
2447 // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
2448 DrawBackground(DX, DY, DXSIZE, DYSIZE);
2450 /* redraw game control buttons */
2451 RedrawGameButtons();
2453 SetGameStatus(GAME_MODE_PSEUDO_PANEL);
2455 for (i = 0; i < NUM_GAME_PANEL_CONTROLS; i++)
2457 int nr = game_panel_order[i].nr;
2458 struct GamePanelControlInfo *gpc = &game_panel_controls[nr];
2459 struct TextPosInfo *pos = gpc->pos;
2460 int type = gpc->type;
2461 int value = gpc->value;
2462 int frame = gpc->frame;
2463 int size = pos->size;
2464 int font = pos->font;
2465 boolean draw_masked = pos->draw_masked;
2466 int mask_mode = (draw_masked ? BLIT_MASKED : BLIT_OPAQUE);
2468 if (PANEL_DEACTIVATED(pos))
2471 gpc->last_value = value;
2472 gpc->last_frame = frame;
2474 if (type == TYPE_INTEGER)
2476 if (nr == GAME_PANEL_LEVEL_NUMBER ||
2477 nr == GAME_PANEL_TIME)
2479 boolean use_dynamic_size = (size == -1 ? TRUE : FALSE);
2481 if (use_dynamic_size) /* use dynamic number of digits */
2483 int value_change = (nr == GAME_PANEL_LEVEL_NUMBER ? 100 : 1000);
2484 int size1 = (nr == GAME_PANEL_LEVEL_NUMBER ? 2 : 3);
2485 int size2 = size1 + 1;
2486 int font1 = pos->font;
2487 int font2 = pos->font_alt;
2489 size = (value < value_change ? size1 : size2);
2490 font = (value < value_change ? font1 : font2);
2494 /* correct text size if "digits" is zero or less */
2496 size = strlen(int2str(value, size));
2498 /* dynamically correct text alignment */
2499 pos->width = size * getFontWidth(font);
2501 DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2502 int2str(value, size), font, mask_mode);
2504 else if (type == TYPE_ELEMENT)
2506 int element, graphic;
2510 int dst_x = PANEL_XPOS(pos);
2511 int dst_y = PANEL_YPOS(pos);
2513 if (value != EL_UNDEFINED && value != EL_EMPTY)
2516 graphic = el2panelimg(value);
2518 // printf("::: %d, '%s' [%d]\n", element, EL_NAME(element), size);
2520 if (element >= EL_GRAPHIC_1 && element <= EL_GRAPHIC_8 && size == 0)
2523 getSizedGraphicSource(graphic, frame, size, &src_bitmap,
2526 width = graphic_info[graphic].width * size / TILESIZE;
2527 height = graphic_info[graphic].height * size / TILESIZE;
2530 BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2533 BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2537 else if (type == TYPE_STRING)
2539 boolean active = (value != 0);
2540 char *state_normal = "off";
2541 char *state_active = "on";
2542 char *state = (active ? state_active : state_normal);
2543 char *s = (nr == GAME_PANEL_GRAVITY_STATE ? state :
2544 nr == GAME_PANEL_PLAYER_NAME ? setup.player_name :
2545 nr == GAME_PANEL_LEVEL_NAME ? level.name :
2546 nr == GAME_PANEL_LEVEL_AUTHOR ? level.author : NULL);
2548 if (nr == GAME_PANEL_GRAVITY_STATE)
2550 int font1 = pos->font; /* (used for normal state) */
2551 int font2 = pos->font_alt; /* (used for active state) */
2553 font = (active ? font2 : font1);
2562 /* don't truncate output if "chars" is zero or less */
2565 /* dynamically correct text alignment */
2566 pos->width = size * getFontWidth(font);
2569 s_cut = getStringCopyN(s, size);
2571 DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2572 s_cut, font, mask_mode);
2578 redraw_mask |= REDRAW_DOOR_1;
2581 SetGameStatus(GAME_MODE_PLAYING);
2584 void UpdateAndDisplayGameControlValues()
2586 if (tape.deactivate_display)
2589 UpdateGameControlValues();
2590 DisplayGameControlValues();
2593 void UpdateGameDoorValues()
2595 UpdateGameControlValues();
2598 void DrawGameDoorValues()
2600 DisplayGameControlValues();
2605 =============================================================================
2607 -----------------------------------------------------------------------------
2608 initialize game engine due to level / tape version number
2609 =============================================================================
2612 static void InitGameEngine()
2614 int i, j, k, l, x, y;
2616 /* set game engine from tape file when re-playing, else from level file */
2617 game.engine_version = (tape.playing ? tape.engine_version :
2618 level.game_version);
2620 /* set single or multi-player game mode (needed for re-playing tapes) */
2621 game.team_mode = setup.team_mode;
2625 int num_players = 0;
2627 for (i = 0; i < MAX_PLAYERS; i++)
2628 if (tape.player_participates[i])
2631 /* multi-player tapes contain input data for more than one player */
2632 game.team_mode = (num_players > 1);
2635 /* ---------------------------------------------------------------------- */
2636 /* set flags for bugs and changes according to active game engine version */
2637 /* ---------------------------------------------------------------------- */
2640 Summary of bugfix/change:
2641 Fixed handling for custom elements that change when pushed by the player.
2643 Fixed/changed in version:
2647 Before 3.1.0, custom elements that "change when pushing" changed directly
2648 after the player started pushing them (until then handled in "DigField()").
2649 Since 3.1.0, these custom elements are not changed until the "pushing"
2650 move of the element is finished (now handled in "ContinueMoving()").
2652 Affected levels/tapes:
2653 The first condition is generally needed for all levels/tapes before version
2654 3.1.0, which might use the old behaviour before it was changed; known tapes
2655 that are affected are some tapes from the level set "Walpurgis Gardens" by
2657 The second condition is an exception from the above case and is needed for
2658 the special case of tapes recorded with game (not engine!) version 3.1.0 or
2659 above (including some development versions of 3.1.0), but before it was
2660 known that this change would break tapes like the above and was fixed in
2661 3.1.1, so that the changed behaviour was active although the engine version
2662 while recording maybe was before 3.1.0. There is at least one tape that is
2663 affected by this exception, which is the tape for the one-level set "Bug
2664 Machine" by Juergen Bonhagen.
2667 game.use_change_when_pushing_bug =
2668 (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2670 tape.game_version >= VERSION_IDENT(3,1,0,0) &&
2671 tape.game_version < VERSION_IDENT(3,1,1,0)));
2674 Summary of bugfix/change:
2675 Fixed handling for blocking the field the player leaves when moving.
2677 Fixed/changed in version:
2681 Before 3.1.1, when "block last field when moving" was enabled, the field
2682 the player is leaving when moving was blocked for the time of the move,
2683 and was directly unblocked afterwards. This resulted in the last field
2684 being blocked for exactly one less than the number of frames of one player
2685 move. Additionally, even when blocking was disabled, the last field was
2686 blocked for exactly one frame.
2687 Since 3.1.1, due to changes in player movement handling, the last field
2688 is not blocked at all when blocking is disabled. When blocking is enabled,
2689 the last field is blocked for exactly the number of frames of one player
2690 move. Additionally, if the player is Murphy, the hero of Supaplex, the
2691 last field is blocked for exactly one more than the number of frames of
2694 Affected levels/tapes:
2695 (!!! yet to be determined -- probably many !!!)
2698 game.use_block_last_field_bug =
2699 (game.engine_version < VERSION_IDENT(3,1,1,0));
2701 game_em.use_single_button = game_em.use_single_button_initial =
2702 (game.engine_version > VERSION_IDENT(4,0,0,2));
2704 /* ---------------------------------------------------------------------- */
2706 /* set maximal allowed number of custom element changes per game frame */
2707 game.max_num_changes_per_frame = 1;
2709 /* default scan direction: scan playfield from top/left to bottom/right */
2710 InitPlayfieldScanMode(CA_ARG_SCAN_MODE_NORMAL);
2712 /* dynamically adjust element properties according to game engine version */
2713 InitElementPropertiesEngine(game.engine_version);
2716 printf("level %d: level version == %06d\n", level_nr, level.game_version);
2717 printf(" tape version == %06d [%s] [file: %06d]\n",
2718 tape.engine_version, (tape.playing ? "PLAYING" : "RECORDING"),
2720 printf(" => game.engine_version == %06d\n", game.engine_version);
2723 /* ---------- initialize player's initial move delay --------------------- */
2725 /* dynamically adjust player properties according to level information */
2726 for (i = 0; i < MAX_PLAYERS; i++)
2727 game.initial_move_delay_value[i] =
2728 get_move_delay_from_stepsize(level.initial_player_stepsize[i]);
2730 /* dynamically adjust player properties according to game engine version */
2731 for (i = 0; i < MAX_PLAYERS; i++)
2732 game.initial_move_delay[i] =
2733 (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
2734 game.initial_move_delay_value[i] : 0);
2736 /* ---------- initialize player's initial push delay --------------------- */
2738 /* dynamically adjust player properties according to game engine version */
2739 game.initial_push_delay_value =
2740 (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
2742 /* ---------- initialize changing elements ------------------------------- */
2744 /* initialize changing elements information */
2745 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2747 struct ElementInfo *ei = &element_info[i];
2749 /* this pointer might have been changed in the level editor */
2750 ei->change = &ei->change_page[0];
2752 if (!IS_CUSTOM_ELEMENT(i))
2754 ei->change->target_element = EL_EMPTY_SPACE;
2755 ei->change->delay_fixed = 0;
2756 ei->change->delay_random = 0;
2757 ei->change->delay_frames = 1;
2760 for (j = 0; j < NUM_CHANGE_EVENTS; j++)
2762 ei->has_change_event[j] = FALSE;
2764 ei->event_page_nr[j] = 0;
2765 ei->event_page[j] = &ei->change_page[0];
2769 /* add changing elements from pre-defined list */
2770 for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
2772 struct ChangingElementInfo *ch_delay = &change_delay_list[i];
2773 struct ElementInfo *ei = &element_info[ch_delay->element];
2775 ei->change->target_element = ch_delay->target_element;
2776 ei->change->delay_fixed = ch_delay->change_delay;
2778 ei->change->pre_change_function = ch_delay->pre_change_function;
2779 ei->change->change_function = ch_delay->change_function;
2780 ei->change->post_change_function = ch_delay->post_change_function;
2782 ei->change->can_change = TRUE;
2783 ei->change->can_change_or_has_action = TRUE;
2785 ei->has_change_event[CE_DELAY] = TRUE;
2787 SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
2788 SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE_OR_HAS_ACTION, TRUE);
2791 /* ---------- initialize internal run-time variables --------------------- */
2793 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2795 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
2797 for (j = 0; j < ei->num_change_pages; j++)
2799 ei->change_page[j].can_change_or_has_action =
2800 (ei->change_page[j].can_change |
2801 ei->change_page[j].has_action);
2805 /* add change events from custom element configuration */
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 if (!ei->change_page[j].can_change_or_has_action)
2815 for (k = 0; k < NUM_CHANGE_EVENTS; k++)
2817 /* only add event page for the first page found with this event */
2818 if (ei->change_page[j].has_event[k] && !(ei->has_change_event[k]))
2820 ei->has_change_event[k] = TRUE;
2822 ei->event_page_nr[k] = j;
2823 ei->event_page[k] = &ei->change_page[j];
2829 /* ---------- initialize reference elements in change conditions --------- */
2831 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2833 int element = EL_CUSTOM_START + i;
2834 struct ElementInfo *ei = &element_info[element];
2836 for (j = 0; j < ei->num_change_pages; j++)
2838 int trigger_element = ei->change_page[j].initial_trigger_element;
2840 if (trigger_element >= EL_PREV_CE_8 &&
2841 trigger_element <= EL_NEXT_CE_8)
2842 trigger_element = RESOLVED_REFERENCE_ELEMENT(element, trigger_element);
2844 ei->change_page[j].trigger_element = trigger_element;
2848 /* ---------- initialize run-time trigger player and element ------------- */
2850 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2852 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
2854 for (j = 0; j < ei->num_change_pages; j++)
2856 ei->change_page[j].actual_trigger_element = EL_EMPTY;
2857 ei->change_page[j].actual_trigger_player = EL_EMPTY;
2858 ei->change_page[j].actual_trigger_player_bits = CH_PLAYER_NONE;
2859 ei->change_page[j].actual_trigger_side = CH_SIDE_NONE;
2860 ei->change_page[j].actual_trigger_ce_value = 0;
2861 ei->change_page[j].actual_trigger_ce_score = 0;
2865 /* ---------- initialize trigger events ---------------------------------- */
2867 /* initialize trigger events information */
2868 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2869 for (j = 0; j < NUM_CHANGE_EVENTS; j++)
2870 trigger_events[i][j] = FALSE;
2872 /* add trigger events from element change event properties */
2873 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2875 struct ElementInfo *ei = &element_info[i];
2877 for (j = 0; j < ei->num_change_pages; j++)
2879 if (!ei->change_page[j].can_change_or_has_action)
2882 if (ei->change_page[j].has_event[CE_BY_OTHER_ACTION])
2884 int trigger_element = ei->change_page[j].trigger_element;
2886 for (k = 0; k < NUM_CHANGE_EVENTS; k++)
2888 if (ei->change_page[j].has_event[k])
2890 if (IS_GROUP_ELEMENT(trigger_element))
2892 struct ElementGroupInfo *group =
2893 element_info[trigger_element].group;
2895 for (l = 0; l < group->num_elements_resolved; l++)
2896 trigger_events[group->element_resolved[l]][k] = TRUE;
2898 else if (trigger_element == EL_ANY_ELEMENT)
2899 for (l = 0; l < MAX_NUM_ELEMENTS; l++)
2900 trigger_events[l][k] = TRUE;
2902 trigger_events[trigger_element][k] = TRUE;
2909 /* ---------- initialize push delay -------------------------------------- */
2911 /* initialize push delay values to default */
2912 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2914 if (!IS_CUSTOM_ELEMENT(i))
2916 /* set default push delay values (corrected since version 3.0.7-1) */
2917 if (game.engine_version < VERSION_IDENT(3,0,7,1))
2919 element_info[i].push_delay_fixed = 2;
2920 element_info[i].push_delay_random = 8;
2924 element_info[i].push_delay_fixed = 8;
2925 element_info[i].push_delay_random = 8;
2930 /* set push delay value for certain elements from pre-defined list */
2931 for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
2933 int e = push_delay_list[i].element;
2935 element_info[e].push_delay_fixed = push_delay_list[i].push_delay_fixed;
2936 element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
2939 /* set push delay value for Supaplex elements for newer engine versions */
2940 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
2942 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2944 if (IS_SP_ELEMENT(i))
2946 /* set SP push delay to just enough to push under a falling zonk */
2947 int delay = (game.engine_version >= VERSION_IDENT(3,1,1,0) ? 8 : 6);
2949 element_info[i].push_delay_fixed = delay;
2950 element_info[i].push_delay_random = 0;
2955 /* ---------- initialize move stepsize ----------------------------------- */
2957 /* initialize move stepsize values to default */
2958 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2959 if (!IS_CUSTOM_ELEMENT(i))
2960 element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
2962 /* set move stepsize value for certain elements from pre-defined list */
2963 for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
2965 int e = move_stepsize_list[i].element;
2967 element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
2970 /* ---------- initialize collect score ----------------------------------- */
2972 /* initialize collect score values for custom elements from initial value */
2973 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2974 if (IS_CUSTOM_ELEMENT(i))
2975 element_info[i].collect_score = element_info[i].collect_score_initial;
2977 /* ---------- initialize collect count ----------------------------------- */
2979 /* initialize collect count values for non-custom elements */
2980 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2981 if (!IS_CUSTOM_ELEMENT(i))
2982 element_info[i].collect_count_initial = 0;
2984 /* add collect count values for all elements from pre-defined list */
2985 for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
2986 element_info[collect_count_list[i].element].collect_count_initial =
2987 collect_count_list[i].count;
2989 /* ---------- initialize access direction -------------------------------- */
2991 /* initialize access direction values to default (access from every side) */
2992 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2993 if (!IS_CUSTOM_ELEMENT(i))
2994 element_info[i].access_direction = MV_ALL_DIRECTIONS;
2996 /* set access direction value for certain elements from pre-defined list */
2997 for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
2998 element_info[access_direction_list[i].element].access_direction =
2999 access_direction_list[i].direction;
3001 /* ---------- initialize explosion content ------------------------------- */
3002 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3004 if (IS_CUSTOM_ELEMENT(i))
3007 for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
3009 /* (content for EL_YAMYAM set at run-time with game.yamyam_content_nr) */
3011 element_info[i].content.e[x][y] =
3012 (i == EL_PLAYER_1 ? EL_EMERALD_YELLOW :
3013 i == EL_PLAYER_2 ? EL_EMERALD_RED :
3014 i == EL_PLAYER_3 ? EL_EMERALD :
3015 i == EL_PLAYER_4 ? EL_EMERALD_PURPLE :
3016 i == EL_MOLE ? EL_EMERALD_RED :
3017 i == EL_PENGUIN ? EL_EMERALD_PURPLE :
3018 i == EL_BUG ? (x == 1 && y == 1 ? EL_DIAMOND : EL_EMERALD) :
3019 i == EL_BD_BUTTERFLY ? EL_BD_DIAMOND :
3020 i == EL_SP_ELECTRON ? EL_SP_INFOTRON :
3021 i == EL_AMOEBA_TO_DIAMOND ? level.amoeba_content :
3022 i == EL_WALL_EMERALD ? EL_EMERALD :
3023 i == EL_WALL_DIAMOND ? EL_DIAMOND :
3024 i == EL_WALL_BD_DIAMOND ? EL_BD_DIAMOND :
3025 i == EL_WALL_EMERALD_YELLOW ? EL_EMERALD_YELLOW :
3026 i == EL_WALL_EMERALD_RED ? EL_EMERALD_RED :
3027 i == EL_WALL_EMERALD_PURPLE ? EL_EMERALD_PURPLE :
3028 i == EL_WALL_PEARL ? EL_PEARL :
3029 i == EL_WALL_CRYSTAL ? EL_CRYSTAL :
3034 /* ---------- initialize recursion detection ------------------------------ */
3035 recursion_loop_depth = 0;
3036 recursion_loop_detected = FALSE;
3037 recursion_loop_element = EL_UNDEFINED;
3039 /* ---------- initialize graphics engine ---------------------------------- */
3040 game.scroll_delay_value =
3041 (game.forced_scroll_delay_value != -1 ? game.forced_scroll_delay_value :
3042 setup.scroll_delay ? setup.scroll_delay_value : 0);
3043 game.scroll_delay_value =
3044 MIN(MAX(MIN_SCROLL_DELAY, game.scroll_delay_value), MAX_SCROLL_DELAY);
3046 /* ---------- initialize game engine snapshots ---------------------------- */
3047 for (i = 0; i < MAX_PLAYERS; i++)
3048 game.snapshot.last_action[i] = 0;
3049 game.snapshot.changed_action = FALSE;
3050 game.snapshot.collected_item = FALSE;
3051 game.snapshot.mode =
3052 (strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_STEP) ?
3053 SNAPSHOT_MODE_EVERY_STEP :
3054 strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_MOVE) ?
3055 SNAPSHOT_MODE_EVERY_MOVE :
3056 strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_COLLECT) ?
3057 SNAPSHOT_MODE_EVERY_COLLECT : SNAPSHOT_MODE_OFF);
3058 game.snapshot.save_snapshot = FALSE;
3061 int get_num_special_action(int element, int action_first, int action_last)
3063 int num_special_action = 0;
3066 for (i = action_first; i <= action_last; i++)
3068 boolean found = FALSE;
3070 for (j = 0; j < NUM_DIRECTIONS; j++)
3071 if (el_act_dir2img(element, i, j) !=
3072 el_act_dir2img(element, ACTION_DEFAULT, j))
3076 num_special_action++;
3081 return num_special_action;
3086 =============================================================================
3088 -----------------------------------------------------------------------------
3089 initialize and start new game
3090 =============================================================================
3095 int full_lev_fieldx = lev_fieldx + (BorderElement != EL_EMPTY ? 2 : 0);
3096 int full_lev_fieldy = lev_fieldy + (BorderElement != EL_EMPTY ? 2 : 0);
3097 int fade_mask = REDRAW_FIELD;
3099 boolean emulate_bd = TRUE; /* unless non-BOULDERDASH elements found */
3100 boolean emulate_sb = TRUE; /* unless non-SOKOBAN elements found */
3101 boolean emulate_sp = TRUE; /* unless non-SUPAPLEX elements found */
3102 int initial_move_dir = MV_DOWN;
3105 // required here to update video display before fading (FIX THIS)
3106 DrawMaskedBorder(REDRAW_DOOR_2);
3108 if (!game.restart_level)
3109 CloseDoor(DOOR_CLOSE_1);
3111 SetGameStatus(GAME_MODE_PLAYING);
3113 if (level_editor_test_game)
3114 FadeSkipNextFadeIn();
3116 FadeSetEnterScreen();
3118 if (CheckIfGlobalBorderHasChanged())
3119 fade_mask = REDRAW_ALL;
3121 FadeSoundsAndMusic();
3123 ExpireSoundLoops(TRUE);
3127 /* needed if different viewport properties defined for playing */
3128 ChangeViewportPropertiesIfNeeded();
3132 OpenDoor(GetDoorState() | DOOR_NO_DELAY | DOOR_FORCE_REDRAW);
3134 DrawCompleteVideoDisplay();
3137 InitGameControlValues();
3139 /* don't play tapes over network */
3140 network_playing = (options.network && !tape.playing);
3142 for (i = 0; i < MAX_PLAYERS; i++)
3144 struct PlayerInfo *player = &stored_player[i];
3146 player->index_nr = i;
3147 player->index_bit = (1 << i);
3148 player->element_nr = EL_PLAYER_1 + i;
3150 player->present = FALSE;
3151 player->active = FALSE;
3152 player->mapped = FALSE;
3154 player->killed = FALSE;
3155 player->reanimated = FALSE;
3158 player->effective_action = 0;
3159 player->programmed_action = 0;
3162 player->score_final = 0;
3164 player->gems_still_needed = level.gems_needed;
3165 player->sokobanfields_still_needed = 0;
3166 player->lights_still_needed = 0;
3167 player->friends_still_needed = 0;
3169 for (j = 0; j < MAX_NUM_KEYS; j++)
3170 player->key[j] = FALSE;
3172 player->num_white_keys = 0;
3174 player->dynabomb_count = 0;
3175 player->dynabomb_size = 1;
3176 player->dynabombs_left = 0;
3177 player->dynabomb_xl = FALSE;
3179 player->MovDir = initial_move_dir;
3182 player->GfxDir = initial_move_dir;
3183 player->GfxAction = ACTION_DEFAULT;
3185 player->StepFrame = 0;
3187 player->initial_element = player->element_nr;
3188 player->artwork_element =
3189 (level.use_artwork_element[i] ? level.artwork_element[i] :
3190 player->element_nr);
3191 player->use_murphy = FALSE;
3193 player->block_last_field = FALSE; /* initialized in InitPlayerField() */
3194 player->block_delay_adjustment = 0; /* initialized in InitPlayerField() */
3196 player->gravity = level.initial_player_gravity[i];
3198 player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
3200 player->actual_frame_counter = 0;
3202 player->step_counter = 0;
3204 player->last_move_dir = initial_move_dir;
3206 player->is_active = FALSE;
3208 player->is_waiting = FALSE;
3209 player->is_moving = FALSE;
3210 player->is_auto_moving = FALSE;
3211 player->is_digging = FALSE;
3212 player->is_snapping = FALSE;
3213 player->is_collecting = FALSE;
3214 player->is_pushing = FALSE;
3215 player->is_switching = FALSE;
3216 player->is_dropping = FALSE;
3217 player->is_dropping_pressed = FALSE;
3219 player->is_bored = FALSE;
3220 player->is_sleeping = FALSE;
3222 player->was_waiting = TRUE;
3223 player->was_moving = FALSE;
3224 player->was_snapping = FALSE;
3225 player->was_dropping = FALSE;
3227 player->frame_counter_bored = -1;
3228 player->frame_counter_sleeping = -1;
3230 player->anim_delay_counter = 0;
3231 player->post_delay_counter = 0;
3233 player->dir_waiting = initial_move_dir;
3234 player->action_waiting = ACTION_DEFAULT;
3235 player->last_action_waiting = ACTION_DEFAULT;
3236 player->special_action_bored = ACTION_DEFAULT;
3237 player->special_action_sleeping = ACTION_DEFAULT;
3239 player->switch_x = -1;
3240 player->switch_y = -1;
3242 player->drop_x = -1;
3243 player->drop_y = -1;
3245 player->show_envelope = 0;
3247 SetPlayerMoveSpeed(player, level.initial_player_stepsize[i], TRUE);
3249 player->push_delay = -1; /* initialized when pushing starts */
3250 player->push_delay_value = game.initial_push_delay_value;
3252 player->drop_delay = 0;
3253 player->drop_pressed_delay = 0;
3255 player->last_jx = -1;
3256 player->last_jy = -1;
3260 player->shield_normal_time_left = 0;
3261 player->shield_deadly_time_left = 0;
3263 player->inventory_infinite_element = EL_UNDEFINED;
3264 player->inventory_size = 0;
3266 if (level.use_initial_inventory[i])
3268 for (j = 0; j < level.initial_inventory_size[i]; j++)
3270 int element = level.initial_inventory_content[i][j];
3271 int collect_count = element_info[element].collect_count_initial;
3274 if (!IS_CUSTOM_ELEMENT(element))
3277 if (collect_count == 0)
3278 player->inventory_infinite_element = element;
3280 for (k = 0; k < collect_count; k++)
3281 if (player->inventory_size < MAX_INVENTORY_SIZE)
3282 player->inventory_element[player->inventory_size++] = element;
3286 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
3287 SnapField(player, 0, 0);
3289 player->LevelSolved = FALSE;
3290 player->GameOver = FALSE;
3292 player->LevelSolved_GameWon = FALSE;
3293 player->LevelSolved_GameEnd = FALSE;
3294 player->LevelSolved_PanelOff = FALSE;
3295 player->LevelSolved_SaveTape = FALSE;
3296 player->LevelSolved_SaveScore = FALSE;
3297 player->LevelSolved_CountingTime = 0;
3298 player->LevelSolved_CountingScore = 0;
3300 map_player_action[i] = i;
3303 network_player_action_received = FALSE;
3305 #if defined(NETWORK_AVALIABLE)
3306 /* initial null action */
3307 if (network_playing)
3308 SendToServer_MovePlayer(MV_NONE);
3317 TimeLeft = level.time;
3320 ScreenMovDir = MV_NONE;
3324 ScrollStepSize = 0; /* will be correctly initialized by ScrollScreen() */
3326 AllPlayersGone = FALSE;
3328 game.no_time_limit = (level.time == 0);
3330 game.yamyam_content_nr = 0;
3331 game.robot_wheel_active = FALSE;
3332 game.magic_wall_active = FALSE;
3333 game.magic_wall_time_left = 0;
3334 game.light_time_left = 0;
3335 game.timegate_time_left = 0;
3336 game.switchgate_pos = 0;
3337 game.wind_direction = level.wind_direction_initial;
3339 game.lenses_time_left = 0;
3340 game.magnify_time_left = 0;
3342 game.ball_state = level.ball_state_initial;
3343 game.ball_content_nr = 0;
3345 game.envelope_active = FALSE;
3347 /* set focus to local player for network games, else to all players */
3348 game.centered_player_nr = (network_playing ? local_player->index_nr : -1);
3349 game.centered_player_nr_next = game.centered_player_nr;
3350 game.set_centered_player = FALSE;
3352 if (network_playing && tape.recording)
3354 /* store client dependent player focus when recording network games */
3355 tape.centered_player_nr_next = game.centered_player_nr_next;
3356 tape.set_centered_player = TRUE;
3359 for (i = 0; i < NUM_BELTS; i++)
3361 game.belt_dir[i] = MV_NONE;
3362 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
3365 for (i = 0; i < MAX_NUM_AMOEBA; i++)
3366 AmoebaCnt[i] = AmoebaCnt2[i] = 0;
3368 #if DEBUG_INIT_PLAYER
3371 printf("Player status at level initialization:\n");
3375 SCAN_PLAYFIELD(x, y)
3377 Feld[x][y] = level.field[x][y];
3378 MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
3379 ChangeDelay[x][y] = 0;
3380 ChangePage[x][y] = -1;
3381 CustomValue[x][y] = 0; /* initialized in InitField() */
3382 Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
3384 WasJustMoving[x][y] = 0;
3385 WasJustFalling[x][y] = 0;
3386 CheckCollision[x][y] = 0;
3387 CheckImpact[x][y] = 0;
3389 Pushed[x][y] = FALSE;
3391 ChangeCount[x][y] = 0;
3392 ChangeEvent[x][y] = -1;
3394 ExplodePhase[x][y] = 0;
3395 ExplodeDelay[x][y] = 0;
3396 ExplodeField[x][y] = EX_TYPE_NONE;
3398 RunnerVisit[x][y] = 0;
3399 PlayerVisit[x][y] = 0;
3402 GfxRandom[x][y] = INIT_GFX_RANDOM();
3403 GfxElement[x][y] = EL_UNDEFINED;
3404 GfxAction[x][y] = ACTION_DEFAULT;
3405 GfxDir[x][y] = MV_NONE;
3406 GfxRedraw[x][y] = GFX_REDRAW_NONE;
3409 SCAN_PLAYFIELD(x, y)
3411 if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
3413 if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
3415 if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
3418 InitField(x, y, TRUE);
3420 ResetGfxAnimation(x, y);
3425 for (i = 0; i < MAX_PLAYERS; i++)
3427 struct PlayerInfo *player = &stored_player[i];
3429 /* set number of special actions for bored and sleeping animation */
3430 player->num_special_action_bored =
3431 get_num_special_action(player->artwork_element,
3432 ACTION_BORING_1, ACTION_BORING_LAST);
3433 player->num_special_action_sleeping =
3434 get_num_special_action(player->artwork_element,
3435 ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
3438 game.emulation = (emulate_bd ? EMU_BOULDERDASH :
3439 emulate_sb ? EMU_SOKOBAN :
3440 emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
3442 /* initialize type of slippery elements */
3443 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3445 if (!IS_CUSTOM_ELEMENT(i))
3447 /* default: elements slip down either to the left or right randomly */
3448 element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
3450 /* SP style elements prefer to slip down on the left side */
3451 if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
3452 element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3454 /* BD style elements prefer to slip down on the left side */
3455 if (game.emulation == EMU_BOULDERDASH)
3456 element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3460 /* initialize explosion and ignition delay */
3461 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3463 if (!IS_CUSTOM_ELEMENT(i))
3466 int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
3467 game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
3468 game.emulation == EMU_SUPAPLEX ? 3 : 2);
3469 int last_phase = (num_phase + 1) * delay;
3470 int half_phase = (num_phase / 2) * delay;
3472 element_info[i].explosion_delay = last_phase - 1;
3473 element_info[i].ignition_delay = half_phase;
3475 if (i == EL_BLACK_ORB)
3476 element_info[i].ignition_delay = 1;
3480 /* correct non-moving belts to start moving left */
3481 for (i = 0; i < NUM_BELTS; i++)
3482 if (game.belt_dir[i] == MV_NONE)
3483 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
3485 #if USE_NEW_PLAYER_ASSIGNMENTS
3486 /* !!! SAME AS init.c:InitPlayerInfo() -- FIX THIS !!! */
3487 /* choose default local player */
3488 local_player = &stored_player[0];
3490 for (i = 0; i < MAX_PLAYERS; i++)
3491 stored_player[i].connected = FALSE;
3493 local_player->connected = TRUE;
3494 /* !!! SAME AS init.c:InitPlayerInfo() -- FIX THIS !!! */
3498 for (i = 0; i < MAX_PLAYERS; i++)
3499 stored_player[i].connected = tape.player_participates[i];
3501 else if (game.team_mode && !options.network)
3503 /* try to guess locally connected team mode players (needed for correct
3504 assignment of player figures from level to locally playing players) */
3506 for (i = 0; i < MAX_PLAYERS; i++)
3507 if (setup.input[i].use_joystick ||
3508 setup.input[i].key.left != KSYM_UNDEFINED)
3509 stored_player[i].connected = TRUE;
3512 #if DEBUG_INIT_PLAYER
3515 printf("Player status after level initialization:\n");
3517 for (i = 0; i < MAX_PLAYERS; i++)
3519 struct PlayerInfo *player = &stored_player[i];
3521 printf("- player %d: present == %d, connected == %d, active == %d",
3527 if (local_player == player)
3528 printf(" (local player)");
3535 #if DEBUG_INIT_PLAYER
3537 printf("Reassigning players ...\n");
3540 /* check if any connected player was not found in playfield */
3541 for (i = 0; i < MAX_PLAYERS; i++)
3543 struct PlayerInfo *player = &stored_player[i];
3545 if (player->connected && !player->present)
3547 struct PlayerInfo *field_player = NULL;
3549 #if DEBUG_INIT_PLAYER
3551 printf("- looking for field player for player %d ...\n", i + 1);
3554 /* assign first free player found that is present in the playfield */
3556 /* first try: look for unmapped playfield player that is not connected */
3557 for (j = 0; j < MAX_PLAYERS; j++)
3558 if (field_player == NULL &&
3559 stored_player[j].present &&
3560 !stored_player[j].mapped &&
3561 !stored_player[j].connected)
3562 field_player = &stored_player[j];
3564 /* second try: look for *any* unmapped playfield player */
3565 for (j = 0; j < MAX_PLAYERS; j++)
3566 if (field_player == NULL &&
3567 stored_player[j].present &&
3568 !stored_player[j].mapped)
3569 field_player = &stored_player[j];
3571 if (field_player != NULL)
3573 int jx = field_player->jx, jy = field_player->jy;
3575 #if DEBUG_INIT_PLAYER
3577 printf("- found player %d\n", field_player->index_nr + 1);
3580 player->present = FALSE;
3581 player->active = FALSE;
3583 field_player->present = TRUE;
3584 field_player->active = TRUE;
3587 player->initial_element = field_player->initial_element;
3588 player->artwork_element = field_player->artwork_element;
3590 player->block_last_field = field_player->block_last_field;
3591 player->block_delay_adjustment = field_player->block_delay_adjustment;
3594 StorePlayer[jx][jy] = field_player->element_nr;
3596 field_player->jx = field_player->last_jx = jx;
3597 field_player->jy = field_player->last_jy = jy;
3599 if (local_player == player)
3600 local_player = field_player;
3602 map_player_action[field_player->index_nr] = i;
3604 field_player->mapped = TRUE;
3606 #if DEBUG_INIT_PLAYER
3608 printf("- map_player_action[%d] == %d\n",
3609 field_player->index_nr + 1, i + 1);
3614 if (player->connected && player->present)
3615 player->mapped = TRUE;
3618 #if DEBUG_INIT_PLAYER
3621 printf("Player status after player assignment (first stage):\n");
3623 for (i = 0; i < MAX_PLAYERS; i++)
3625 struct PlayerInfo *player = &stored_player[i];
3627 printf("- player %d: present == %d, connected == %d, active == %d",
3633 if (local_player == player)
3634 printf(" (local player)");
3643 /* check if any connected player was not found in playfield */
3644 for (i = 0; i < MAX_PLAYERS; i++)
3646 struct PlayerInfo *player = &stored_player[i];
3648 if (player->connected && !player->present)
3650 for (j = 0; j < MAX_PLAYERS; j++)
3652 struct PlayerInfo *field_player = &stored_player[j];
3653 int jx = field_player->jx, jy = field_player->jy;
3655 /* assign first free player found that is present in the playfield */
3656 if (field_player->present && !field_player->connected)
3658 player->present = TRUE;
3659 player->active = TRUE;
3661 field_player->present = FALSE;
3662 field_player->active = FALSE;
3664 player->initial_element = field_player->initial_element;
3665 player->artwork_element = field_player->artwork_element;
3667 player->block_last_field = field_player->block_last_field;
3668 player->block_delay_adjustment = field_player->block_delay_adjustment;
3670 StorePlayer[jx][jy] = player->element_nr;
3672 player->jx = player->last_jx = jx;
3673 player->jy = player->last_jy = jy;
3683 printf("::: local_player->present == %d\n", local_player->present);
3688 /* when playing a tape, eliminate all players who do not participate */
3690 #if USE_NEW_PLAYER_ASSIGNMENTS
3692 if (!game.team_mode)
3694 for (i = 0; i < MAX_PLAYERS; i++)
3696 if (stored_player[i].active &&
3697 !tape.player_participates[map_player_action[i]])
3699 struct PlayerInfo *player = &stored_player[i];
3700 int jx = player->jx, jy = player->jy;
3702 #if DEBUG_INIT_PLAYER
3704 printf("Removing player %d at (%d, %d)\n", i + 1, jx, jy);
3707 player->active = FALSE;
3708 StorePlayer[jx][jy] = 0;
3709 Feld[jx][jy] = EL_EMPTY;
3716 for (i = 0; i < MAX_PLAYERS; i++)
3718 if (stored_player[i].active &&
3719 !tape.player_participates[i])
3721 struct PlayerInfo *player = &stored_player[i];
3722 int jx = player->jx, jy = player->jy;
3724 player->active = FALSE;
3725 StorePlayer[jx][jy] = 0;
3726 Feld[jx][jy] = EL_EMPTY;
3731 else if (!options.network && !game.team_mode) /* && !tape.playing */
3733 /* when in single player mode, eliminate all but the first active player */
3735 for (i = 0; i < MAX_PLAYERS; i++)
3737 if (stored_player[i].active)
3739 for (j = i + 1; j < MAX_PLAYERS; j++)
3741 if (stored_player[j].active)
3743 struct PlayerInfo *player = &stored_player[j];
3744 int jx = player->jx, jy = player->jy;
3746 player->active = FALSE;
3747 player->present = FALSE;
3749 StorePlayer[jx][jy] = 0;
3750 Feld[jx][jy] = EL_EMPTY;
3757 /* when recording the game, store which players take part in the game */
3760 #if USE_NEW_PLAYER_ASSIGNMENTS
3761 for (i = 0; i < MAX_PLAYERS; i++)
3762 if (stored_player[i].connected)
3763 tape.player_participates[i] = TRUE;
3765 for (i = 0; i < MAX_PLAYERS; i++)
3766 if (stored_player[i].active)
3767 tape.player_participates[i] = TRUE;
3771 #if DEBUG_INIT_PLAYER
3774 printf("Player status after player assignment (final stage):\n");
3776 for (i = 0; i < MAX_PLAYERS; i++)
3778 struct PlayerInfo *player = &stored_player[i];
3780 printf("- player %d: present == %d, connected == %d, active == %d",
3786 if (local_player == player)
3787 printf(" (local player)");
3794 if (BorderElement == EL_EMPTY)
3797 SBX_Right = lev_fieldx - SCR_FIELDX;
3799 SBY_Lower = lev_fieldy - SCR_FIELDY;
3804 SBX_Right = lev_fieldx - SCR_FIELDX + 1;
3806 SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
3809 if (full_lev_fieldx <= SCR_FIELDX)
3810 SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
3811 if (full_lev_fieldy <= SCR_FIELDY)
3812 SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
3814 if (EVEN(SCR_FIELDX) && full_lev_fieldx > SCR_FIELDX)
3816 if (EVEN(SCR_FIELDY) && full_lev_fieldy > SCR_FIELDY)
3819 /* if local player not found, look for custom element that might create
3820 the player (make some assumptions about the right custom element) */
3821 if (!local_player->present)
3823 int start_x = 0, start_y = 0;
3824 int found_rating = 0;
3825 int found_element = EL_UNDEFINED;
3826 int player_nr = local_player->index_nr;
3828 SCAN_PLAYFIELD(x, y)
3830 int element = Feld[x][y];
3835 if (level.use_start_element[player_nr] &&
3836 level.start_element[player_nr] == element &&
3843 found_element = element;
3846 if (!IS_CUSTOM_ELEMENT(element))
3849 if (CAN_CHANGE(element))
3851 for (i = 0; i < element_info[element].num_change_pages; i++)
3853 /* check for player created from custom element as single target */
3854 content = element_info[element].change_page[i].target_element;
3855 is_player = ELEM_IS_PLAYER(content);
3857 if (is_player && (found_rating < 3 ||
3858 (found_rating == 3 && element < found_element)))
3864 found_element = element;
3869 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
3871 /* check for player created from custom element as explosion content */
3872 content = element_info[element].content.e[xx][yy];
3873 is_player = ELEM_IS_PLAYER(content);
3875 if (is_player && (found_rating < 2 ||
3876 (found_rating == 2 && element < found_element)))
3878 start_x = x + xx - 1;
3879 start_y = y + yy - 1;
3882 found_element = element;
3885 if (!CAN_CHANGE(element))
3888 for (i = 0; i < element_info[element].num_change_pages; i++)
3890 /* check for player created from custom element as extended target */
3892 element_info[element].change_page[i].target_content.e[xx][yy];
3894 is_player = ELEM_IS_PLAYER(content);
3896 if (is_player && (found_rating < 1 ||
3897 (found_rating == 1 && element < found_element)))
3899 start_x = x + xx - 1;
3900 start_y = y + yy - 1;
3903 found_element = element;
3909 scroll_x = SCROLL_POSITION_X(start_x);
3910 scroll_y = SCROLL_POSITION_Y(start_y);
3914 scroll_x = SCROLL_POSITION_X(local_player->jx);
3915 scroll_y = SCROLL_POSITION_Y(local_player->jy);
3918 /* !!! FIX THIS (START) !!! */
3919 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
3921 InitGameEngine_EM();
3923 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
3925 InitGameEngine_SP();
3929 DrawLevel(REDRAW_FIELD);
3932 /* after drawing the level, correct some elements */
3933 if (game.timegate_time_left == 0)
3934 CloseAllOpenTimegates();
3937 /* blit playfield from scroll buffer to normal back buffer for fading in */
3938 BlitScreenToBitmap(backbuffer);
3939 /* !!! FIX THIS (END) !!! */
3941 DrawMaskedBorder(fade_mask);
3946 // full screen redraw is required at this point in the following cases:
3947 // - special editor door undrawn when game was started from level editor
3948 // - drawing area (playfield) was changed and has to be removed completely
3949 redraw_mask = REDRAW_ALL;
3953 if (!game.restart_level)
3955 /* copy default game door content to main double buffer */
3957 /* !!! CHECK AGAIN !!! */
3958 SetPanelBackground();
3959 // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
3960 DrawBackground(DX, DY, DXSIZE, DYSIZE);
3963 SetPanelBackground();
3964 SetDrawBackgroundMask(REDRAW_DOOR_1);
3966 UpdateAndDisplayGameControlValues();
3968 if (!game.restart_level)
3974 CreateGameButtons();
3976 game_gadget[SOUND_CTRL_ID_MUSIC]->checked = setup.sound_music;
3977 game_gadget[SOUND_CTRL_ID_LOOPS]->checked = setup.sound_loops;
3978 game_gadget[SOUND_CTRL_ID_SIMPLE]->checked = setup.sound_simple;
3983 /* copy actual game door content to door double buffer for OpenDoor() */
3984 BlitBitmap(drawto, bitmap_db_door_1, DX, DY, DXSIZE, DYSIZE, 0, 0);
3986 OpenDoor(DOOR_OPEN_ALL);
3988 PlaySound(SND_GAME_STARTING);
3990 if (setup.sound_music)
3993 KeyboardAutoRepeatOffUnlessAutoplay();
3995 #if DEBUG_INIT_PLAYER
3998 printf("Player status (final):\n");
4000 for (i = 0; i < MAX_PLAYERS; i++)
4002 struct PlayerInfo *player = &stored_player[i];
4004 printf("- player %d: present == %d, connected == %d, active == %d",
4010 if (local_player == player)
4011 printf(" (local player)");
4024 if (!game.restart_level && !tape.playing)
4026 LevelStats_incPlayed(level_nr);
4028 SaveLevelSetup_SeriesInfo();
4031 game.restart_level = FALSE;
4033 SaveEngineSnapshotToListInitial();
4036 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y,
4037 int actual_player_x, int actual_player_y)
4039 /* this is used for non-R'n'D game engines to update certain engine values */
4041 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
4043 actual_player_x = correctLevelPosX_EM(actual_player_x);
4044 actual_player_y = correctLevelPosY_EM(actual_player_y);
4047 /* needed to determine if sounds are played within the visible screen area */
4048 scroll_x = actual_scroll_x;
4049 scroll_y = actual_scroll_y;
4051 /* needed to get player position for "follow finger" playing input method */
4052 local_player->jx = actual_player_x;
4053 local_player->jy = actual_player_y;
4056 void InitMovDir(int x, int y)
4058 int i, element = Feld[x][y];
4059 static int xy[4][2] =
4066 static int direction[3][4] =
4068 { MV_RIGHT, MV_UP, MV_LEFT, MV_DOWN },
4069 { MV_LEFT, MV_DOWN, MV_RIGHT, MV_UP },
4070 { MV_LEFT, MV_RIGHT, MV_UP, MV_DOWN }
4079 Feld[x][y] = EL_BUG;
4080 MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
4083 case EL_SPACESHIP_RIGHT:
4084 case EL_SPACESHIP_UP:
4085 case EL_SPACESHIP_LEFT:
4086 case EL_SPACESHIP_DOWN:
4087 Feld[x][y] = EL_SPACESHIP;
4088 MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
4091 case EL_BD_BUTTERFLY_RIGHT:
4092 case EL_BD_BUTTERFLY_UP:
4093 case EL_BD_BUTTERFLY_LEFT:
4094 case EL_BD_BUTTERFLY_DOWN:
4095 Feld[x][y] = EL_BD_BUTTERFLY;
4096 MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
4099 case EL_BD_FIREFLY_RIGHT:
4100 case EL_BD_FIREFLY_UP:
4101 case EL_BD_FIREFLY_LEFT:
4102 case EL_BD_FIREFLY_DOWN:
4103 Feld[x][y] = EL_BD_FIREFLY;
4104 MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
4107 case EL_PACMAN_RIGHT:
4109 case EL_PACMAN_LEFT:
4110 case EL_PACMAN_DOWN:
4111 Feld[x][y] = EL_PACMAN;
4112 MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
4115 case EL_YAMYAM_LEFT:
4116 case EL_YAMYAM_RIGHT:
4118 case EL_YAMYAM_DOWN:
4119 Feld[x][y] = EL_YAMYAM;
4120 MovDir[x][y] = direction[2][element - EL_YAMYAM_LEFT];
4123 case EL_SP_SNIKSNAK:
4124 MovDir[x][y] = MV_UP;
4127 case EL_SP_ELECTRON:
4128 MovDir[x][y] = MV_LEFT;
4135 Feld[x][y] = EL_MOLE;
4136 MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
4140 if (IS_CUSTOM_ELEMENT(element))
4142 struct ElementInfo *ei = &element_info[element];
4143 int move_direction_initial = ei->move_direction_initial;
4144 int move_pattern = ei->move_pattern;
4146 if (move_direction_initial == MV_START_PREVIOUS)
4148 if (MovDir[x][y] != MV_NONE)
4151 move_direction_initial = MV_START_AUTOMATIC;
4154 if (move_direction_initial == MV_START_RANDOM)
4155 MovDir[x][y] = 1 << RND(4);
4156 else if (move_direction_initial & MV_ANY_DIRECTION)
4157 MovDir[x][y] = move_direction_initial;
4158 else if (move_pattern == MV_ALL_DIRECTIONS ||
4159 move_pattern == MV_TURNING_LEFT ||
4160 move_pattern == MV_TURNING_RIGHT ||
4161 move_pattern == MV_TURNING_LEFT_RIGHT ||
4162 move_pattern == MV_TURNING_RIGHT_LEFT ||
4163 move_pattern == MV_TURNING_RANDOM)
4164 MovDir[x][y] = 1 << RND(4);
4165 else if (move_pattern == MV_HORIZONTAL)
4166 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
4167 else if (move_pattern == MV_VERTICAL)
4168 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
4169 else if (move_pattern & MV_ANY_DIRECTION)
4170 MovDir[x][y] = element_info[element].move_pattern;
4171 else if (move_pattern == MV_ALONG_LEFT_SIDE ||
4172 move_pattern == MV_ALONG_RIGHT_SIDE)
4174 /* use random direction as default start direction */
4175 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
4176 MovDir[x][y] = 1 << RND(4);
4178 for (i = 0; i < NUM_DIRECTIONS; i++)
4180 int x1 = x + xy[i][0];
4181 int y1 = y + xy[i][1];
4183 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4185 if (move_pattern == MV_ALONG_RIGHT_SIDE)
4186 MovDir[x][y] = direction[0][i];
4188 MovDir[x][y] = direction[1][i];
4197 MovDir[x][y] = 1 << RND(4);
4199 if (element != EL_BUG &&
4200 element != EL_SPACESHIP &&
4201 element != EL_BD_BUTTERFLY &&
4202 element != EL_BD_FIREFLY)
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 (element == EL_BUG || element == EL_BD_BUTTERFLY)
4214 MovDir[x][y] = direction[0][i];
4217 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
4218 element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4220 MovDir[x][y] = direction[1][i];
4229 GfxDir[x][y] = MovDir[x][y];
4232 void InitAmoebaNr(int x, int y)
4235 int group_nr = AmoebeNachbarNr(x, y);
4239 for (i = 1; i < MAX_NUM_AMOEBA; i++)
4241 if (AmoebaCnt[i] == 0)
4249 AmoebaNr[x][y] = group_nr;
4250 AmoebaCnt[group_nr]++;
4251 AmoebaCnt2[group_nr]++;
4254 static void PlayerWins(struct PlayerInfo *player)
4256 player->LevelSolved = TRUE;
4257 player->GameOver = TRUE;
4259 player->score_final = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
4260 level.native_em_level->lev->score : player->score);
4262 player->LevelSolved_CountingTime = (game.no_time_limit ? TimePlayed :
4264 player->LevelSolved_CountingScore = player->score_final;
4269 static int time, time_final;
4270 static int score, score_final;
4271 static int game_over_delay_1 = 0;
4272 static int game_over_delay_2 = 0;
4273 int game_over_delay_value_1 = 50;
4274 int game_over_delay_value_2 = 50;
4276 if (!local_player->LevelSolved_GameWon)
4280 /* do not start end game actions before the player stops moving (to exit) */
4281 if (local_player->MovPos)
4284 local_player->LevelSolved_GameWon = TRUE;
4285 local_player->LevelSolved_SaveTape = tape.recording;
4286 local_player->LevelSolved_SaveScore = !tape.playing;
4290 LevelStats_incSolved(level_nr);
4292 SaveLevelSetup_SeriesInfo();
4295 if (tape.auto_play) /* tape might already be stopped here */
4296 tape.auto_play_level_solved = TRUE;
4300 game_over_delay_1 = game_over_delay_value_1;
4301 game_over_delay_2 = game_over_delay_value_2;
4303 time = time_final = (game.no_time_limit ? TimePlayed : TimeLeft);
4304 score = score_final = local_player->score_final;
4309 score_final += TimeLeft * level.score[SC_TIME_BONUS];
4311 else if (game.no_time_limit && TimePlayed < 999)
4314 score_final += (999 - TimePlayed) * level.score[SC_TIME_BONUS];
4317 local_player->score_final = score_final;
4319 if (level_editor_test_game)
4322 score = score_final;
4324 local_player->LevelSolved_CountingTime = time;
4325 local_player->LevelSolved_CountingScore = score;
4327 game_panel_controls[GAME_PANEL_TIME].value = time;
4328 game_panel_controls[GAME_PANEL_SCORE].value = score;
4330 DisplayGameControlValues();
4333 if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
4335 if (ExitX >= 0 && ExitY >= 0) /* local player has left the level */
4337 /* close exit door after last player */
4338 if ((AllPlayersGone &&
4339 (Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
4340 Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN ||
4341 Feld[ExitX][ExitY] == EL_STEEL_EXIT_OPEN)) ||
4342 Feld[ExitX][ExitY] == EL_EM_EXIT_OPEN ||
4343 Feld[ExitX][ExitY] == EL_EM_STEEL_EXIT_OPEN)
4345 int element = Feld[ExitX][ExitY];
4347 Feld[ExitX][ExitY] =
4348 (element == EL_EXIT_OPEN ? EL_EXIT_CLOSING :
4349 element == EL_EM_EXIT_OPEN ? EL_EM_EXIT_CLOSING :
4350 element == EL_SP_EXIT_OPEN ? EL_SP_EXIT_CLOSING:
4351 element == EL_STEEL_EXIT_OPEN ? EL_STEEL_EXIT_CLOSING:
4352 EL_EM_STEEL_EXIT_CLOSING);
4354 PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING);
4357 /* player disappears */
4358 DrawLevelField(ExitX, ExitY);
4361 for (i = 0; i < MAX_PLAYERS; i++)
4363 struct PlayerInfo *player = &stored_player[i];
4365 if (player->present)
4367 RemovePlayer(player);
4369 /* player disappears */
4370 DrawLevelField(player->jx, player->jy);
4375 PlaySound(SND_GAME_WINNING);
4378 if (game_over_delay_1 > 0)
4380 game_over_delay_1--;
4385 if (time != time_final)
4387 int time_to_go = ABS(time_final - time);
4388 int time_count_dir = (time < time_final ? +1 : -1);
4389 int time_count_steps = (time_to_go > 100 && time_to_go % 10 == 0 ? 10 : 1);
4391 time += time_count_steps * time_count_dir;
4392 score += time_count_steps * level.score[SC_TIME_BONUS];
4394 local_player->LevelSolved_CountingTime = time;
4395 local_player->LevelSolved_CountingScore = score;
4397 game_panel_controls[GAME_PANEL_TIME].value = time;
4398 game_panel_controls[GAME_PANEL_SCORE].value = score;
4400 DisplayGameControlValues();
4402 if (time == time_final)
4403 StopSound(SND_GAME_LEVELTIME_BONUS);
4404 else if (setup.sound_loops)
4405 PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4407 PlaySound(SND_GAME_LEVELTIME_BONUS);
4412 local_player->LevelSolved_PanelOff = TRUE;
4414 if (game_over_delay_2 > 0)
4416 game_over_delay_2--;
4427 boolean raise_level = FALSE;
4429 local_player->LevelSolved_GameEnd = TRUE;
4431 if (!global.use_envelope_request)
4432 CloseDoor(DOOR_CLOSE_1);
4434 if (local_player->LevelSolved_SaveTape)
4436 SaveTapeChecked(tape.level_nr); /* ask to save tape */
4439 CloseDoor(DOOR_CLOSE_ALL);
4441 if (level_editor_test_game)
4443 SetGameStatus(GAME_MODE_MAIN);
4450 if (!local_player->LevelSolved_SaveScore)
4452 SetGameStatus(GAME_MODE_MAIN);
4459 if (level_nr == leveldir_current->handicap_level)
4461 leveldir_current->handicap_level++;
4463 SaveLevelSetup_SeriesInfo();
4466 if (setup.increment_levels &&
4467 level_nr < leveldir_current->last_level)
4468 raise_level = TRUE; /* advance to next level */
4470 if ((hi_pos = NewHiScore()) >= 0)
4472 SetGameStatus(GAME_MODE_SCORES);
4474 DrawHallOfFame(hi_pos);
4484 SetGameStatus(GAME_MODE_MAIN);
4500 boolean one_score_entry_per_name = !program.many_scores_per_name;
4502 LoadScore(level_nr);
4504 if (strEqual(setup.player_name, EMPTY_PLAYER_NAME) ||
4505 local_player->score_final < highscore[MAX_SCORE_ENTRIES - 1].Score)
4508 for (k = 0; k < MAX_SCORE_ENTRIES; k++)
4510 if (local_player->score_final > highscore[k].Score)
4512 /* player has made it to the hall of fame */
4514 if (k < MAX_SCORE_ENTRIES - 1)
4516 int m = MAX_SCORE_ENTRIES - 1;
4518 if (one_score_entry_per_name)
4520 for (l = k; l < MAX_SCORE_ENTRIES; l++)
4521 if (strEqual(setup.player_name, highscore[l].Name))
4524 if (m == k) /* player's new highscore overwrites his old one */
4528 for (l = m; l > k; l--)
4530 strcpy(highscore[l].Name, highscore[l - 1].Name);
4531 highscore[l].Score = highscore[l - 1].Score;
4537 strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
4538 highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
4539 highscore[k].Score = local_player->score_final;
4544 else if (one_score_entry_per_name &&
4545 !strncmp(setup.player_name, highscore[k].Name,
4546 MAX_PLAYER_NAME_LEN))
4547 break; /* player already there with a higher score */
4551 SaveScore(level_nr);
4556 inline static int getElementMoveStepsizeExt(int x, int y, int direction)
4558 int element = Feld[x][y];
4559 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
4560 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
4561 int horiz_move = (dx != 0);
4562 int sign = (horiz_move ? dx : dy);
4563 int step = sign * element_info[element].move_stepsize;
4565 /* special values for move stepsize for spring and things on conveyor belt */
4568 if (CAN_FALL(element) &&
4569 y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
4570 step = sign * MOVE_STEPSIZE_NORMAL / 2;
4571 else if (element == EL_SPRING)
4572 step = sign * MOVE_STEPSIZE_NORMAL * 2;
4578 inline static int getElementMoveStepsize(int x, int y)
4580 return getElementMoveStepsizeExt(x, y, MovDir[x][y]);
4583 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
4585 if (player->GfxAction != action || player->GfxDir != dir)
4587 player->GfxAction = action;
4588 player->GfxDir = dir;
4590 player->StepFrame = 0;
4594 static void ResetGfxFrame(int x, int y)
4596 int element = Feld[x][y];
4597 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
4599 if (graphic_info[graphic].anim_global_sync)
4600 GfxFrame[x][y] = FrameCounter;
4601 else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
4602 GfxFrame[x][y] = CustomValue[x][y];
4603 else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
4604 GfxFrame[x][y] = element_info[element].collect_score;
4605 else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
4606 GfxFrame[x][y] = ChangeDelay[x][y];
4609 static void ResetGfxAnimation(int x, int y)
4611 GfxAction[x][y] = ACTION_DEFAULT;
4612 GfxDir[x][y] = MovDir[x][y];
4615 ResetGfxFrame(x, y);
4618 static void ResetRandomAnimationValue(int x, int y)
4620 GfxRandom[x][y] = INIT_GFX_RANDOM();
4623 void InitMovingField(int x, int y, int direction)
4625 int element = Feld[x][y];
4626 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
4627 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
4630 boolean is_moving_before, is_moving_after;
4632 /* check if element was/is moving or being moved before/after mode change */
4633 is_moving_before = (WasJustMoving[x][y] != 0);
4634 is_moving_after = (getElementMoveStepsizeExt(x, y, direction) != 0);
4636 /* reset animation only for moving elements which change direction of moving
4637 or which just started or stopped moving
4638 (else CEs with property "can move" / "not moving" are reset each frame) */
4639 if (is_moving_before != is_moving_after ||
4640 direction != MovDir[x][y])
4641 ResetGfxAnimation(x, y);
4643 MovDir[x][y] = direction;
4644 GfxDir[x][y] = direction;
4646 GfxAction[x][y] = (!is_moving_after ? ACTION_WAITING :
4647 direction == MV_DOWN && CAN_FALL(element) ?
4648 ACTION_FALLING : ACTION_MOVING);
4650 /* this is needed for CEs with property "can move" / "not moving" */
4652 if (is_moving_after)
4654 if (Feld[newx][newy] == EL_EMPTY)
4655 Feld[newx][newy] = EL_BLOCKED;
4657 MovDir[newx][newy] = MovDir[x][y];
4659 CustomValue[newx][newy] = CustomValue[x][y];
4661 GfxFrame[newx][newy] = GfxFrame[x][y];
4662 GfxRandom[newx][newy] = GfxRandom[x][y];
4663 GfxAction[newx][newy] = GfxAction[x][y];
4664 GfxDir[newx][newy] = GfxDir[x][y];
4668 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
4670 int direction = MovDir[x][y];
4671 int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0);
4672 int newy = y + (direction & MV_UP ? -1 : direction & MV_DOWN ? +1 : 0);
4678 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
4680 int oldx = x, oldy = y;
4681 int direction = MovDir[x][y];
4683 if (direction == MV_LEFT)
4685 else if (direction == MV_RIGHT)
4687 else if (direction == MV_UP)
4689 else if (direction == MV_DOWN)
4692 *comes_from_x = oldx;
4693 *comes_from_y = oldy;
4696 int MovingOrBlocked2Element(int x, int y)
4698 int element = Feld[x][y];
4700 if (element == EL_BLOCKED)
4704 Blocked2Moving(x, y, &oldx, &oldy);
4705 return Feld[oldx][oldy];
4711 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
4713 /* like MovingOrBlocked2Element(), but if element is moving
4714 and (x,y) is the field the moving element is just leaving,
4715 return EL_BLOCKED instead of the element value */
4716 int element = Feld[x][y];
4718 if (IS_MOVING(x, y))
4720 if (element == EL_BLOCKED)
4724 Blocked2Moving(x, y, &oldx, &oldy);
4725 return Feld[oldx][oldy];
4734 static void RemoveField(int x, int y)
4736 Feld[x][y] = EL_EMPTY;
4742 CustomValue[x][y] = 0;
4745 ChangeDelay[x][y] = 0;
4746 ChangePage[x][y] = -1;
4747 Pushed[x][y] = FALSE;
4749 GfxElement[x][y] = EL_UNDEFINED;
4750 GfxAction[x][y] = ACTION_DEFAULT;
4751 GfxDir[x][y] = MV_NONE;
4754 void RemoveMovingField(int x, int y)
4756 int oldx = x, oldy = y, newx = x, newy = y;
4757 int element = Feld[x][y];
4758 int next_element = EL_UNDEFINED;
4760 if (element != EL_BLOCKED && !IS_MOVING(x, y))
4763 if (IS_MOVING(x, y))
4765 Moving2Blocked(x, y, &newx, &newy);
4767 if (Feld[newx][newy] != EL_BLOCKED)
4769 /* element is moving, but target field is not free (blocked), but
4770 already occupied by something different (example: acid pool);
4771 in this case, only remove the moving field, but not the target */
4773 RemoveField(oldx, oldy);
4775 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
4777 TEST_DrawLevelField(oldx, oldy);
4782 else if (element == EL_BLOCKED)
4784 Blocked2Moving(x, y, &oldx, &oldy);
4785 if (!IS_MOVING(oldx, oldy))
4789 if (element == EL_BLOCKED &&
4790 (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
4791 Feld[oldx][oldy] == EL_QUICKSAND_FAST_EMPTYING ||
4792 Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
4793 Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
4794 Feld[oldx][oldy] == EL_DC_MAGIC_WALL_EMPTYING ||
4795 Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
4796 next_element = get_next_element(Feld[oldx][oldy]);
4798 RemoveField(oldx, oldy);
4799 RemoveField(newx, newy);
4801 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
4803 if (next_element != EL_UNDEFINED)
4804 Feld[oldx][oldy] = next_element;
4806 TEST_DrawLevelField(oldx, oldy);
4807 TEST_DrawLevelField(newx, newy);
4810 void DrawDynamite(int x, int y)
4812 int sx = SCREENX(x), sy = SCREENY(y);
4813 int graphic = el2img(Feld[x][y]);
4816 if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
4819 if (IS_WALKABLE_INSIDE(Back[x][y]))
4823 DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
4824 else if (Store[x][y])
4825 DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
4827 frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
4829 if (Back[x][y] || Store[x][y])
4830 DrawGraphicThruMask(sx, sy, graphic, frame);
4832 DrawGraphic(sx, sy, graphic, frame);
4835 void CheckDynamite(int x, int y)
4837 if (MovDelay[x][y] != 0) /* dynamite is still waiting to explode */
4841 if (MovDelay[x][y] != 0)
4844 PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
4850 StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
4855 static void setMinimalPlayerBoundaries(int *sx1, int *sy1, int *sx2, int *sy2)
4857 boolean num_checked_players = 0;
4860 for (i = 0; i < MAX_PLAYERS; i++)
4862 if (stored_player[i].active)
4864 int sx = stored_player[i].jx;
4865 int sy = stored_player[i].jy;
4867 if (num_checked_players == 0)
4874 *sx1 = MIN(*sx1, sx);
4875 *sy1 = MIN(*sy1, sy);
4876 *sx2 = MAX(*sx2, sx);
4877 *sy2 = MAX(*sy2, sy);
4880 num_checked_players++;
4885 static boolean checkIfAllPlayersFitToScreen_RND()
4887 int sx1 = 0, sy1 = 0, sx2 = 0, sy2 = 0;
4889 setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
4891 return (sx2 - sx1 < SCR_FIELDX &&
4892 sy2 - sy1 < SCR_FIELDY);
4895 static void setScreenCenteredToAllPlayers(int *sx, int *sy)
4897 int sx1 = scroll_x, sy1 = scroll_y, sx2 = scroll_x, sy2 = scroll_y;
4899 setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
4901 *sx = (sx1 + sx2) / 2;
4902 *sy = (sy1 + sy2) / 2;
4905 void DrawRelocateScreen(int old_x, int old_y, int x, int y, int move_dir,
4906 boolean center_screen, boolean quick_relocation)
4908 unsigned int frame_delay_value_old = GetVideoFrameDelay();
4909 boolean ffwd_delay = (tape.playing && tape.fast_forward);
4910 boolean no_delay = (tape.warp_forward);
4911 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
4912 int wait_delay_value = (no_delay ? 0 : frame_delay_value);
4913 int new_scroll_x, new_scroll_y;
4915 if (level.lazy_relocation && IN_VIS_FIELD(SCREENX(x), SCREENY(y)))
4917 /* case 1: quick relocation inside visible screen (without scrolling) */
4924 if (!level.shifted_relocation || center_screen)
4926 /* relocation _with_ centering of screen */
4928 new_scroll_x = SCROLL_POSITION_X(x);
4929 new_scroll_y = SCROLL_POSITION_Y(y);
4933 /* relocation _without_ centering of screen */
4935 int center_scroll_x = SCROLL_POSITION_X(old_x);
4936 int center_scroll_y = SCROLL_POSITION_Y(old_y);
4937 int offset_x = x + (scroll_x - center_scroll_x);
4938 int offset_y = y + (scroll_y - center_scroll_y);
4940 /* for new screen position, apply previous offset to center position */
4941 new_scroll_x = SCROLL_POSITION_X(offset_x);
4942 new_scroll_y = SCROLL_POSITION_Y(offset_y);
4945 if (quick_relocation)
4947 /* case 2: quick relocation (redraw without visible scrolling) */
4949 scroll_x = new_scroll_x;
4950 scroll_y = new_scroll_y;
4957 /* case 3: visible relocation (with scrolling to new position) */
4959 ScrollScreen(NULL, SCROLL_GO_ON); /* scroll last frame to full tile */
4961 SetVideoFrameDelay(wait_delay_value);
4963 while (scroll_x != new_scroll_x || scroll_y != new_scroll_y)
4966 int fx = FX, fy = FY;
4968 dx = (new_scroll_x < scroll_x ? +1 : new_scroll_x > scroll_x ? -1 : 0);
4969 dy = (new_scroll_y < scroll_y ? +1 : new_scroll_y > scroll_y ? -1 : 0);
4971 if (dx == 0 && dy == 0) /* no scrolling needed at all */
4977 fx += dx * TILEX / 2;
4978 fy += dy * TILEY / 2;
4980 ScrollLevel(dx, dy);
4983 /* scroll in two steps of half tile size to make things smoother */
4984 BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
4986 /* scroll second step to align at full tile size */
4987 BlitScreenToBitmap(window);
4993 SetVideoFrameDelay(frame_delay_value_old);
4996 void RelocatePlayer(int jx, int jy, int el_player_raw)
4998 int el_player = GET_PLAYER_ELEMENT(el_player_raw);
4999 int player_nr = GET_PLAYER_NR(el_player);
5000 struct PlayerInfo *player = &stored_player[player_nr];
5001 boolean ffwd_delay = (tape.playing && tape.fast_forward);
5002 boolean no_delay = (tape.warp_forward);
5003 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5004 int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5005 int old_jx = player->jx;
5006 int old_jy = player->jy;
5007 int old_element = Feld[old_jx][old_jy];
5008 int element = Feld[jx][jy];
5009 boolean player_relocated = (old_jx != jx || old_jy != jy);
5011 int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
5012 int move_dir_vert = (jy < old_jy ? MV_UP : jy > old_jy ? MV_DOWN : 0);
5013 int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
5014 int enter_side_vert = MV_DIR_OPPOSITE(move_dir_vert);
5015 int leave_side_horiz = move_dir_horiz;
5016 int leave_side_vert = move_dir_vert;
5017 int enter_side = enter_side_horiz | enter_side_vert;
5018 int leave_side = leave_side_horiz | leave_side_vert;
5020 if (player->GameOver) /* do not reanimate dead player */
5023 if (!player_relocated) /* no need to relocate the player */
5026 if (IS_PLAYER(jx, jy)) /* player already placed at new position */
5028 RemoveField(jx, jy); /* temporarily remove newly placed player */
5029 DrawLevelField(jx, jy);
5032 if (player->present)
5034 while (player->MovPos)
5036 ScrollPlayer(player, SCROLL_GO_ON);
5037 ScrollScreen(NULL, SCROLL_GO_ON);
5039 AdvanceFrameAndPlayerCounters(player->index_nr);
5043 BackToFront_WithFrameDelay(wait_delay_value);
5046 DrawPlayer(player); /* needed here only to cleanup last field */
5047 DrawLevelField(player->jx, player->jy); /* remove player graphic */
5049 player->is_moving = FALSE;
5052 if (IS_CUSTOM_ELEMENT(old_element))
5053 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
5055 player->index_bit, leave_side);
5057 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
5059 player->index_bit, leave_side);
5061 Feld[jx][jy] = el_player;
5062 InitPlayerField(jx, jy, el_player, TRUE);
5064 /* "InitPlayerField()" above sets Feld[jx][jy] to EL_EMPTY, but it may be
5065 possible that the relocation target field did not contain a player element,
5066 but a walkable element, to which the new player was relocated -- in this
5067 case, restore that (already initialized!) element on the player field */
5068 if (!ELEM_IS_PLAYER(element)) /* player may be set on walkable element */
5070 Feld[jx][jy] = element; /* restore previously existing element */
5073 /* only visually relocate centered player */
5074 DrawRelocateScreen(old_jx, old_jy, player->jx, player->jy, player->MovDir,
5075 FALSE, level.instant_relocation);
5077 TestIfPlayerTouchesBadThing(jx, jy);
5078 TestIfPlayerTouchesCustomElement(jx, jy);
5080 if (IS_CUSTOM_ELEMENT(element))
5081 CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
5082 player->index_bit, enter_side);
5084 CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
5085 player->index_bit, enter_side);
5087 if (player->is_switching)
5089 /* ensure that relocation while still switching an element does not cause
5090 a new element to be treated as also switched directly after relocation
5091 (this is important for teleporter switches that teleport the player to
5092 a place where another teleporter switch is in the same direction, which
5093 would then incorrectly be treated as immediately switched before the
5094 direction key that caused the switch was released) */
5096 player->switch_x += jx - old_jx;
5097 player->switch_y += jy - old_jy;
5101 void Explode(int ex, int ey, int phase, int mode)
5107 /* !!! eliminate this variable !!! */
5108 int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
5110 if (game.explosions_delayed)
5112 ExplodeField[ex][ey] = mode;
5116 if (phase == EX_PHASE_START) /* initialize 'Store[][]' field */
5118 int center_element = Feld[ex][ey];
5119 int artwork_element, explosion_element; /* set these values later */
5121 /* remove things displayed in background while burning dynamite */
5122 if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
5125 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
5127 /* put moving element to center field (and let it explode there) */
5128 center_element = MovingOrBlocked2Element(ex, ey);
5129 RemoveMovingField(ex, ey);
5130 Feld[ex][ey] = center_element;
5133 /* now "center_element" is finally determined -- set related values now */
5134 artwork_element = center_element; /* for custom player artwork */
5135 explosion_element = center_element; /* for custom player artwork */
5137 if (IS_PLAYER(ex, ey))
5139 int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
5141 artwork_element = stored_player[player_nr].artwork_element;
5143 if (level.use_explosion_element[player_nr])
5145 explosion_element = level.explosion_element[player_nr];
5146 artwork_element = explosion_element;
5150 if (mode == EX_TYPE_NORMAL ||
5151 mode == EX_TYPE_CENTER ||
5152 mode == EX_TYPE_CROSS)
5153 PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
5155 last_phase = element_info[explosion_element].explosion_delay + 1;
5157 for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
5159 int xx = x - ex + 1;
5160 int yy = y - ey + 1;
5163 if (!IN_LEV_FIELD(x, y) ||
5164 (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
5165 (mode == EX_TYPE_CROSS && (x != ex && y != ey)))
5168 element = Feld[x][y];
5170 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
5172 element = MovingOrBlocked2Element(x, y);
5174 if (!IS_EXPLOSION_PROOF(element))
5175 RemoveMovingField(x, y);
5178 /* indestructible elements can only explode in center (but not flames) */
5179 if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
5180 mode == EX_TYPE_BORDER)) ||
5181 element == EL_FLAMES)
5184 /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
5185 behaviour, for example when touching a yamyam that explodes to rocks
5186 with active deadly shield, a rock is created under the player !!! */
5187 /* (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8) */
5189 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
5190 (game.engine_version < VERSION_IDENT(3,1,0,0) ||
5191 (x == ex && y == ey && mode != EX_TYPE_BORDER)))
5193 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
5196 if (IS_ACTIVE_BOMB(element))
5198 /* re-activate things under the bomb like gate or penguin */
5199 Feld[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
5206 /* save walkable background elements while explosion on same tile */
5207 if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
5208 (x != ex || y != ey || mode == EX_TYPE_BORDER))
5209 Back[x][y] = element;
5211 /* ignite explodable elements reached by other explosion */
5212 if (element == EL_EXPLOSION)
5213 element = Store2[x][y];
5215 if (AmoebaNr[x][y] &&
5216 (element == EL_AMOEBA_FULL ||
5217 element == EL_BD_AMOEBA ||
5218 element == EL_AMOEBA_GROWING))
5220 AmoebaCnt[AmoebaNr[x][y]]--;
5221 AmoebaCnt2[AmoebaNr[x][y]]--;
5226 if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
5228 int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
5230 Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
5232 if (PLAYERINFO(ex, ey)->use_murphy)
5233 Store[x][y] = EL_EMPTY;
5236 /* !!! check this case -- currently needed for rnd_rado_negundo_v,
5237 !!! levels 015 018 019 020 021 022 023 026 027 028 !!! */
5238 else if (ELEM_IS_PLAYER(center_element))
5239 Store[x][y] = EL_EMPTY;
5240 else if (center_element == EL_YAMYAM)
5241 Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
5242 else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
5243 Store[x][y] = element_info[center_element].content.e[xx][yy];
5245 /* needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
5246 (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
5247 otherwise) -- FIX THIS !!! */
5248 else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
5249 Store[x][y] = element_info[element].content.e[1][1];
5251 else if (!CAN_EXPLODE(element))
5252 Store[x][y] = element_info[element].content.e[1][1];
5255 Store[x][y] = EL_EMPTY;
5257 if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
5258 center_element == EL_AMOEBA_TO_DIAMOND)
5259 Store2[x][y] = element;
5261 Feld[x][y] = EL_EXPLOSION;
5262 GfxElement[x][y] = artwork_element;
5264 ExplodePhase[x][y] = 1;
5265 ExplodeDelay[x][y] = last_phase;
5270 if (center_element == EL_YAMYAM)
5271 game.yamyam_content_nr =
5272 (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
5284 GfxFrame[x][y] = 0; /* restart explosion animation */
5286 last_phase = ExplodeDelay[x][y];
5288 ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
5290 /* this can happen if the player leaves an explosion just in time */
5291 if (GfxElement[x][y] == EL_UNDEFINED)
5292 GfxElement[x][y] = EL_EMPTY;
5294 border_element = Store2[x][y];
5295 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5296 border_element = StorePlayer[x][y];
5298 if (phase == element_info[border_element].ignition_delay ||
5299 phase == last_phase)
5301 boolean border_explosion = FALSE;
5303 if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
5304 !PLAYER_EXPLOSION_PROTECTED(x, y))
5306 KillPlayerUnlessExplosionProtected(x, y);
5307 border_explosion = TRUE;
5309 else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
5311 Feld[x][y] = Store2[x][y];
5314 border_explosion = TRUE;
5316 else if (border_element == EL_AMOEBA_TO_DIAMOND)
5318 AmoebeUmwandeln(x, y);
5320 border_explosion = TRUE;
5323 /* if an element just explodes due to another explosion (chain-reaction),
5324 do not immediately end the new explosion when it was the last frame of
5325 the explosion (as it would be done in the following "if"-statement!) */
5326 if (border_explosion && phase == last_phase)
5330 if (phase == last_phase)
5334 element = Feld[x][y] = Store[x][y];
5335 Store[x][y] = Store2[x][y] = 0;
5336 GfxElement[x][y] = EL_UNDEFINED;
5338 /* player can escape from explosions and might therefore be still alive */
5339 if (element >= EL_PLAYER_IS_EXPLODING_1 &&
5340 element <= EL_PLAYER_IS_EXPLODING_4)
5342 int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
5343 int explosion_element = EL_PLAYER_1 + player_nr;
5344 int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
5345 int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
5347 if (level.use_explosion_element[player_nr])
5348 explosion_element = level.explosion_element[player_nr];
5350 Feld[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
5351 element_info[explosion_element].content.e[xx][yy]);
5354 /* restore probably existing indestructible background element */
5355 if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
5356 element = Feld[x][y] = Back[x][y];
5359 MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
5360 GfxDir[x][y] = MV_NONE;
5361 ChangeDelay[x][y] = 0;
5362 ChangePage[x][y] = -1;
5364 CustomValue[x][y] = 0;
5366 InitField_WithBug2(x, y, FALSE);
5368 TEST_DrawLevelField(x, y);
5370 TestIfElementTouchesCustomElement(x, y);
5372 if (GFX_CRUMBLED(element))
5373 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
5375 if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
5376 StorePlayer[x][y] = 0;
5378 if (ELEM_IS_PLAYER(element))
5379 RelocatePlayer(x, y, element);
5381 else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
5383 int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
5384 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5387 TEST_DrawLevelFieldCrumbled(x, y);
5389 if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
5391 DrawLevelElement(x, y, Back[x][y]);
5392 DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
5394 else if (IS_WALKABLE_UNDER(Back[x][y]))
5396 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5397 DrawLevelElementThruMask(x, y, Back[x][y]);
5399 else if (!IS_WALKABLE_INSIDE(Back[x][y]))
5400 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5404 void DynaExplode(int ex, int ey)
5407 int dynabomb_element = Feld[ex][ey];
5408 int dynabomb_size = 1;
5409 boolean dynabomb_xl = FALSE;
5410 struct PlayerInfo *player;
5411 static int xy[4][2] =
5419 if (IS_ACTIVE_BOMB(dynabomb_element))
5421 player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
5422 dynabomb_size = player->dynabomb_size;
5423 dynabomb_xl = player->dynabomb_xl;
5424 player->dynabombs_left++;
5427 Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
5429 for (i = 0; i < NUM_DIRECTIONS; i++)
5431 for (j = 1; j <= dynabomb_size; j++)
5433 int x = ex + j * xy[i][0];
5434 int y = ey + j * xy[i][1];
5437 if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
5440 element = Feld[x][y];
5442 /* do not restart explosions of fields with active bombs */
5443 if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
5446 Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
5448 if (element != EL_EMPTY && element != EL_EXPLOSION &&
5449 !IS_DIGGABLE(element) && !dynabomb_xl)
5455 void Bang(int x, int y)
5457 int element = MovingOrBlocked2Element(x, y);
5458 int explosion_type = EX_TYPE_NORMAL;
5460 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5462 struct PlayerInfo *player = PLAYERINFO(x, y);
5464 element = Feld[x][y] = player->initial_element;
5466 if (level.use_explosion_element[player->index_nr])
5468 int explosion_element = level.explosion_element[player->index_nr];
5470 if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
5471 explosion_type = EX_TYPE_CROSS;
5472 else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
5473 explosion_type = EX_TYPE_CENTER;
5481 case EL_BD_BUTTERFLY:
5484 case EL_DARK_YAMYAM:
5488 RaiseScoreElement(element);
5491 case EL_DYNABOMB_PLAYER_1_ACTIVE:
5492 case EL_DYNABOMB_PLAYER_2_ACTIVE:
5493 case EL_DYNABOMB_PLAYER_3_ACTIVE:
5494 case EL_DYNABOMB_PLAYER_4_ACTIVE:
5495 case EL_DYNABOMB_INCREASE_NUMBER:
5496 case EL_DYNABOMB_INCREASE_SIZE:
5497 case EL_DYNABOMB_INCREASE_POWER:
5498 explosion_type = EX_TYPE_DYNA;
5501 case EL_DC_LANDMINE:
5502 explosion_type = EX_TYPE_CENTER;
5507 case EL_LAMP_ACTIVE:
5508 case EL_AMOEBA_TO_DIAMOND:
5509 if (!IS_PLAYER(x, y)) /* penguin and player may be at same field */
5510 explosion_type = EX_TYPE_CENTER;
5514 if (element_info[element].explosion_type == EXPLODES_CROSS)
5515 explosion_type = EX_TYPE_CROSS;
5516 else if (element_info[element].explosion_type == EXPLODES_1X1)
5517 explosion_type = EX_TYPE_CENTER;
5521 if (explosion_type == EX_TYPE_DYNA)
5524 Explode(x, y, EX_PHASE_START, explosion_type);
5526 CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
5529 void SplashAcid(int x, int y)
5531 if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
5532 (!IN_LEV_FIELD(x - 1, y - 2) ||
5533 !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
5534 Feld[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
5536 if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
5537 (!IN_LEV_FIELD(x + 1, y - 2) ||
5538 !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
5539 Feld[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
5541 PlayLevelSound(x, y, SND_ACID_SPLASHING);
5544 static void InitBeltMovement()
5546 static int belt_base_element[4] =
5548 EL_CONVEYOR_BELT_1_LEFT,
5549 EL_CONVEYOR_BELT_2_LEFT,
5550 EL_CONVEYOR_BELT_3_LEFT,
5551 EL_CONVEYOR_BELT_4_LEFT
5553 static int belt_base_active_element[4] =
5555 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
5556 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
5557 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
5558 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
5563 /* set frame order for belt animation graphic according to belt direction */
5564 for (i = 0; i < NUM_BELTS; i++)
5568 for (j = 0; j < NUM_BELT_PARTS; j++)
5570 int element = belt_base_active_element[belt_nr] + j;
5571 int graphic_1 = el2img(element);
5572 int graphic_2 = el2panelimg(element);
5574 if (game.belt_dir[i] == MV_LEFT)
5576 graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
5577 graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
5581 graphic_info[graphic_1].anim_mode |= ANIM_REVERSE;
5582 graphic_info[graphic_2].anim_mode |= ANIM_REVERSE;
5587 SCAN_PLAYFIELD(x, y)
5589 int element = Feld[x][y];
5591 for (i = 0; i < NUM_BELTS; i++)
5593 if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
5595 int e_belt_nr = getBeltNrFromBeltElement(element);
5598 if (e_belt_nr == belt_nr)
5600 int belt_part = Feld[x][y] - belt_base_element[belt_nr];
5602 Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
5609 static void ToggleBeltSwitch(int x, int y)
5611 static int belt_base_element[4] =
5613 EL_CONVEYOR_BELT_1_LEFT,
5614 EL_CONVEYOR_BELT_2_LEFT,
5615 EL_CONVEYOR_BELT_3_LEFT,
5616 EL_CONVEYOR_BELT_4_LEFT
5618 static int belt_base_active_element[4] =
5620 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
5621 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
5622 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
5623 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
5625 static int belt_base_switch_element[4] =
5627 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
5628 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
5629 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
5630 EL_CONVEYOR_BELT_4_SWITCH_LEFT
5632 static int belt_move_dir[4] =
5640 int element = Feld[x][y];
5641 int belt_nr = getBeltNrFromBeltSwitchElement(element);
5642 int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
5643 int belt_dir = belt_move_dir[belt_dir_nr];
5646 if (!IS_BELT_SWITCH(element))
5649 game.belt_dir_nr[belt_nr] = belt_dir_nr;
5650 game.belt_dir[belt_nr] = belt_dir;
5652 if (belt_dir_nr == 3)
5655 /* set frame order for belt animation graphic according to belt direction */
5656 for (i = 0; i < NUM_BELT_PARTS; i++)
5658 int element = belt_base_active_element[belt_nr] + i;
5659 int graphic_1 = el2img(element);
5660 int graphic_2 = el2panelimg(element);
5662 if (belt_dir == MV_LEFT)
5664 graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
5665 graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
5669 graphic_info[graphic_1].anim_mode |= ANIM_REVERSE;
5670 graphic_info[graphic_2].anim_mode |= ANIM_REVERSE;
5674 SCAN_PLAYFIELD(xx, yy)
5676 int element = Feld[xx][yy];
5678 if (IS_BELT_SWITCH(element))
5680 int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
5682 if (e_belt_nr == belt_nr)
5684 Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
5685 TEST_DrawLevelField(xx, yy);
5688 else if (IS_BELT(element) && belt_dir != MV_NONE)
5690 int e_belt_nr = getBeltNrFromBeltElement(element);
5692 if (e_belt_nr == belt_nr)
5694 int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
5696 Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
5697 TEST_DrawLevelField(xx, yy);
5700 else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
5702 int e_belt_nr = getBeltNrFromBeltActiveElement(element);
5704 if (e_belt_nr == belt_nr)
5706 int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
5708 Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
5709 TEST_DrawLevelField(xx, yy);
5715 static void ToggleSwitchgateSwitch(int x, int y)
5719 game.switchgate_pos = !game.switchgate_pos;
5721 SCAN_PLAYFIELD(xx, yy)
5723 int element = Feld[xx][yy];
5725 if (element == EL_SWITCHGATE_SWITCH_UP)
5727 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_DOWN;
5728 TEST_DrawLevelField(xx, yy);
5730 else if (element == EL_SWITCHGATE_SWITCH_DOWN)
5732 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP;
5733 TEST_DrawLevelField(xx, yy);
5735 else if (element == EL_DC_SWITCHGATE_SWITCH_UP)
5737 Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_DOWN;
5738 TEST_DrawLevelField(xx, yy);
5740 else if (element == EL_DC_SWITCHGATE_SWITCH_DOWN)
5742 Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP;
5743 TEST_DrawLevelField(xx, yy);
5745 else if (element == EL_SWITCHGATE_OPEN ||
5746 element == EL_SWITCHGATE_OPENING)
5748 Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
5750 PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
5752 else if (element == EL_SWITCHGATE_CLOSED ||
5753 element == EL_SWITCHGATE_CLOSING)
5755 Feld[xx][yy] = EL_SWITCHGATE_OPENING;
5757 PlayLevelSoundAction(xx, yy, ACTION_OPENING);
5762 static int getInvisibleActiveFromInvisibleElement(int element)
5764 return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
5765 element == EL_INVISIBLE_WALL ? EL_INVISIBLE_WALL_ACTIVE :
5766 element == EL_INVISIBLE_SAND ? EL_INVISIBLE_SAND_ACTIVE :
5770 static int getInvisibleFromInvisibleActiveElement(int element)
5772 return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
5773 element == EL_INVISIBLE_WALL_ACTIVE ? EL_INVISIBLE_WALL :
5774 element == EL_INVISIBLE_SAND_ACTIVE ? EL_INVISIBLE_SAND :
5778 static void RedrawAllLightSwitchesAndInvisibleElements()
5782 SCAN_PLAYFIELD(x, y)
5784 int element = Feld[x][y];
5786 if (element == EL_LIGHT_SWITCH &&
5787 game.light_time_left > 0)
5789 Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
5790 TEST_DrawLevelField(x, y);
5792 else if (element == EL_LIGHT_SWITCH_ACTIVE &&
5793 game.light_time_left == 0)
5795 Feld[x][y] = EL_LIGHT_SWITCH;
5796 TEST_DrawLevelField(x, y);
5798 else if (element == EL_EMC_DRIPPER &&
5799 game.light_time_left > 0)
5801 Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
5802 TEST_DrawLevelField(x, y);
5804 else if (element == EL_EMC_DRIPPER_ACTIVE &&
5805 game.light_time_left == 0)
5807 Feld[x][y] = EL_EMC_DRIPPER;
5808 TEST_DrawLevelField(x, y);
5810 else if (element == EL_INVISIBLE_STEELWALL ||
5811 element == EL_INVISIBLE_WALL ||
5812 element == EL_INVISIBLE_SAND)
5814 if (game.light_time_left > 0)
5815 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
5817 TEST_DrawLevelField(x, y);
5819 /* uncrumble neighbour fields, if needed */
5820 if (element == EL_INVISIBLE_SAND)
5821 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
5823 else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
5824 element == EL_INVISIBLE_WALL_ACTIVE ||
5825 element == EL_INVISIBLE_SAND_ACTIVE)
5827 if (game.light_time_left == 0)
5828 Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
5830 TEST_DrawLevelField(x, y);
5832 /* re-crumble neighbour fields, if needed */
5833 if (element == EL_INVISIBLE_SAND)
5834 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
5839 static void RedrawAllInvisibleElementsForLenses()
5843 SCAN_PLAYFIELD(x, y)
5845 int element = Feld[x][y];
5847 if (element == EL_EMC_DRIPPER &&
5848 game.lenses_time_left > 0)
5850 Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
5851 TEST_DrawLevelField(x, y);
5853 else if (element == EL_EMC_DRIPPER_ACTIVE &&
5854 game.lenses_time_left == 0)
5856 Feld[x][y] = EL_EMC_DRIPPER;
5857 TEST_DrawLevelField(x, y);
5859 else if (element == EL_INVISIBLE_STEELWALL ||
5860 element == EL_INVISIBLE_WALL ||
5861 element == EL_INVISIBLE_SAND)
5863 if (game.lenses_time_left > 0)
5864 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
5866 TEST_DrawLevelField(x, y);
5868 /* uncrumble neighbour fields, if needed */
5869 if (element == EL_INVISIBLE_SAND)
5870 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
5872 else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
5873 element == EL_INVISIBLE_WALL_ACTIVE ||
5874 element == EL_INVISIBLE_SAND_ACTIVE)
5876 if (game.lenses_time_left == 0)
5877 Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
5879 TEST_DrawLevelField(x, y);
5881 /* re-crumble neighbour fields, if needed */
5882 if (element == EL_INVISIBLE_SAND)
5883 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
5888 static void RedrawAllInvisibleElementsForMagnifier()
5892 SCAN_PLAYFIELD(x, y)
5894 int element = Feld[x][y];
5896 if (element == EL_EMC_FAKE_GRASS &&
5897 game.magnify_time_left > 0)
5899 Feld[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
5900 TEST_DrawLevelField(x, y);
5902 else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
5903 game.magnify_time_left == 0)
5905 Feld[x][y] = EL_EMC_FAKE_GRASS;
5906 TEST_DrawLevelField(x, y);
5908 else if (IS_GATE_GRAY(element) &&
5909 game.magnify_time_left > 0)
5911 Feld[x][y] = (IS_RND_GATE_GRAY(element) ?
5912 element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
5913 IS_EM_GATE_GRAY(element) ?
5914 element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
5915 IS_EMC_GATE_GRAY(element) ?
5916 element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
5917 IS_DC_GATE_GRAY(element) ?
5918 EL_DC_GATE_WHITE_GRAY_ACTIVE :
5920 TEST_DrawLevelField(x, y);
5922 else if (IS_GATE_GRAY_ACTIVE(element) &&
5923 game.magnify_time_left == 0)
5925 Feld[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
5926 element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
5927 IS_EM_GATE_GRAY_ACTIVE(element) ?
5928 element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
5929 IS_EMC_GATE_GRAY_ACTIVE(element) ?
5930 element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
5931 IS_DC_GATE_GRAY_ACTIVE(element) ?
5932 EL_DC_GATE_WHITE_GRAY :
5934 TEST_DrawLevelField(x, y);
5939 static void ToggleLightSwitch(int x, int y)
5941 int element = Feld[x][y];
5943 game.light_time_left =
5944 (element == EL_LIGHT_SWITCH ?
5945 level.time_light * FRAMES_PER_SECOND : 0);
5947 RedrawAllLightSwitchesAndInvisibleElements();
5950 static void ActivateTimegateSwitch(int x, int y)
5954 game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
5956 SCAN_PLAYFIELD(xx, yy)
5958 int element = Feld[xx][yy];
5960 if (element == EL_TIMEGATE_CLOSED ||
5961 element == EL_TIMEGATE_CLOSING)
5963 Feld[xx][yy] = EL_TIMEGATE_OPENING;
5964 PlayLevelSound(xx, yy, SND_CLASS_TIMEGATE_OPENING);
5968 else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
5970 Feld[xx][yy] = EL_TIMEGATE_SWITCH;
5971 TEST_DrawLevelField(xx, yy);
5977 Feld[x][y] = (Feld[x][y] == EL_TIMEGATE_SWITCH ? EL_TIMEGATE_SWITCH_ACTIVE :
5978 EL_DC_TIMEGATE_SWITCH_ACTIVE);
5981 void Impact(int x, int y)
5983 boolean last_line = (y == lev_fieldy - 1);
5984 boolean object_hit = FALSE;
5985 boolean impact = (last_line || object_hit);
5986 int element = Feld[x][y];
5987 int smashed = EL_STEELWALL;
5989 if (!last_line) /* check if element below was hit */
5991 if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
5994 object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
5995 MovDir[x][y + 1] != MV_DOWN ||
5996 MovPos[x][y + 1] <= TILEY / 2));
5998 /* do not smash moving elements that left the smashed field in time */
5999 if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
6000 ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
6003 #if USE_QUICKSAND_IMPACT_BUGFIX
6004 if (Feld[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
6006 RemoveMovingField(x, y + 1);
6007 Feld[x][y + 1] = EL_QUICKSAND_EMPTY;
6008 Feld[x][y + 2] = EL_ROCK;
6009 TEST_DrawLevelField(x, y + 2);
6014 if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTYING && object_hit == FALSE)
6016 RemoveMovingField(x, y + 1);
6017 Feld[x][y + 1] = EL_QUICKSAND_FAST_EMPTY;
6018 Feld[x][y + 2] = EL_ROCK;
6019 TEST_DrawLevelField(x, y + 2);
6026 smashed = MovingOrBlocked2Element(x, y + 1);
6028 impact = (last_line || object_hit);
6031 if (!last_line && smashed == EL_ACID) /* element falls into acid */
6033 SplashAcid(x, y + 1);
6037 /* !!! not sufficient for all cases -- see EL_PEARL below !!! */
6038 /* only reset graphic animation if graphic really changes after impact */
6040 el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
6042 ResetGfxAnimation(x, y);
6043 TEST_DrawLevelField(x, y);
6046 if (impact && CAN_EXPLODE_IMPACT(element))
6051 else if (impact && element == EL_PEARL &&
6052 smashed != EL_DC_MAGIC_WALL && smashed != EL_DC_MAGIC_WALL_ACTIVE)
6054 ResetGfxAnimation(x, y);
6056 Feld[x][y] = EL_PEARL_BREAKING;
6057 PlayLevelSound(x, y, SND_PEARL_BREAKING);
6060 else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
6062 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6067 if (impact && element == EL_AMOEBA_DROP)
6069 if (object_hit && IS_PLAYER(x, y + 1))
6070 KillPlayerUnlessEnemyProtected(x, y + 1);
6071 else if (object_hit && smashed == EL_PENGUIN)
6075 Feld[x][y] = EL_AMOEBA_GROWING;
6076 Store[x][y] = EL_AMOEBA_WET;
6078 ResetRandomAnimationValue(x, y);
6083 if (object_hit) /* check which object was hit */
6085 if ((CAN_PASS_MAGIC_WALL(element) &&
6086 (smashed == EL_MAGIC_WALL ||
6087 smashed == EL_BD_MAGIC_WALL)) ||
6088 (CAN_PASS_DC_MAGIC_WALL(element) &&
6089 smashed == EL_DC_MAGIC_WALL))
6092 int activated_magic_wall =
6093 (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
6094 smashed == EL_BD_MAGIC_WALL ? EL_BD_MAGIC_WALL_ACTIVE :
6095 EL_DC_MAGIC_WALL_ACTIVE);
6097 /* activate magic wall / mill */
6098 SCAN_PLAYFIELD(xx, yy)
6100 if (Feld[xx][yy] == smashed)
6101 Feld[xx][yy] = activated_magic_wall;
6104 game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
6105 game.magic_wall_active = TRUE;
6107 PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
6108 SND_MAGIC_WALL_ACTIVATING :
6109 smashed == EL_BD_MAGIC_WALL ?
6110 SND_BD_MAGIC_WALL_ACTIVATING :
6111 SND_DC_MAGIC_WALL_ACTIVATING));
6114 if (IS_PLAYER(x, y + 1))
6116 if (CAN_SMASH_PLAYER(element))
6118 KillPlayerUnlessEnemyProtected(x, y + 1);
6122 else if (smashed == EL_PENGUIN)
6124 if (CAN_SMASH_PLAYER(element))
6130 else if (element == EL_BD_DIAMOND)
6132 if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
6138 else if (((element == EL_SP_INFOTRON ||
6139 element == EL_SP_ZONK) &&
6140 (smashed == EL_SP_SNIKSNAK ||
6141 smashed == EL_SP_ELECTRON ||
6142 smashed == EL_SP_DISK_ORANGE)) ||
6143 (element == EL_SP_INFOTRON &&
6144 smashed == EL_SP_DISK_YELLOW))
6149 else if (CAN_SMASH_EVERYTHING(element))
6151 if (IS_CLASSIC_ENEMY(smashed) ||
6152 CAN_EXPLODE_SMASHED(smashed))
6157 else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
6159 if (smashed == EL_LAMP ||
6160 smashed == EL_LAMP_ACTIVE)
6165 else if (smashed == EL_NUT)
6167 Feld[x][y + 1] = EL_NUT_BREAKING;
6168 PlayLevelSound(x, y, SND_NUT_BREAKING);
6169 RaiseScoreElement(EL_NUT);
6172 else if (smashed == EL_PEARL)
6174 ResetGfxAnimation(x, y);
6176 Feld[x][y + 1] = EL_PEARL_BREAKING;
6177 PlayLevelSound(x, y, SND_PEARL_BREAKING);
6180 else if (smashed == EL_DIAMOND)
6182 Feld[x][y + 1] = EL_DIAMOND_BREAKING;
6183 PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
6186 else if (IS_BELT_SWITCH(smashed))
6188 ToggleBeltSwitch(x, y + 1);
6190 else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
6191 smashed == EL_SWITCHGATE_SWITCH_DOWN ||
6192 smashed == EL_DC_SWITCHGATE_SWITCH_UP ||
6193 smashed == EL_DC_SWITCHGATE_SWITCH_DOWN)
6195 ToggleSwitchgateSwitch(x, y + 1);
6197 else if (smashed == EL_LIGHT_SWITCH ||
6198 smashed == EL_LIGHT_SWITCH_ACTIVE)
6200 ToggleLightSwitch(x, y + 1);
6204 CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6206 CheckElementChangeBySide(x, y + 1, smashed, element,
6207 CE_SWITCHED, CH_SIDE_TOP);
6208 CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
6214 CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6219 /* play sound of magic wall / mill */
6221 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
6222 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ||
6223 Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE))
6225 if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
6226 PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
6227 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
6228 PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
6229 else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
6230 PlayLevelSound(x, y, SND_DC_MAGIC_WALL_FILLING);
6235 /* play sound of object that hits the ground */
6236 if (last_line || object_hit)
6237 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6240 inline static void TurnRoundExt(int x, int y)
6252 { 0, 0 }, { 0, 0 }, { 0, 0 },
6257 int left, right, back;
6261 { MV_DOWN, MV_UP, MV_RIGHT },
6262 { MV_UP, MV_DOWN, MV_LEFT },
6264 { MV_LEFT, MV_RIGHT, MV_DOWN },
6268 { MV_RIGHT, MV_LEFT, MV_UP }
6271 int element = Feld[x][y];
6272 int move_pattern = element_info[element].move_pattern;
6274 int old_move_dir = MovDir[x][y];
6275 int left_dir = turn[old_move_dir].left;
6276 int right_dir = turn[old_move_dir].right;
6277 int back_dir = turn[old_move_dir].back;
6279 int left_dx = move_xy[left_dir].dx, left_dy = move_xy[left_dir].dy;
6280 int right_dx = move_xy[right_dir].dx, right_dy = move_xy[right_dir].dy;
6281 int move_dx = move_xy[old_move_dir].dx, move_dy = move_xy[old_move_dir].dy;
6282 int back_dx = move_xy[back_dir].dx, back_dy = move_xy[back_dir].dy;
6284 int left_x = x + left_dx, left_y = y + left_dy;
6285 int right_x = x + right_dx, right_y = y + right_dy;
6286 int move_x = x + move_dx, move_y = y + move_dy;
6290 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
6292 TestIfBadThingTouchesOtherBadThing(x, y);
6294 if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
6295 MovDir[x][y] = right_dir;
6296 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6297 MovDir[x][y] = left_dir;
6299 if (element == EL_BUG && MovDir[x][y] != old_move_dir)
6301 else if (element == EL_BD_BUTTERFLY) /* && MovDir[x][y] == left_dir) */
6304 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
6306 TestIfBadThingTouchesOtherBadThing(x, y);
6308 if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
6309 MovDir[x][y] = left_dir;
6310 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6311 MovDir[x][y] = right_dir;
6313 if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
6315 else if (element == EL_BD_FIREFLY) /* && MovDir[x][y] == right_dir) */
6318 else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
6320 TestIfBadThingTouchesOtherBadThing(x, y);
6322 if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
6323 MovDir[x][y] = left_dir;
6324 else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
6325 MovDir[x][y] = right_dir;
6327 if (MovDir[x][y] != old_move_dir)
6330 else if (element == EL_YAMYAM)
6332 boolean can_turn_left = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
6333 boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
6335 if (can_turn_left && can_turn_right)
6336 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6337 else if (can_turn_left)
6338 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6339 else if (can_turn_right)
6340 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6342 MovDir[x][y] = back_dir;
6344 MovDelay[x][y] = 16 + 16 * RND(3);
6346 else if (element == EL_DARK_YAMYAM)
6348 boolean can_turn_left = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6350 boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6353 if (can_turn_left && can_turn_right)
6354 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6355 else if (can_turn_left)
6356 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6357 else if (can_turn_right)
6358 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6360 MovDir[x][y] = back_dir;
6362 MovDelay[x][y] = 16 + 16 * RND(3);
6364 else if (element == EL_PACMAN)
6366 boolean can_turn_left = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
6367 boolean can_turn_right = PACMAN_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] = 6 + RND(40);
6380 else if (element == EL_PIG)
6382 boolean can_turn_left = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
6383 boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
6384 boolean can_move_on = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
6385 boolean should_turn_left, should_turn_right, should_move_on;
6387 int rnd = RND(rnd_value);
6389 should_turn_left = (can_turn_left &&
6391 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
6392 y + back_dy + left_dy)));
6393 should_turn_right = (can_turn_right &&
6395 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
6396 y + back_dy + right_dy)));
6397 should_move_on = (can_move_on &&
6400 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
6401 y + move_dy + left_dy) ||
6402 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
6403 y + move_dy + right_dy)));
6405 if (should_turn_left || should_turn_right || should_move_on)
6407 if (should_turn_left && should_turn_right && should_move_on)
6408 MovDir[x][y] = (rnd < rnd_value / 3 ? left_dir :
6409 rnd < 2 * rnd_value / 3 ? right_dir :
6411 else if (should_turn_left && should_turn_right)
6412 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6413 else if (should_turn_left && should_move_on)
6414 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
6415 else if (should_turn_right && should_move_on)
6416 MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
6417 else if (should_turn_left)
6418 MovDir[x][y] = left_dir;
6419 else if (should_turn_right)
6420 MovDir[x][y] = right_dir;
6421 else if (should_move_on)
6422 MovDir[x][y] = old_move_dir;
6424 else if (can_move_on && rnd > rnd_value / 8)
6425 MovDir[x][y] = old_move_dir;
6426 else if (can_turn_left && can_turn_right)
6427 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6428 else if (can_turn_left && rnd > rnd_value / 8)
6429 MovDir[x][y] = left_dir;
6430 else if (can_turn_right && rnd > rnd_value/8)
6431 MovDir[x][y] = right_dir;
6433 MovDir[x][y] = back_dir;
6435 xx = x + move_xy[MovDir[x][y]].dx;
6436 yy = y + move_xy[MovDir[x][y]].dy;
6438 if (!IN_LEV_FIELD(xx, yy) ||
6439 (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy])))
6440 MovDir[x][y] = old_move_dir;
6444 else if (element == EL_DRAGON)
6446 boolean can_turn_left = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
6447 boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
6448 boolean can_move_on = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
6450 int rnd = RND(rnd_value);
6452 if (can_move_on && rnd > rnd_value / 8)
6453 MovDir[x][y] = old_move_dir;
6454 else if (can_turn_left && can_turn_right)
6455 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6456 else if (can_turn_left && rnd > rnd_value / 8)
6457 MovDir[x][y] = left_dir;
6458 else if (can_turn_right && rnd > rnd_value / 8)
6459 MovDir[x][y] = right_dir;
6461 MovDir[x][y] = back_dir;
6463 xx = x + move_xy[MovDir[x][y]].dx;
6464 yy = y + move_xy[MovDir[x][y]].dy;
6466 if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
6467 MovDir[x][y] = old_move_dir;
6471 else if (element == EL_MOLE)
6473 boolean can_move_on =
6474 (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
6475 IS_AMOEBOID(Feld[move_x][move_y]) ||
6476 Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
6479 boolean can_turn_left =
6480 (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
6481 IS_AMOEBOID(Feld[left_x][left_y])));
6483 boolean can_turn_right =
6484 (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
6485 IS_AMOEBOID(Feld[right_x][right_y])));
6487 if (can_turn_left && can_turn_right)
6488 MovDir[x][y] = (RND(2) ? left_dir : right_dir);
6489 else if (can_turn_left)
6490 MovDir[x][y] = left_dir;
6492 MovDir[x][y] = right_dir;
6495 if (MovDir[x][y] != old_move_dir)
6498 else if (element == EL_BALLOON)
6500 MovDir[x][y] = game.wind_direction;
6503 else if (element == EL_SPRING)
6505 if (MovDir[x][y] & MV_HORIZONTAL)
6507 if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
6508 !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
6510 Feld[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
6511 ResetGfxAnimation(move_x, move_y);
6512 TEST_DrawLevelField(move_x, move_y);
6514 MovDir[x][y] = back_dir;
6516 else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
6517 SPRING_CAN_ENTER_FIELD(element, x, y + 1))
6518 MovDir[x][y] = MV_NONE;
6523 else if (element == EL_ROBOT ||
6524 element == EL_SATELLITE ||
6525 element == EL_PENGUIN ||
6526 element == EL_EMC_ANDROID)
6528 int attr_x = -1, attr_y = -1;
6539 for (i = 0; i < MAX_PLAYERS; i++)
6541 struct PlayerInfo *player = &stored_player[i];
6542 int jx = player->jx, jy = player->jy;
6544 if (!player->active)
6548 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
6556 if (element == EL_ROBOT && ZX >= 0 && ZY >= 0 &&
6557 (Feld[ZX][ZY] == EL_ROBOT_WHEEL_ACTIVE ||
6558 game.engine_version < VERSION_IDENT(3,1,0,0)))
6564 if (element == EL_PENGUIN)
6567 static int xy[4][2] =
6575 for (i = 0; i < NUM_DIRECTIONS; i++)
6577 int ex = x + xy[i][0];
6578 int ey = y + xy[i][1];
6580 if (IN_LEV_FIELD(ex, ey) && (Feld[ex][ey] == EL_EXIT_OPEN ||
6581 Feld[ex][ey] == EL_EM_EXIT_OPEN ||
6582 Feld[ex][ey] == EL_STEEL_EXIT_OPEN ||
6583 Feld[ex][ey] == EL_EM_STEEL_EXIT_OPEN))
6592 MovDir[x][y] = MV_NONE;
6594 MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
6595 else if (attr_x > x)
6596 MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
6598 MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
6599 else if (attr_y > y)
6600 MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
6602 if (element == EL_ROBOT)
6606 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6607 MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
6608 Moving2Blocked(x, y, &newx, &newy);
6610 if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
6611 MovDelay[x][y] = 8 + 8 * !RND(3);
6613 MovDelay[x][y] = 16;
6615 else if (element == EL_PENGUIN)
6621 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6623 boolean first_horiz = RND(2);
6624 int new_move_dir = MovDir[x][y];
6627 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6628 Moving2Blocked(x, y, &newx, &newy);
6630 if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
6634 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6635 Moving2Blocked(x, y, &newx, &newy);
6637 if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
6640 MovDir[x][y] = old_move_dir;
6644 else if (element == EL_SATELLITE)
6650 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6652 boolean first_horiz = RND(2);
6653 int new_move_dir = MovDir[x][y];
6656 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6657 Moving2Blocked(x, y, &newx, &newy);
6659 if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
6663 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6664 Moving2Blocked(x, y, &newx, &newy);
6666 if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
6669 MovDir[x][y] = old_move_dir;
6673 else if (element == EL_EMC_ANDROID)
6675 static int check_pos[16] =
6677 -1, /* 0 => (invalid) */
6678 7, /* 1 => MV_LEFT */
6679 3, /* 2 => MV_RIGHT */
6680 -1, /* 3 => (invalid) */
6682 0, /* 5 => MV_LEFT | MV_UP */
6683 2, /* 6 => MV_RIGHT | MV_UP */
6684 -1, /* 7 => (invalid) */
6685 5, /* 8 => MV_DOWN */
6686 6, /* 9 => MV_LEFT | MV_DOWN */
6687 4, /* 10 => MV_RIGHT | MV_DOWN */
6688 -1, /* 11 => (invalid) */
6689 -1, /* 12 => (invalid) */
6690 -1, /* 13 => (invalid) */
6691 -1, /* 14 => (invalid) */
6692 -1, /* 15 => (invalid) */
6700 { -1, -1, MV_LEFT | MV_UP },
6702 { +1, -1, MV_RIGHT | MV_UP },
6703 { +1, 0, MV_RIGHT },
6704 { +1, +1, MV_RIGHT | MV_DOWN },
6706 { -1, +1, MV_LEFT | MV_DOWN },
6709 int start_pos, check_order;
6710 boolean can_clone = FALSE;
6713 /* check if there is any free field around current position */
6714 for (i = 0; i < 8; i++)
6716 int newx = x + check_xy[i].dx;
6717 int newy = y + check_xy[i].dy;
6719 if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
6727 if (can_clone) /* randomly find an element to clone */
6731 start_pos = check_pos[RND(8)];
6732 check_order = (RND(2) ? -1 : +1);
6734 for (i = 0; i < 8; i++)
6736 int pos_raw = start_pos + i * check_order;
6737 int pos = (pos_raw + 8) % 8;
6738 int newx = x + check_xy[pos].dx;
6739 int newy = y + check_xy[pos].dy;
6741 if (ANDROID_CAN_CLONE_FIELD(newx, newy))
6743 element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
6744 element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
6746 Store[x][y] = Feld[newx][newy];
6755 if (can_clone) /* randomly find a direction to move */
6759 start_pos = check_pos[RND(8)];
6760 check_order = (RND(2) ? -1 : +1);
6762 for (i = 0; i < 8; i++)
6764 int pos_raw = start_pos + i * check_order;
6765 int pos = (pos_raw + 8) % 8;
6766 int newx = x + check_xy[pos].dx;
6767 int newy = y + check_xy[pos].dy;
6768 int new_move_dir = check_xy[pos].dir;
6770 if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
6772 MovDir[x][y] = new_move_dir;
6773 MovDelay[x][y] = level.android_clone_time * 8 + 1;
6782 if (can_clone) /* cloning and moving successful */
6785 /* cannot clone -- try to move towards player */
6787 start_pos = check_pos[MovDir[x][y] & 0x0f];
6788 check_order = (RND(2) ? -1 : +1);
6790 for (i = 0; i < 3; i++)
6792 /* first check start_pos, then previous/next or (next/previous) pos */
6793 int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
6794 int pos = (pos_raw + 8) % 8;
6795 int newx = x + check_xy[pos].dx;
6796 int newy = y + check_xy[pos].dy;
6797 int new_move_dir = check_xy[pos].dir;
6799 if (IS_PLAYER(newx, newy))
6802 if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
6804 MovDir[x][y] = new_move_dir;
6805 MovDelay[x][y] = level.android_move_time * 8 + 1;
6812 else if (move_pattern == MV_TURNING_LEFT ||
6813 move_pattern == MV_TURNING_RIGHT ||
6814 move_pattern == MV_TURNING_LEFT_RIGHT ||
6815 move_pattern == MV_TURNING_RIGHT_LEFT ||
6816 move_pattern == MV_TURNING_RANDOM ||
6817 move_pattern == MV_ALL_DIRECTIONS)
6819 boolean can_turn_left =
6820 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
6821 boolean can_turn_right =
6822 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
6824 if (element_info[element].move_stepsize == 0) /* "not moving" */
6827 if (move_pattern == MV_TURNING_LEFT)
6828 MovDir[x][y] = left_dir;
6829 else if (move_pattern == MV_TURNING_RIGHT)
6830 MovDir[x][y] = right_dir;
6831 else if (move_pattern == MV_TURNING_LEFT_RIGHT)
6832 MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
6833 else if (move_pattern == MV_TURNING_RIGHT_LEFT)
6834 MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
6835 else if (move_pattern == MV_TURNING_RANDOM)
6836 MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
6837 can_turn_right && !can_turn_left ? right_dir :
6838 RND(2) ? left_dir : right_dir);
6839 else if (can_turn_left && can_turn_right)
6840 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6841 else if (can_turn_left)
6842 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6843 else if (can_turn_right)
6844 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6846 MovDir[x][y] = back_dir;
6848 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6850 else if (move_pattern == MV_HORIZONTAL ||
6851 move_pattern == MV_VERTICAL)
6853 if (move_pattern & old_move_dir)
6854 MovDir[x][y] = back_dir;
6855 else if (move_pattern == MV_HORIZONTAL)
6856 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
6857 else if (move_pattern == MV_VERTICAL)
6858 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
6860 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6862 else if (move_pattern & MV_ANY_DIRECTION)
6864 MovDir[x][y] = move_pattern;
6865 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6867 else if (move_pattern & MV_WIND_DIRECTION)
6869 MovDir[x][y] = game.wind_direction;
6870 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6872 else if (move_pattern == MV_ALONG_LEFT_SIDE)
6874 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
6875 MovDir[x][y] = left_dir;
6876 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
6877 MovDir[x][y] = right_dir;
6879 if (MovDir[x][y] != old_move_dir)
6880 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6882 else if (move_pattern == MV_ALONG_RIGHT_SIDE)
6884 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
6885 MovDir[x][y] = right_dir;
6886 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
6887 MovDir[x][y] = left_dir;
6889 if (MovDir[x][y] != old_move_dir)
6890 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6892 else if (move_pattern == MV_TOWARDS_PLAYER ||
6893 move_pattern == MV_AWAY_FROM_PLAYER)
6895 int attr_x = -1, attr_y = -1;
6897 boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
6908 for (i = 0; i < MAX_PLAYERS; i++)
6910 struct PlayerInfo *player = &stored_player[i];
6911 int jx = player->jx, jy = player->jy;
6913 if (!player->active)
6917 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
6925 MovDir[x][y] = MV_NONE;
6927 MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
6928 else if (attr_x > x)
6929 MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
6931 MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
6932 else if (attr_y > y)
6933 MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
6935 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6937 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6939 boolean first_horiz = RND(2);
6940 int new_move_dir = MovDir[x][y];
6942 if (element_info[element].move_stepsize == 0) /* "not moving" */
6944 first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
6945 MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6951 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6952 Moving2Blocked(x, y, &newx, &newy);
6954 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
6958 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6959 Moving2Blocked(x, y, &newx, &newy);
6961 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
6964 MovDir[x][y] = old_move_dir;
6967 else if (move_pattern == MV_WHEN_PUSHED ||
6968 move_pattern == MV_WHEN_DROPPED)
6970 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
6971 MovDir[x][y] = MV_NONE;
6975 else if (move_pattern & MV_MAZE_RUNNER_STYLE)
6977 static int test_xy[7][2] =
6987 static int test_dir[7] =
6997 boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
6998 int move_preference = -1000000; /* start with very low preference */
6999 int new_move_dir = MV_NONE;
7000 int start_test = RND(4);
7003 for (i = 0; i < NUM_DIRECTIONS; i++)
7005 int move_dir = test_dir[start_test + i];
7006 int move_dir_preference;
7008 xx = x + test_xy[start_test + i][0];
7009 yy = y + test_xy[start_test + i][1];
7011 if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
7012 (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
7014 new_move_dir = move_dir;
7019 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
7022 move_dir_preference = -1 * RunnerVisit[xx][yy];
7023 if (hunter_mode && PlayerVisit[xx][yy] > 0)
7024 move_dir_preference = PlayerVisit[xx][yy];
7026 if (move_dir_preference > move_preference)
7028 /* prefer field that has not been visited for the longest time */
7029 move_preference = move_dir_preference;
7030 new_move_dir = move_dir;
7032 else if (move_dir_preference == move_preference &&
7033 move_dir == old_move_dir)
7035 /* prefer last direction when all directions are preferred equally */
7036 move_preference = move_dir_preference;
7037 new_move_dir = move_dir;
7041 MovDir[x][y] = new_move_dir;
7042 if (old_move_dir != new_move_dir)
7043 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7047 static void TurnRound(int x, int y)
7049 int direction = MovDir[x][y];
7053 GfxDir[x][y] = MovDir[x][y];
7055 if (direction != MovDir[x][y])
7059 GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
7061 ResetGfxFrame(x, y);
7064 static boolean JustBeingPushed(int x, int y)
7068 for (i = 0; i < MAX_PLAYERS; i++)
7070 struct PlayerInfo *player = &stored_player[i];
7072 if (player->active && player->is_pushing && player->MovPos)
7074 int next_jx = player->jx + (player->jx - player->last_jx);
7075 int next_jy = player->jy + (player->jy - player->last_jy);
7077 if (x == next_jx && y == next_jy)
7085 void StartMoving(int x, int y)
7087 boolean started_moving = FALSE; /* some elements can fall _and_ move */
7088 int element = Feld[x][y];
7093 if (MovDelay[x][y] == 0)
7094 GfxAction[x][y] = ACTION_DEFAULT;
7096 if (CAN_FALL(element) && y < lev_fieldy - 1)
7098 if ((x > 0 && IS_PLAYER(x - 1, y)) ||
7099 (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
7100 if (JustBeingPushed(x, y))
7103 if (element == EL_QUICKSAND_FULL)
7105 if (IS_FREE(x, y + 1))
7107 InitMovingField(x, y, MV_DOWN);
7108 started_moving = TRUE;
7110 Feld[x][y] = EL_QUICKSAND_EMPTYING;
7111 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7112 if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7113 Store[x][y] = EL_ROCK;
7115 Store[x][y] = EL_ROCK;
7118 PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7120 else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7122 if (!MovDelay[x][y])
7124 MovDelay[x][y] = TILEY + 1;
7126 ResetGfxAnimation(x, y);
7127 ResetGfxAnimation(x, y + 1);
7132 DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7133 DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7140 Feld[x][y] = EL_QUICKSAND_EMPTY;
7141 Feld[x][y + 1] = EL_QUICKSAND_FULL;
7142 Store[x][y + 1] = Store[x][y];
7145 PlayLevelSoundAction(x, y, ACTION_FILLING);
7147 else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7149 if (!MovDelay[x][y])
7151 MovDelay[x][y] = TILEY + 1;
7153 ResetGfxAnimation(x, y);
7154 ResetGfxAnimation(x, y + 1);
7159 DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7160 DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7167 Feld[x][y] = EL_QUICKSAND_EMPTY;
7168 Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7169 Store[x][y + 1] = Store[x][y];
7172 PlayLevelSoundAction(x, y, ACTION_FILLING);
7175 else if (element == EL_QUICKSAND_FAST_FULL)
7177 if (IS_FREE(x, y + 1))
7179 InitMovingField(x, y, MV_DOWN);
7180 started_moving = TRUE;
7182 Feld[x][y] = EL_QUICKSAND_FAST_EMPTYING;
7183 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7184 if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7185 Store[x][y] = EL_ROCK;
7187 Store[x][y] = EL_ROCK;
7190 PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7192 else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7194 if (!MovDelay[x][y])
7196 MovDelay[x][y] = TILEY + 1;
7198 ResetGfxAnimation(x, y);
7199 ResetGfxAnimation(x, y + 1);
7204 DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7205 DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7212 Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
7213 Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7214 Store[x][y + 1] = Store[x][y];
7217 PlayLevelSoundAction(x, y, ACTION_FILLING);
7219 else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7221 if (!MovDelay[x][y])
7223 MovDelay[x][y] = TILEY + 1;
7225 ResetGfxAnimation(x, y);
7226 ResetGfxAnimation(x, y + 1);
7231 DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7232 DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7239 Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
7240 Feld[x][y + 1] = EL_QUICKSAND_FULL;
7241 Store[x][y + 1] = Store[x][y];
7244 PlayLevelSoundAction(x, y, ACTION_FILLING);
7247 else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7248 Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7250 InitMovingField(x, y, MV_DOWN);
7251 started_moving = TRUE;
7253 Feld[x][y] = EL_QUICKSAND_FILLING;
7254 Store[x][y] = element;
7256 PlayLevelSoundAction(x, y, ACTION_FILLING);
7258 else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7259 Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7261 InitMovingField(x, y, MV_DOWN);
7262 started_moving = TRUE;
7264 Feld[x][y] = EL_QUICKSAND_FAST_FILLING;
7265 Store[x][y] = element;
7267 PlayLevelSoundAction(x, y, ACTION_FILLING);
7269 else if (element == EL_MAGIC_WALL_FULL)
7271 if (IS_FREE(x, y + 1))
7273 InitMovingField(x, y, MV_DOWN);
7274 started_moving = TRUE;
7276 Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
7277 Store[x][y] = EL_CHANGED(Store[x][y]);
7279 else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
7281 if (!MovDelay[x][y])
7282 MovDelay[x][y] = TILEY / 4 + 1;
7291 Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
7292 Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
7293 Store[x][y + 1] = EL_CHANGED(Store[x][y]);
7297 else if (element == EL_BD_MAGIC_WALL_FULL)
7299 if (IS_FREE(x, y + 1))
7301 InitMovingField(x, y, MV_DOWN);
7302 started_moving = TRUE;
7304 Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
7305 Store[x][y] = EL_CHANGED_BD(Store[x][y]);
7307 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
7309 if (!MovDelay[x][y])
7310 MovDelay[x][y] = TILEY / 4 + 1;
7319 Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
7320 Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
7321 Store[x][y + 1] = EL_CHANGED_BD(Store[x][y]);
7325 else if (element == EL_DC_MAGIC_WALL_FULL)
7327 if (IS_FREE(x, y + 1))
7329 InitMovingField(x, y, MV_DOWN);
7330 started_moving = TRUE;
7332 Feld[x][y] = EL_DC_MAGIC_WALL_EMPTYING;
7333 Store[x][y] = EL_CHANGED_DC(Store[x][y]);
7335 else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
7337 if (!MovDelay[x][y])
7338 MovDelay[x][y] = TILEY / 4 + 1;
7347 Feld[x][y] = EL_DC_MAGIC_WALL_ACTIVE;
7348 Feld[x][y + 1] = EL_DC_MAGIC_WALL_FULL;
7349 Store[x][y + 1] = EL_CHANGED_DC(Store[x][y]);
7353 else if ((CAN_PASS_MAGIC_WALL(element) &&
7354 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
7355 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)) ||
7356 (CAN_PASS_DC_MAGIC_WALL(element) &&
7357 (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)))
7360 InitMovingField(x, y, MV_DOWN);
7361 started_moving = TRUE;
7364 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
7365 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ? EL_BD_MAGIC_WALL_FILLING :
7366 EL_DC_MAGIC_WALL_FILLING);
7367 Store[x][y] = element;
7369 else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
7371 SplashAcid(x, y + 1);
7373 InitMovingField(x, y, MV_DOWN);
7374 started_moving = TRUE;
7376 Store[x][y] = EL_ACID;
7379 (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7380 CheckImpact[x][y] && !IS_FREE(x, y + 1)) ||
7381 (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
7382 CAN_FALL(element) && WasJustFalling[x][y] &&
7383 (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
7385 (game.engine_version < VERSION_IDENT(2,2,0,7) &&
7386 CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
7387 (Feld[x][y + 1] == EL_BLOCKED)))
7389 /* this is needed for a special case not covered by calling "Impact()"
7390 from "ContinueMoving()": if an element moves to a tile directly below
7391 another element which was just falling on that tile (which was empty
7392 in the previous frame), the falling element above would just stop
7393 instead of smashing the element below (in previous version, the above
7394 element was just checked for "moving" instead of "falling", resulting
7395 in incorrect smashes caused by horizontal movement of the above
7396 element; also, the case of the player being the element to smash was
7397 simply not covered here... :-/ ) */
7399 CheckCollision[x][y] = 0;
7400 CheckImpact[x][y] = 0;
7404 else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
7406 if (MovDir[x][y] == MV_NONE)
7408 InitMovingField(x, y, MV_DOWN);
7409 started_moving = TRUE;
7412 else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
7414 if (WasJustFalling[x][y]) /* prevent animation from being restarted */
7415 MovDir[x][y] = MV_DOWN;
7417 InitMovingField(x, y, MV_DOWN);
7418 started_moving = TRUE;
7420 else if (element == EL_AMOEBA_DROP)
7422 Feld[x][y] = EL_AMOEBA_GROWING;
7423 Store[x][y] = EL_AMOEBA_WET;
7425 else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
7426 (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
7427 !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
7428 element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
7430 boolean can_fall_left = (x > 0 && IS_FREE(x - 1, y) &&
7431 (IS_FREE(x - 1, y + 1) ||
7432 Feld[x - 1][y + 1] == EL_ACID));
7433 boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
7434 (IS_FREE(x + 1, y + 1) ||
7435 Feld[x + 1][y + 1] == EL_ACID));
7436 boolean can_fall_any = (can_fall_left || can_fall_right);
7437 boolean can_fall_both = (can_fall_left && can_fall_right);
7438 int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
7440 if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
7442 if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
7443 can_fall_right = FALSE;
7444 else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
7445 can_fall_left = FALSE;
7446 else if (slippery_type == SLIPPERY_ONLY_LEFT)
7447 can_fall_right = FALSE;
7448 else if (slippery_type == SLIPPERY_ONLY_RIGHT)
7449 can_fall_left = FALSE;
7451 can_fall_any = (can_fall_left || can_fall_right);
7452 can_fall_both = FALSE;
7457 if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
7458 can_fall_right = FALSE; /* slip down on left side */
7460 can_fall_left = !(can_fall_right = RND(2));
7462 can_fall_both = FALSE;
7467 /* if not determined otherwise, prefer left side for slipping down */
7468 InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
7469 started_moving = TRUE;
7472 else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
7474 boolean left_is_free = (x > 0 && IS_FREE(x - 1, y));
7475 boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
7476 int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
7477 int belt_dir = game.belt_dir[belt_nr];
7479 if ((belt_dir == MV_LEFT && left_is_free) ||
7480 (belt_dir == MV_RIGHT && right_is_free))
7482 int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
7484 InitMovingField(x, y, belt_dir);
7485 started_moving = TRUE;
7487 Pushed[x][y] = TRUE;
7488 Pushed[nextx][y] = TRUE;
7490 GfxAction[x][y] = ACTION_DEFAULT;
7494 MovDir[x][y] = 0; /* if element was moving, stop it */
7499 /* not "else if" because of elements that can fall and move (EL_SPRING) */
7500 if (CAN_MOVE(element) && !started_moving)
7502 int move_pattern = element_info[element].move_pattern;
7505 Moving2Blocked(x, y, &newx, &newy);
7507 if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
7510 if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7511 CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7513 WasJustMoving[x][y] = 0;
7514 CheckCollision[x][y] = 0;
7516 TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
7518 if (Feld[x][y] != element) /* element has changed */
7522 if (!MovDelay[x][y]) /* start new movement phase */
7524 /* all objects that can change their move direction after each step
7525 (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
7527 if (element != EL_YAMYAM &&
7528 element != EL_DARK_YAMYAM &&
7529 element != EL_PACMAN &&
7530 !(move_pattern & MV_ANY_DIRECTION) &&
7531 move_pattern != MV_TURNING_LEFT &&
7532 move_pattern != MV_TURNING_RIGHT &&
7533 move_pattern != MV_TURNING_LEFT_RIGHT &&
7534 move_pattern != MV_TURNING_RIGHT_LEFT &&
7535 move_pattern != MV_TURNING_RANDOM)
7539 if (MovDelay[x][y] && (element == EL_BUG ||
7540 element == EL_SPACESHIP ||
7541 element == EL_SP_SNIKSNAK ||
7542 element == EL_SP_ELECTRON ||
7543 element == EL_MOLE))
7544 TEST_DrawLevelField(x, y);
7548 if (MovDelay[x][y]) /* wait some time before next movement */
7552 if (element == EL_ROBOT ||
7553 element == EL_YAMYAM ||
7554 element == EL_DARK_YAMYAM)
7556 DrawLevelElementAnimationIfNeeded(x, y, element);
7557 PlayLevelSoundAction(x, y, ACTION_WAITING);
7559 else if (element == EL_SP_ELECTRON)
7560 DrawLevelElementAnimationIfNeeded(x, y, element);
7561 else if (element == EL_DRAGON)
7564 int dir = MovDir[x][y];
7565 int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
7566 int dy = (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
7567 int graphic = (dir == MV_LEFT ? IMG_FLAMES_1_LEFT :
7568 dir == MV_RIGHT ? IMG_FLAMES_1_RIGHT :
7569 dir == MV_UP ? IMG_FLAMES_1_UP :
7570 dir == MV_DOWN ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
7571 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
7573 GfxAction[x][y] = ACTION_ATTACKING;
7575 if (IS_PLAYER(x, y))
7576 DrawPlayerField(x, y);
7578 TEST_DrawLevelField(x, y);
7580 PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
7582 for (i = 1; i <= 3; i++)
7584 int xx = x + i * dx;
7585 int yy = y + i * dy;
7586 int sx = SCREENX(xx);
7587 int sy = SCREENY(yy);
7588 int flame_graphic = graphic + (i - 1);
7590 if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
7595 int flamed = MovingOrBlocked2Element(xx, yy);
7597 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
7600 RemoveMovingField(xx, yy);
7602 ChangeDelay[xx][yy] = 0;
7604 Feld[xx][yy] = EL_FLAMES;
7606 if (IN_SCR_FIELD(sx, sy))
7608 TEST_DrawLevelFieldCrumbled(xx, yy);
7609 DrawGraphic(sx, sy, flame_graphic, frame);
7614 if (Feld[xx][yy] == EL_FLAMES)
7615 Feld[xx][yy] = EL_EMPTY;
7616 TEST_DrawLevelField(xx, yy);
7621 if (MovDelay[x][y]) /* element still has to wait some time */
7623 PlayLevelSoundAction(x, y, ACTION_WAITING);
7629 /* now make next step */
7631 Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
7633 if (DONT_COLLIDE_WITH(element) &&
7634 IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
7635 !PLAYER_ENEMY_PROTECTED(newx, newy))
7637 TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
7642 else if (CAN_MOVE_INTO_ACID(element) &&
7643 IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID &&
7644 !IS_MV_DIAGONAL(MovDir[x][y]) &&
7645 (MovDir[x][y] == MV_DOWN ||
7646 game.engine_version >= VERSION_IDENT(3,1,0,0)))
7648 SplashAcid(newx, newy);
7649 Store[x][y] = EL_ACID;
7651 else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
7653 if (Feld[newx][newy] == EL_EXIT_OPEN ||
7654 Feld[newx][newy] == EL_EM_EXIT_OPEN ||
7655 Feld[newx][newy] == EL_STEEL_EXIT_OPEN ||
7656 Feld[newx][newy] == EL_EM_STEEL_EXIT_OPEN)
7659 TEST_DrawLevelField(x, y);
7661 PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
7662 if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
7663 DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
7665 local_player->friends_still_needed--;
7666 if (!local_player->friends_still_needed &&
7667 !local_player->GameOver && AllPlayersGone)
7668 PlayerWins(local_player);
7672 else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
7674 if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MP_MOVING)
7675 TEST_DrawLevelField(newx, newy);
7677 GfxDir[x][y] = MovDir[x][y] = MV_NONE;
7679 else if (!IS_FREE(newx, newy))
7681 GfxAction[x][y] = ACTION_WAITING;
7683 if (IS_PLAYER(x, y))
7684 DrawPlayerField(x, y);
7686 TEST_DrawLevelField(x, y);
7691 else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
7693 if (IS_FOOD_PIG(Feld[newx][newy]))
7695 if (IS_MOVING(newx, newy))
7696 RemoveMovingField(newx, newy);
7699 Feld[newx][newy] = EL_EMPTY;
7700 TEST_DrawLevelField(newx, newy);
7703 PlayLevelSound(x, y, SND_PIG_DIGGING);
7705 else if (!IS_FREE(newx, newy))
7707 if (IS_PLAYER(x, y))
7708 DrawPlayerField(x, y);
7710 TEST_DrawLevelField(x, y);
7715 else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
7717 if (Store[x][y] != EL_EMPTY)
7719 boolean can_clone = FALSE;
7722 /* check if element to clone is still there */
7723 for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
7725 if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == Store[x][y])
7733 /* cannot clone or target field not free anymore -- do not clone */
7734 if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7735 Store[x][y] = EL_EMPTY;
7738 if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7740 if (IS_MV_DIAGONAL(MovDir[x][y]))
7742 int diagonal_move_dir = MovDir[x][y];
7743 int stored = Store[x][y];
7744 int change_delay = 8;
7747 /* android is moving diagonally */
7749 CreateField(x, y, EL_DIAGONAL_SHRINKING);
7751 Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
7752 GfxElement[x][y] = EL_EMC_ANDROID;
7753 GfxAction[x][y] = ACTION_SHRINKING;
7754 GfxDir[x][y] = diagonal_move_dir;
7755 ChangeDelay[x][y] = change_delay;
7757 graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
7760 DrawLevelGraphicAnimation(x, y, graphic);
7761 PlayLevelSoundAction(x, y, ACTION_SHRINKING);
7763 if (Feld[newx][newy] == EL_ACID)
7765 SplashAcid(newx, newy);
7770 CreateField(newx, newy, EL_DIAGONAL_GROWING);
7772 Store[newx][newy] = EL_EMC_ANDROID;
7773 GfxElement[newx][newy] = EL_EMC_ANDROID;
7774 GfxAction[newx][newy] = ACTION_GROWING;
7775 GfxDir[newx][newy] = diagonal_move_dir;
7776 ChangeDelay[newx][newy] = change_delay;
7778 graphic = el_act_dir2img(GfxElement[newx][newy],
7779 GfxAction[newx][newy], GfxDir[newx][newy]);
7781 DrawLevelGraphicAnimation(newx, newy, graphic);
7782 PlayLevelSoundAction(newx, newy, ACTION_GROWING);
7788 Feld[newx][newy] = EL_EMPTY;
7789 TEST_DrawLevelField(newx, newy);
7791 PlayLevelSoundAction(x, y, ACTION_DIGGING);
7794 else if (!IS_FREE(newx, newy))
7799 else if (IS_CUSTOM_ELEMENT(element) &&
7800 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7802 if (!DigFieldByCE(newx, newy, element))
7805 if (move_pattern & MV_MAZE_RUNNER_STYLE)
7807 RunnerVisit[x][y] = FrameCounter;
7808 PlayerVisit[x][y] /= 8; /* expire player visit path */
7811 else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
7813 if (!IS_FREE(newx, newy))
7815 if (IS_PLAYER(x, y))
7816 DrawPlayerField(x, y);
7818 TEST_DrawLevelField(x, y);
7824 boolean wanna_flame = !RND(10);
7825 int dx = newx - x, dy = newy - y;
7826 int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
7827 int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
7828 int element1 = (IN_LEV_FIELD(newx1, newy1) ?
7829 MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
7830 int element2 = (IN_LEV_FIELD(newx2, newy2) ?
7831 MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
7834 IS_CLASSIC_ENEMY(element1) ||
7835 IS_CLASSIC_ENEMY(element2)) &&
7836 element1 != EL_DRAGON && element2 != EL_DRAGON &&
7837 element1 != EL_FLAMES && element2 != EL_FLAMES)
7839 ResetGfxAnimation(x, y);
7840 GfxAction[x][y] = ACTION_ATTACKING;
7842 if (IS_PLAYER(x, y))
7843 DrawPlayerField(x, y);
7845 TEST_DrawLevelField(x, y);
7847 PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
7849 MovDelay[x][y] = 50;
7851 Feld[newx][newy] = EL_FLAMES;
7852 if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
7853 Feld[newx1][newy1] = EL_FLAMES;
7854 if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
7855 Feld[newx2][newy2] = EL_FLAMES;
7861 else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
7862 Feld[newx][newy] == EL_DIAMOND)
7864 if (IS_MOVING(newx, newy))
7865 RemoveMovingField(newx, newy);
7868 Feld[newx][newy] = EL_EMPTY;
7869 TEST_DrawLevelField(newx, newy);
7872 PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
7874 else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
7875 IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
7877 if (AmoebaNr[newx][newy])
7879 AmoebaCnt2[AmoebaNr[newx][newy]]--;
7880 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
7881 Feld[newx][newy] == EL_BD_AMOEBA)
7882 AmoebaCnt[AmoebaNr[newx][newy]]--;
7885 if (IS_MOVING(newx, newy))
7887 RemoveMovingField(newx, newy);
7891 Feld[newx][newy] = EL_EMPTY;
7892 TEST_DrawLevelField(newx, newy);
7895 PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
7897 else if ((element == EL_PACMAN || element == EL_MOLE)
7898 && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
7900 if (AmoebaNr[newx][newy])
7902 AmoebaCnt2[AmoebaNr[newx][newy]]--;
7903 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
7904 Feld[newx][newy] == EL_BD_AMOEBA)
7905 AmoebaCnt[AmoebaNr[newx][newy]]--;
7908 if (element == EL_MOLE)
7910 Feld[newx][newy] = EL_AMOEBA_SHRINKING;
7911 PlayLevelSound(x, y, SND_MOLE_DIGGING);
7913 ResetGfxAnimation(x, y);
7914 GfxAction[x][y] = ACTION_DIGGING;
7915 TEST_DrawLevelField(x, y);
7917 MovDelay[newx][newy] = 0; /* start amoeba shrinking delay */
7919 return; /* wait for shrinking amoeba */
7921 else /* element == EL_PACMAN */
7923 Feld[newx][newy] = EL_EMPTY;
7924 TEST_DrawLevelField(newx, newy);
7925 PlayLevelSound(x, y, SND_PACMAN_DIGGING);
7928 else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
7929 (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
7930 (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
7932 /* wait for shrinking amoeba to completely disappear */
7935 else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
7937 /* object was running against a wall */
7941 if (GFX_ELEMENT(element) != EL_SAND) /* !!! FIX THIS (crumble) !!! */
7942 DrawLevelElementAnimation(x, y, element);
7944 if (DONT_TOUCH(element))
7945 TestIfBadThingTouchesPlayer(x, y);
7950 InitMovingField(x, y, MovDir[x][y]);
7952 PlayLevelSoundAction(x, y, ACTION_MOVING);
7956 ContinueMoving(x, y);
7959 void ContinueMoving(int x, int y)
7961 int element = Feld[x][y];
7962 struct ElementInfo *ei = &element_info[element];
7963 int direction = MovDir[x][y];
7964 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
7965 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
7966 int newx = x + dx, newy = y + dy;
7967 int stored = Store[x][y];
7968 int stored_new = Store[newx][newy];
7969 boolean pushed_by_player = (Pushed[x][y] && IS_PLAYER(x, y));
7970 boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
7971 boolean last_line = (newy == lev_fieldy - 1);
7973 MovPos[x][y] += getElementMoveStepsize(x, y);
7975 if (pushed_by_player) /* special case: moving object pushed by player */
7976 MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
7978 if (ABS(MovPos[x][y]) < TILEX)
7980 TEST_DrawLevelField(x, y);
7982 return; /* element is still moving */
7985 /* element reached destination field */
7987 Feld[x][y] = EL_EMPTY;
7988 Feld[newx][newy] = element;
7989 MovPos[x][y] = 0; /* force "not moving" for "crumbled sand" */
7991 if (Store[x][y] == EL_ACID) /* element is moving into acid pool */
7993 element = Feld[newx][newy] = EL_ACID;
7995 else if (element == EL_MOLE)
7997 Feld[x][y] = EL_SAND;
7999 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8001 else if (element == EL_QUICKSAND_FILLING)
8003 element = Feld[newx][newy] = get_next_element(element);
8004 Store[newx][newy] = Store[x][y];
8006 else if (element == EL_QUICKSAND_EMPTYING)
8008 Feld[x][y] = get_next_element(element);
8009 element = Feld[newx][newy] = Store[x][y];
8011 else if (element == EL_QUICKSAND_FAST_FILLING)
8013 element = Feld[newx][newy] = get_next_element(element);
8014 Store[newx][newy] = Store[x][y];
8016 else if (element == EL_QUICKSAND_FAST_EMPTYING)
8018 Feld[x][y] = get_next_element(element);
8019 element = Feld[newx][newy] = Store[x][y];
8021 else if (element == EL_MAGIC_WALL_FILLING)
8023 element = Feld[newx][newy] = get_next_element(element);
8024 if (!game.magic_wall_active)
8025 element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
8026 Store[newx][newy] = Store[x][y];
8028 else if (element == EL_MAGIC_WALL_EMPTYING)
8030 Feld[x][y] = get_next_element(element);
8031 if (!game.magic_wall_active)
8032 Feld[x][y] = EL_MAGIC_WALL_DEAD;
8033 element = Feld[newx][newy] = Store[x][y];
8035 InitField(newx, newy, FALSE);
8037 else if (element == EL_BD_MAGIC_WALL_FILLING)
8039 element = Feld[newx][newy] = get_next_element(element);
8040 if (!game.magic_wall_active)
8041 element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
8042 Store[newx][newy] = Store[x][y];
8044 else if (element == EL_BD_MAGIC_WALL_EMPTYING)
8046 Feld[x][y] = get_next_element(element);
8047 if (!game.magic_wall_active)
8048 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
8049 element = Feld[newx][newy] = Store[x][y];
8051 InitField(newx, newy, FALSE);
8053 else if (element == EL_DC_MAGIC_WALL_FILLING)
8055 element = Feld[newx][newy] = get_next_element(element);
8056 if (!game.magic_wall_active)
8057 element = Feld[newx][newy] = EL_DC_MAGIC_WALL_DEAD;
8058 Store[newx][newy] = Store[x][y];
8060 else if (element == EL_DC_MAGIC_WALL_EMPTYING)
8062 Feld[x][y] = get_next_element(element);
8063 if (!game.magic_wall_active)
8064 Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
8065 element = Feld[newx][newy] = Store[x][y];
8067 InitField(newx, newy, FALSE);
8069 else if (element == EL_AMOEBA_DROPPING)
8071 Feld[x][y] = get_next_element(element);
8072 element = Feld[newx][newy] = Store[x][y];
8074 else if (element == EL_SOKOBAN_OBJECT)
8077 Feld[x][y] = Back[x][y];
8079 if (Back[newx][newy])
8080 Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
8082 Back[x][y] = Back[newx][newy] = 0;
8085 Store[x][y] = EL_EMPTY;
8090 MovDelay[newx][newy] = 0;
8092 if (CAN_CHANGE_OR_HAS_ACTION(element))
8094 /* copy element change control values to new field */
8095 ChangeDelay[newx][newy] = ChangeDelay[x][y];
8096 ChangePage[newx][newy] = ChangePage[x][y];
8097 ChangeCount[newx][newy] = ChangeCount[x][y];
8098 ChangeEvent[newx][newy] = ChangeEvent[x][y];
8101 CustomValue[newx][newy] = CustomValue[x][y];
8103 ChangeDelay[x][y] = 0;
8104 ChangePage[x][y] = -1;
8105 ChangeCount[x][y] = 0;
8106 ChangeEvent[x][y] = -1;
8108 CustomValue[x][y] = 0;
8110 /* copy animation control values to new field */
8111 GfxFrame[newx][newy] = GfxFrame[x][y];
8112 GfxRandom[newx][newy] = GfxRandom[x][y]; /* keep same random value */
8113 GfxAction[newx][newy] = GfxAction[x][y]; /* keep action one frame */
8114 GfxDir[newx][newy] = GfxDir[x][y]; /* keep element direction */
8116 Pushed[x][y] = Pushed[newx][newy] = FALSE;
8118 /* some elements can leave other elements behind after moving */
8119 if (ei->move_leave_element != EL_EMPTY &&
8120 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
8121 (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
8123 int move_leave_element = ei->move_leave_element;
8125 /* this makes it possible to leave the removed element again */
8126 if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
8127 move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
8129 Feld[x][y] = move_leave_element;
8131 if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
8132 MovDir[x][y] = direction;
8134 InitField(x, y, FALSE);
8136 if (GFX_CRUMBLED(Feld[x][y]))
8137 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8139 if (ELEM_IS_PLAYER(move_leave_element))
8140 RelocatePlayer(x, y, move_leave_element);
8143 /* do this after checking for left-behind element */
8144 ResetGfxAnimation(x, y); /* reset animation values for old field */
8146 if (!CAN_MOVE(element) ||
8147 (CAN_FALL(element) && direction == MV_DOWN &&
8148 (element == EL_SPRING ||
8149 element_info[element].move_pattern == MV_WHEN_PUSHED ||
8150 element_info[element].move_pattern == MV_WHEN_DROPPED)))
8151 GfxDir[x][y] = MovDir[newx][newy] = 0;
8153 TEST_DrawLevelField(x, y);
8154 TEST_DrawLevelField(newx, newy);
8156 Stop[newx][newy] = TRUE; /* ignore this element until the next frame */
8158 /* prevent pushed element from moving on in pushed direction */
8159 if (pushed_by_player && CAN_MOVE(element) &&
8160 element_info[element].move_pattern & MV_ANY_DIRECTION &&
8161 !(element_info[element].move_pattern & direction))
8162 TurnRound(newx, newy);
8164 /* prevent elements on conveyor belt from moving on in last direction */
8165 if (pushed_by_conveyor && CAN_FALL(element) &&
8166 direction & MV_HORIZONTAL)
8167 MovDir[newx][newy] = 0;
8169 if (!pushed_by_player)
8171 int nextx = newx + dx, nexty = newy + dy;
8172 boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
8174 WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
8176 if (CAN_FALL(element) && direction == MV_DOWN)
8177 WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
8179 if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
8180 CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
8182 if (CAN_FALL(element) && direction == MV_DOWN && check_collision_again)
8183 CheckImpact[newx][newy] = CHECK_DELAY_IMPACT;
8186 if (DONT_TOUCH(element)) /* object may be nasty to player or others */
8188 TestIfBadThingTouchesPlayer(newx, newy);
8189 TestIfBadThingTouchesFriend(newx, newy);
8191 if (!IS_CUSTOM_ELEMENT(element))
8192 TestIfBadThingTouchesOtherBadThing(newx, newy);
8194 else if (element == EL_PENGUIN)
8195 TestIfFriendTouchesBadThing(newx, newy);
8197 if (DONT_GET_HIT_BY(element))
8199 TestIfGoodThingGetsHitByBadThing(newx, newy, direction);
8202 /* give the player one last chance (one more frame) to move away */
8203 if (CAN_FALL(element) && direction == MV_DOWN &&
8204 (last_line || (!IS_FREE(x, newy + 1) &&
8205 (!IS_PLAYER(x, newy + 1) ||
8206 game.engine_version < VERSION_IDENT(3,1,1,0)))))
8209 if (pushed_by_player && !game.use_change_when_pushing_bug)
8211 int push_side = MV_DIR_OPPOSITE(direction);
8212 struct PlayerInfo *player = PLAYERINFO(x, y);
8214 CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
8215 player->index_bit, push_side);
8216 CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
8217 player->index_bit, push_side);
8220 if (element == EL_EMC_ANDROID && pushed_by_player) /* make another move */
8221 MovDelay[newx][newy] = 1;
8223 CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
8225 TestIfElementTouchesCustomElement(x, y); /* empty or new element */
8226 TestIfElementHitsCustomElement(newx, newy, direction);
8227 TestIfPlayerTouchesCustomElement(newx, newy);
8228 TestIfElementTouchesCustomElement(newx, newy);
8230 if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
8231 IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
8232 CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
8233 MV_DIR_OPPOSITE(direction));
8236 int AmoebeNachbarNr(int ax, int ay)
8239 int element = Feld[ax][ay];
8241 static int xy[4][2] =
8249 for (i = 0; i < NUM_DIRECTIONS; i++)
8251 int x = ax + xy[i][0];
8252 int y = ay + xy[i][1];
8254 if (!IN_LEV_FIELD(x, y))
8257 if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
8258 group_nr = AmoebaNr[x][y];
8264 void AmoebenVereinigen(int ax, int ay)
8266 int i, x, y, xx, yy;
8267 int new_group_nr = AmoebaNr[ax][ay];
8268 static int xy[4][2] =
8276 if (new_group_nr == 0)
8279 for (i = 0; i < NUM_DIRECTIONS; i++)
8284 if (!IN_LEV_FIELD(x, y))
8287 if ((Feld[x][y] == EL_AMOEBA_FULL ||
8288 Feld[x][y] == EL_BD_AMOEBA ||
8289 Feld[x][y] == EL_AMOEBA_DEAD) &&
8290 AmoebaNr[x][y] != new_group_nr)
8292 int old_group_nr = AmoebaNr[x][y];
8294 if (old_group_nr == 0)
8297 AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
8298 AmoebaCnt[old_group_nr] = 0;
8299 AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
8300 AmoebaCnt2[old_group_nr] = 0;
8302 SCAN_PLAYFIELD(xx, yy)
8304 if (AmoebaNr[xx][yy] == old_group_nr)
8305 AmoebaNr[xx][yy] = new_group_nr;
8311 void AmoebeUmwandeln(int ax, int ay)
8315 if (Feld[ax][ay] == EL_AMOEBA_DEAD)
8317 int group_nr = AmoebaNr[ax][ay];
8322 printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
8323 printf("AmoebeUmwandeln(): This should never happen!\n");
8328 SCAN_PLAYFIELD(x, y)
8330 if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
8333 Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
8337 PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
8338 SND_AMOEBA_TURNING_TO_GEM :
8339 SND_AMOEBA_TURNING_TO_ROCK));
8344 static int xy[4][2] =
8352 for (i = 0; i < NUM_DIRECTIONS; i++)
8357 if (!IN_LEV_FIELD(x, y))
8360 if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
8362 PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
8363 SND_AMOEBA_TURNING_TO_GEM :
8364 SND_AMOEBA_TURNING_TO_ROCK));
8371 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
8374 int group_nr = AmoebaNr[ax][ay];
8375 boolean done = FALSE;
8380 printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
8381 printf("AmoebeUmwandelnBD(): This should never happen!\n");
8386 SCAN_PLAYFIELD(x, y)
8388 if (AmoebaNr[x][y] == group_nr &&
8389 (Feld[x][y] == EL_AMOEBA_DEAD ||
8390 Feld[x][y] == EL_BD_AMOEBA ||
8391 Feld[x][y] == EL_AMOEBA_GROWING))
8394 Feld[x][y] = new_element;
8395 InitField(x, y, FALSE);
8396 TEST_DrawLevelField(x, y);
8402 PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
8403 SND_BD_AMOEBA_TURNING_TO_ROCK :
8404 SND_BD_AMOEBA_TURNING_TO_GEM));
8407 void AmoebeWaechst(int x, int y)
8409 static unsigned int sound_delay = 0;
8410 static unsigned int sound_delay_value = 0;
8412 if (!MovDelay[x][y]) /* start new growing cycle */
8416 if (DelayReached(&sound_delay, sound_delay_value))
8418 PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
8419 sound_delay_value = 30;
8423 if (MovDelay[x][y]) /* wait some time before growing bigger */
8426 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8428 int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
8429 6 - MovDelay[x][y]);
8431 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
8434 if (!MovDelay[x][y])
8436 Feld[x][y] = Store[x][y];
8438 TEST_DrawLevelField(x, y);
8443 void AmoebaDisappearing(int x, int y)
8445 static unsigned int sound_delay = 0;
8446 static unsigned int sound_delay_value = 0;
8448 if (!MovDelay[x][y]) /* start new shrinking cycle */
8452 if (DelayReached(&sound_delay, sound_delay_value))
8453 sound_delay_value = 30;
8456 if (MovDelay[x][y]) /* wait some time before shrinking */
8459 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8461 int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
8462 6 - MovDelay[x][y]);
8464 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
8467 if (!MovDelay[x][y])
8469 Feld[x][y] = EL_EMPTY;
8470 TEST_DrawLevelField(x, y);
8472 /* don't let mole enter this field in this cycle;
8473 (give priority to objects falling to this field from above) */
8479 void AmoebeAbleger(int ax, int ay)
8482 int element = Feld[ax][ay];
8483 int graphic = el2img(element);
8484 int newax = ax, neway = ay;
8485 boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
8486 static int xy[4][2] =
8494 if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
8496 Feld[ax][ay] = EL_AMOEBA_DEAD;
8497 TEST_DrawLevelField(ax, ay);
8501 if (IS_ANIMATED(graphic))
8502 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
8504 if (!MovDelay[ax][ay]) /* start making new amoeba field */
8505 MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
8507 if (MovDelay[ax][ay]) /* wait some time before making new amoeba */
8510 if (MovDelay[ax][ay])
8514 if (can_drop) /* EL_AMOEBA_WET or EL_EMC_DRIPPER */
8517 int x = ax + xy[start][0];
8518 int y = ay + xy[start][1];
8520 if (!IN_LEV_FIELD(x, y))
8523 if (IS_FREE(x, y) ||
8524 CAN_GROW_INTO(Feld[x][y]) ||
8525 Feld[x][y] == EL_QUICKSAND_EMPTY ||
8526 Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
8532 if (newax == ax && neway == ay)
8535 else /* normal or "filled" (BD style) amoeba */
8538 boolean waiting_for_player = FALSE;
8540 for (i = 0; i < NUM_DIRECTIONS; i++)
8542 int j = (start + i) % 4;
8543 int x = ax + xy[j][0];
8544 int y = ay + xy[j][1];
8546 if (!IN_LEV_FIELD(x, y))
8549 if (IS_FREE(x, y) ||
8550 CAN_GROW_INTO(Feld[x][y]) ||
8551 Feld[x][y] == EL_QUICKSAND_EMPTY ||
8552 Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
8558 else if (IS_PLAYER(x, y))
8559 waiting_for_player = TRUE;
8562 if (newax == ax && neway == ay) /* amoeba cannot grow */
8564 if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
8566 Feld[ax][ay] = EL_AMOEBA_DEAD;
8567 TEST_DrawLevelField(ax, ay);
8568 AmoebaCnt[AmoebaNr[ax][ay]]--;
8570 if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0) /* amoeba is completely dead */
8572 if (element == EL_AMOEBA_FULL)
8573 AmoebeUmwandeln(ax, ay);
8574 else if (element == EL_BD_AMOEBA)
8575 AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
8580 else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
8582 /* amoeba gets larger by growing in some direction */
8584 int new_group_nr = AmoebaNr[ax][ay];
8587 if (new_group_nr == 0)
8589 printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
8590 printf("AmoebeAbleger(): This should never happen!\n");
8595 AmoebaNr[newax][neway] = new_group_nr;
8596 AmoebaCnt[new_group_nr]++;
8597 AmoebaCnt2[new_group_nr]++;
8599 /* if amoeba touches other amoeba(s) after growing, unify them */
8600 AmoebenVereinigen(newax, neway);
8602 if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
8604 AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
8610 if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
8611 (neway == lev_fieldy - 1 && newax != ax))
8613 Feld[newax][neway] = EL_AMOEBA_GROWING; /* creation of new amoeba */
8614 Store[newax][neway] = element;
8616 else if (neway == ay || element == EL_EMC_DRIPPER)
8618 Feld[newax][neway] = EL_AMOEBA_DROP; /* drop left/right of amoeba */
8620 PlayLevelSoundAction(newax, neway, ACTION_GROWING);
8624 InitMovingField(ax, ay, MV_DOWN); /* drop dripping from amoeba */
8625 Feld[ax][ay] = EL_AMOEBA_DROPPING;
8626 Store[ax][ay] = EL_AMOEBA_DROP;
8627 ContinueMoving(ax, ay);
8631 TEST_DrawLevelField(newax, neway);
8634 void Life(int ax, int ay)
8638 int element = Feld[ax][ay];
8639 int graphic = el2img(element);
8640 int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
8642 boolean changed = FALSE;
8644 if (IS_ANIMATED(graphic))
8645 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
8650 if (!MovDelay[ax][ay]) /* start new "game of life" cycle */
8651 MovDelay[ax][ay] = life_time;
8653 if (MovDelay[ax][ay]) /* wait some time before next cycle */
8656 if (MovDelay[ax][ay])
8660 for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
8662 int xx = ax+x1, yy = ay+y1;
8665 if (!IN_LEV_FIELD(xx, yy))
8668 for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
8670 int x = xx+x2, y = yy+y2;
8672 if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
8675 if (((Feld[x][y] == element ||
8676 (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
8678 (IS_FREE(x, y) && Stop[x][y]))
8682 if (xx == ax && yy == ay) /* field in the middle */
8684 if (nachbarn < life_parameter[0] ||
8685 nachbarn > life_parameter[1])
8687 Feld[xx][yy] = EL_EMPTY;
8689 TEST_DrawLevelField(xx, yy);
8690 Stop[xx][yy] = TRUE;
8694 else if (IS_FREE(xx, yy) || CAN_GROW_INTO(Feld[xx][yy]))
8695 { /* free border field */
8696 if (nachbarn >= life_parameter[2] &&
8697 nachbarn <= life_parameter[3])
8699 Feld[xx][yy] = element;
8700 MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
8702 TEST_DrawLevelField(xx, yy);
8703 Stop[xx][yy] = TRUE;
8710 PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
8711 SND_GAME_OF_LIFE_GROWING);
8714 static void InitRobotWheel(int x, int y)
8716 ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
8719 static void RunRobotWheel(int x, int y)
8721 PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
8724 static void StopRobotWheel(int x, int y)
8726 if (ZX == x && ZY == y)
8730 game.robot_wheel_active = FALSE;
8734 static void InitTimegateWheel(int x, int y)
8736 ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
8739 static void RunTimegateWheel(int x, int y)
8741 PlayLevelSound(x, y, SND_CLASS_TIMEGATE_SWITCH_ACTIVE);
8744 static void InitMagicBallDelay(int x, int y)
8746 ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
8749 static void ActivateMagicBall(int bx, int by)
8753 if (level.ball_random)
8755 int pos_border = RND(8); /* select one of the eight border elements */
8756 int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
8757 int xx = pos_content % 3;
8758 int yy = pos_content / 3;
8763 if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
8764 CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
8768 for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
8770 int xx = x - bx + 1;
8771 int yy = y - by + 1;
8773 if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
8774 CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
8778 game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
8781 void CheckExit(int x, int y)
8783 if (local_player->gems_still_needed > 0 ||
8784 local_player->sokobanfields_still_needed > 0 ||
8785 local_player->lights_still_needed > 0)
8787 int element = Feld[x][y];
8788 int graphic = el2img(element);
8790 if (IS_ANIMATED(graphic))
8791 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8796 if (AllPlayersGone) /* do not re-open exit door closed after last player */
8799 Feld[x][y] = EL_EXIT_OPENING;
8801 PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
8804 void CheckExitEM(int x, int y)
8806 if (local_player->gems_still_needed > 0 ||
8807 local_player->sokobanfields_still_needed > 0 ||
8808 local_player->lights_still_needed > 0)
8810 int element = Feld[x][y];
8811 int graphic = el2img(element);
8813 if (IS_ANIMATED(graphic))
8814 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8819 if (AllPlayersGone) /* do not re-open exit door closed after last player */
8822 Feld[x][y] = EL_EM_EXIT_OPENING;
8824 PlayLevelSoundNearest(x, y, SND_CLASS_EM_EXIT_OPENING);
8827 void CheckExitSteel(int x, int y)
8829 if (local_player->gems_still_needed > 0 ||
8830 local_player->sokobanfields_still_needed > 0 ||
8831 local_player->lights_still_needed > 0)
8833 int element = Feld[x][y];
8834 int graphic = el2img(element);
8836 if (IS_ANIMATED(graphic))
8837 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8842 if (AllPlayersGone) /* do not re-open exit door closed after last player */
8845 Feld[x][y] = EL_STEEL_EXIT_OPENING;
8847 PlayLevelSoundNearest(x, y, SND_CLASS_STEEL_EXIT_OPENING);
8850 void CheckExitSteelEM(int x, int y)
8852 if (local_player->gems_still_needed > 0 ||
8853 local_player->sokobanfields_still_needed > 0 ||
8854 local_player->lights_still_needed > 0)
8856 int element = Feld[x][y];
8857 int graphic = el2img(element);
8859 if (IS_ANIMATED(graphic))
8860 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8865 if (AllPlayersGone) /* do not re-open exit door closed after last player */
8868 Feld[x][y] = EL_EM_STEEL_EXIT_OPENING;
8870 PlayLevelSoundNearest(x, y, SND_CLASS_EM_STEEL_EXIT_OPENING);
8873 void CheckExitSP(int x, int y)
8875 if (local_player->gems_still_needed > 0)
8877 int element = Feld[x][y];
8878 int graphic = el2img(element);
8880 if (IS_ANIMATED(graphic))
8881 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8886 if (AllPlayersGone) /* do not re-open exit door closed after last player */
8889 Feld[x][y] = EL_SP_EXIT_OPENING;
8891 PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
8894 static void CloseAllOpenTimegates()
8898 SCAN_PLAYFIELD(x, y)
8900 int element = Feld[x][y];
8902 if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
8904 Feld[x][y] = EL_TIMEGATE_CLOSING;
8906 PlayLevelSoundAction(x, y, ACTION_CLOSING);
8911 void DrawTwinkleOnField(int x, int y)
8913 if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
8916 if (Feld[x][y] == EL_BD_DIAMOND)
8919 if (MovDelay[x][y] == 0) /* next animation frame */
8920 MovDelay[x][y] = 11 * !GetSimpleRandom(500);
8922 if (MovDelay[x][y] != 0) /* wait some time before next frame */
8926 DrawLevelElementAnimation(x, y, Feld[x][y]);
8928 if (MovDelay[x][y] != 0)
8930 int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
8931 10 - MovDelay[x][y]);
8933 DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
8938 void MauerWaechst(int x, int y)
8942 if (!MovDelay[x][y]) /* next animation frame */
8943 MovDelay[x][y] = 3 * delay;
8945 if (MovDelay[x][y]) /* wait some time before next frame */
8949 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8951 int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
8952 int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
8954 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
8957 if (!MovDelay[x][y])
8959 if (MovDir[x][y] == MV_LEFT)
8961 if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
8962 TEST_DrawLevelField(x - 1, y);
8964 else if (MovDir[x][y] == MV_RIGHT)
8966 if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
8967 TEST_DrawLevelField(x + 1, y);
8969 else if (MovDir[x][y] == MV_UP)
8971 if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
8972 TEST_DrawLevelField(x, y - 1);
8976 if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
8977 TEST_DrawLevelField(x, y + 1);
8980 Feld[x][y] = Store[x][y];
8982 GfxDir[x][y] = MovDir[x][y] = MV_NONE;
8983 TEST_DrawLevelField(x, y);
8988 void MauerAbleger(int ax, int ay)
8990 int element = Feld[ax][ay];
8991 int graphic = el2img(element);
8992 boolean oben_frei = FALSE, unten_frei = FALSE;
8993 boolean links_frei = FALSE, rechts_frei = FALSE;
8994 boolean oben_massiv = FALSE, unten_massiv = FALSE;
8995 boolean links_massiv = FALSE, rechts_massiv = FALSE;
8996 boolean new_wall = FALSE;
8998 if (IS_ANIMATED(graphic))
8999 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9001 if (!MovDelay[ax][ay]) /* start building new wall */
9002 MovDelay[ax][ay] = 6;
9004 if (MovDelay[ax][ay]) /* wait some time before building new wall */
9007 if (MovDelay[ax][ay])
9011 if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9013 if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9015 if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9017 if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9020 if (element == EL_EXPANDABLE_WALL_VERTICAL ||
9021 element == EL_EXPANDABLE_WALL_ANY)
9025 Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
9026 Store[ax][ay-1] = element;
9027 GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9028 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9029 DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9030 IMG_EXPANDABLE_WALL_GROWING_UP, 0);
9035 Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
9036 Store[ax][ay+1] = element;
9037 GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9038 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9039 DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9040 IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
9045 if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9046 element == EL_EXPANDABLE_WALL_ANY ||
9047 element == EL_EXPANDABLE_WALL ||
9048 element == EL_BD_EXPANDABLE_WALL)
9052 Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
9053 Store[ax-1][ay] = element;
9054 GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9055 if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9056 DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9057 IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
9063 Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
9064 Store[ax+1][ay] = element;
9065 GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9066 if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9067 DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9068 IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
9073 if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
9074 TEST_DrawLevelField(ax, ay);
9076 if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
9078 if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
9079 unten_massiv = TRUE;
9080 if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
9081 links_massiv = TRUE;
9082 if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
9083 rechts_massiv = TRUE;
9085 if (((oben_massiv && unten_massiv) ||
9086 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9087 element == EL_EXPANDABLE_WALL) &&
9088 ((links_massiv && rechts_massiv) ||
9089 element == EL_EXPANDABLE_WALL_VERTICAL))
9090 Feld[ax][ay] = EL_WALL;
9093 PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9096 void MauerAblegerStahl(int ax, int ay)
9098 int element = Feld[ax][ay];
9099 int graphic = el2img(element);
9100 boolean oben_frei = FALSE, unten_frei = FALSE;
9101 boolean links_frei = FALSE, rechts_frei = FALSE;
9102 boolean oben_massiv = FALSE, unten_massiv = FALSE;
9103 boolean links_massiv = FALSE, rechts_massiv = FALSE;
9104 boolean new_wall = FALSE;
9106 if (IS_ANIMATED(graphic))
9107 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9109 if (!MovDelay[ax][ay]) /* start building new wall */
9110 MovDelay[ax][ay] = 6;
9112 if (MovDelay[ax][ay]) /* wait some time before building new wall */
9115 if (MovDelay[ax][ay])
9119 if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9121 if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9123 if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9125 if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9128 if (element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
9129 element == EL_EXPANDABLE_STEELWALL_ANY)
9133 Feld[ax][ay-1] = EL_EXPANDABLE_STEELWALL_GROWING;
9134 Store[ax][ay-1] = element;
9135 GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9136 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9137 DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9138 IMG_EXPANDABLE_STEELWALL_GROWING_UP, 0);
9143 Feld[ax][ay+1] = EL_EXPANDABLE_STEELWALL_GROWING;
9144 Store[ax][ay+1] = element;
9145 GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9146 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9147 DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9148 IMG_EXPANDABLE_STEELWALL_GROWING_DOWN, 0);
9153 if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
9154 element == EL_EXPANDABLE_STEELWALL_ANY)
9158 Feld[ax-1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9159 Store[ax-1][ay] = element;
9160 GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9161 if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9162 DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9163 IMG_EXPANDABLE_STEELWALL_GROWING_LEFT, 0);
9169 Feld[ax+1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9170 Store[ax+1][ay] = element;
9171 GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9172 if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9173 DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9174 IMG_EXPANDABLE_STEELWALL_GROWING_RIGHT, 0);
9179 if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
9181 if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
9182 unten_massiv = TRUE;
9183 if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
9184 links_massiv = TRUE;
9185 if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
9186 rechts_massiv = TRUE;
9188 if (((oben_massiv && unten_massiv) ||
9189 element == EL_EXPANDABLE_STEELWALL_HORIZONTAL) &&
9190 ((links_massiv && rechts_massiv) ||
9191 element == EL_EXPANDABLE_STEELWALL_VERTICAL))
9192 Feld[ax][ay] = EL_STEELWALL;
9195 PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9198 void CheckForDragon(int x, int y)
9201 boolean dragon_found = FALSE;
9202 static int xy[4][2] =
9210 for (i = 0; i < NUM_DIRECTIONS; i++)
9212 for (j = 0; j < 4; j++)
9214 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9216 if (IN_LEV_FIELD(xx, yy) &&
9217 (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
9219 if (Feld[xx][yy] == EL_DRAGON)
9220 dragon_found = TRUE;
9229 for (i = 0; i < NUM_DIRECTIONS; i++)
9231 for (j = 0; j < 3; j++)
9233 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9235 if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
9237 Feld[xx][yy] = EL_EMPTY;
9238 TEST_DrawLevelField(xx, yy);
9247 static void InitBuggyBase(int x, int y)
9249 int element = Feld[x][y];
9250 int activating_delay = FRAMES_PER_SECOND / 4;
9253 (element == EL_SP_BUGGY_BASE ?
9254 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
9255 element == EL_SP_BUGGY_BASE_ACTIVATING ?
9257 element == EL_SP_BUGGY_BASE_ACTIVE ?
9258 1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
9261 static void WarnBuggyBase(int x, int y)
9264 static int xy[4][2] =
9272 for (i = 0; i < NUM_DIRECTIONS; i++)
9274 int xx = x + xy[i][0];
9275 int yy = y + xy[i][1];
9277 if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
9279 PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
9286 static void InitTrap(int x, int y)
9288 ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
9291 static void ActivateTrap(int x, int y)
9293 PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
9296 static void ChangeActiveTrap(int x, int y)
9298 int graphic = IMG_TRAP_ACTIVE;
9300 /* if new animation frame was drawn, correct crumbled sand border */
9301 if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
9302 TEST_DrawLevelFieldCrumbled(x, y);
9305 static int getSpecialActionElement(int element, int number, int base_element)
9307 return (element != EL_EMPTY ? element :
9308 number != -1 ? base_element + number - 1 :
9312 static int getModifiedActionNumber(int value_old, int operator, int operand,
9313 int value_min, int value_max)
9315 int value_new = (operator == CA_MODE_SET ? operand :
9316 operator == CA_MODE_ADD ? value_old + operand :
9317 operator == CA_MODE_SUBTRACT ? value_old - operand :
9318 operator == CA_MODE_MULTIPLY ? value_old * operand :
9319 operator == CA_MODE_DIVIDE ? value_old / MAX(1, operand) :
9320 operator == CA_MODE_MODULO ? value_old % MAX(1, operand) :
9323 return (value_new < value_min ? value_min :
9324 value_new > value_max ? value_max :
9328 static void ExecuteCustomElementAction(int x, int y, int element, int page)
9330 struct ElementInfo *ei = &element_info[element];
9331 struct ElementChangeInfo *change = &ei->change_page[page];
9332 int target_element = change->target_element;
9333 int action_type = change->action_type;
9334 int action_mode = change->action_mode;
9335 int action_arg = change->action_arg;
9336 int action_element = change->action_element;
9339 if (!change->has_action)
9342 /* ---------- determine action paramater values -------------------------- */
9344 int level_time_value =
9345 (level.time > 0 ? TimeLeft :
9348 int action_arg_element_raw =
9349 (action_arg == CA_ARG_PLAYER_TRIGGER ? change->actual_trigger_player :
9350 action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
9351 action_arg == CA_ARG_ELEMENT_TARGET ? change->target_element :
9352 action_arg == CA_ARG_ELEMENT_ACTION ? change->action_element :
9353 action_arg == CA_ARG_INVENTORY_RM_TRIGGER ? change->actual_trigger_element:
9354 action_arg == CA_ARG_INVENTORY_RM_TARGET ? change->target_element :
9355 action_arg == CA_ARG_INVENTORY_RM_ACTION ? change->action_element :
9357 int action_arg_element = GetElementFromGroupElement(action_arg_element_raw);
9359 int action_arg_direction =
9360 (action_arg >= CA_ARG_DIRECTION_LEFT &&
9361 action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
9362 action_arg == CA_ARG_DIRECTION_TRIGGER ?
9363 change->actual_trigger_side :
9364 action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
9365 MV_DIR_OPPOSITE(change->actual_trigger_side) :
9368 int action_arg_number_min =
9369 (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
9372 int action_arg_number_max =
9373 (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
9374 action_type == CA_SET_LEVEL_GEMS ? 999 :
9375 action_type == CA_SET_LEVEL_TIME ? 9999 :
9376 action_type == CA_SET_LEVEL_SCORE ? 99999 :
9377 action_type == CA_SET_CE_VALUE ? 9999 :
9378 action_type == CA_SET_CE_SCORE ? 9999 :
9381 int action_arg_number_reset =
9382 (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize[0] :
9383 action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
9384 action_type == CA_SET_LEVEL_TIME ? level.time :
9385 action_type == CA_SET_LEVEL_SCORE ? 0 :
9386 action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
9387 action_type == CA_SET_CE_SCORE ? 0 :
9390 int action_arg_number =
9391 (action_arg <= CA_ARG_MAX ? action_arg :
9392 action_arg >= CA_ARG_SPEED_NOT_MOVING &&
9393 action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
9394 action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
9395 action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
9396 action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
9397 action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
9398 action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
9399 action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
9400 action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
9401 action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
9402 action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? local_player->gems_still_needed :
9403 action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? local_player->score :
9404 action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
9405 action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
9406 action_arg == CA_ARG_ELEMENT_CV_ACTION ? GET_NEW_CE_VALUE(action_element):
9407 action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
9408 action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
9409 action_arg == CA_ARG_ELEMENT_CS_ACTION ? GET_CE_SCORE(action_element) :
9410 action_arg == CA_ARG_ELEMENT_NR_TARGET ? change->target_element :
9411 action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
9412 action_arg == CA_ARG_ELEMENT_NR_ACTION ? change->action_element :
9415 int action_arg_number_old =
9416 (action_type == CA_SET_LEVEL_GEMS ? local_player->gems_still_needed :
9417 action_type == CA_SET_LEVEL_TIME ? TimeLeft :
9418 action_type == CA_SET_LEVEL_SCORE ? local_player->score :
9419 action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
9420 action_type == CA_SET_CE_SCORE ? ei->collect_score :
9423 int action_arg_number_new =
9424 getModifiedActionNumber(action_arg_number_old,
9425 action_mode, action_arg_number,
9426 action_arg_number_min, action_arg_number_max);
9428 int trigger_player_bits =
9429 (change->actual_trigger_player_bits != CH_PLAYER_NONE ?
9430 change->actual_trigger_player_bits : change->trigger_player);
9432 int action_arg_player_bits =
9433 (action_arg >= CA_ARG_PLAYER_1 &&
9434 action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
9435 action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
9436 action_arg == CA_ARG_PLAYER_ACTION ? 1 << GET_PLAYER_NR(action_element) :
9439 /* ---------- execute action -------------------------------------------- */
9441 switch (action_type)
9448 /* ---------- level actions ------------------------------------------- */
9450 case CA_RESTART_LEVEL:
9452 game.restart_level = TRUE;
9457 case CA_SHOW_ENVELOPE:
9459 int element = getSpecialActionElement(action_arg_element,
9460 action_arg_number, EL_ENVELOPE_1);
9462 if (IS_ENVELOPE(element))
9463 local_player->show_envelope = element;
9468 case CA_SET_LEVEL_TIME:
9470 if (level.time > 0) /* only modify limited time value */
9472 TimeLeft = action_arg_number_new;
9474 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
9476 DisplayGameControlValues();
9478 if (!TimeLeft && setup.time_limit)
9479 for (i = 0; i < MAX_PLAYERS; i++)
9480 KillPlayer(&stored_player[i]);
9486 case CA_SET_LEVEL_SCORE:
9488 local_player->score = action_arg_number_new;
9490 game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
9492 DisplayGameControlValues();
9497 case CA_SET_LEVEL_GEMS:
9499 local_player->gems_still_needed = action_arg_number_new;
9501 game.snapshot.collected_item = TRUE;
9503 game_panel_controls[GAME_PANEL_GEMS].value =
9504 local_player->gems_still_needed;
9506 DisplayGameControlValues();
9511 case CA_SET_LEVEL_WIND:
9513 game.wind_direction = action_arg_direction;
9518 case CA_SET_LEVEL_RANDOM_SEED:
9520 /* ensure that setting a new random seed while playing is predictable */
9521 InitRND(action_arg_number_new ? action_arg_number_new : RND(1000000) + 1);
9526 /* ---------- player actions ------------------------------------------ */
9528 case CA_MOVE_PLAYER:
9530 /* automatically move to the next field in specified direction */
9531 for (i = 0; i < MAX_PLAYERS; i++)
9532 if (trigger_player_bits & (1 << i))
9533 stored_player[i].programmed_action = action_arg_direction;
9538 case CA_EXIT_PLAYER:
9540 for (i = 0; i < MAX_PLAYERS; i++)
9541 if (action_arg_player_bits & (1 << i))
9542 PlayerWins(&stored_player[i]);
9547 case CA_KILL_PLAYER:
9549 for (i = 0; i < MAX_PLAYERS; i++)
9550 if (action_arg_player_bits & (1 << i))
9551 KillPlayer(&stored_player[i]);
9556 case CA_SET_PLAYER_KEYS:
9558 int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
9559 int element = getSpecialActionElement(action_arg_element,
9560 action_arg_number, EL_KEY_1);
9562 if (IS_KEY(element))
9564 for (i = 0; i < MAX_PLAYERS; i++)
9566 if (trigger_player_bits & (1 << i))
9568 stored_player[i].key[KEY_NR(element)] = key_state;
9570 DrawGameDoorValues();
9578 case CA_SET_PLAYER_SPEED:
9580 for (i = 0; i < MAX_PLAYERS; i++)
9582 if (trigger_player_bits & (1 << i))
9584 int move_stepsize = TILEX / stored_player[i].move_delay_value;
9586 if (action_arg == CA_ARG_SPEED_FASTER &&
9587 stored_player[i].cannot_move)
9589 action_arg_number = STEPSIZE_VERY_SLOW;
9591 else if (action_arg == CA_ARG_SPEED_SLOWER ||
9592 action_arg == CA_ARG_SPEED_FASTER)
9594 action_arg_number = 2;
9595 action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
9598 else if (action_arg == CA_ARG_NUMBER_RESET)
9600 action_arg_number = level.initial_player_stepsize[i];
9604 getModifiedActionNumber(move_stepsize,
9607 action_arg_number_min,
9608 action_arg_number_max);
9610 SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
9617 case CA_SET_PLAYER_SHIELD:
9619 for (i = 0; i < MAX_PLAYERS; i++)
9621 if (trigger_player_bits & (1 << i))
9623 if (action_arg == CA_ARG_SHIELD_OFF)
9625 stored_player[i].shield_normal_time_left = 0;
9626 stored_player[i].shield_deadly_time_left = 0;
9628 else if (action_arg == CA_ARG_SHIELD_NORMAL)
9630 stored_player[i].shield_normal_time_left = 999999;
9632 else if (action_arg == CA_ARG_SHIELD_DEADLY)
9634 stored_player[i].shield_normal_time_left = 999999;
9635 stored_player[i].shield_deadly_time_left = 999999;
9643 case CA_SET_PLAYER_GRAVITY:
9645 for (i = 0; i < MAX_PLAYERS; i++)
9647 if (trigger_player_bits & (1 << i))
9649 stored_player[i].gravity =
9650 (action_arg == CA_ARG_GRAVITY_OFF ? FALSE :
9651 action_arg == CA_ARG_GRAVITY_ON ? TRUE :
9652 action_arg == CA_ARG_GRAVITY_TOGGLE ? !stored_player[i].gravity :
9653 stored_player[i].gravity);
9660 case CA_SET_PLAYER_ARTWORK:
9662 for (i = 0; i < MAX_PLAYERS; i++)
9664 if (trigger_player_bits & (1 << i))
9666 int artwork_element = action_arg_element;
9668 if (action_arg == CA_ARG_ELEMENT_RESET)
9670 (level.use_artwork_element[i] ? level.artwork_element[i] :
9671 stored_player[i].element_nr);
9673 if (stored_player[i].artwork_element != artwork_element)
9674 stored_player[i].Frame = 0;
9676 stored_player[i].artwork_element = artwork_element;
9678 SetPlayerWaiting(&stored_player[i], FALSE);
9680 /* set number of special actions for bored and sleeping animation */
9681 stored_player[i].num_special_action_bored =
9682 get_num_special_action(artwork_element,
9683 ACTION_BORING_1, ACTION_BORING_LAST);
9684 stored_player[i].num_special_action_sleeping =
9685 get_num_special_action(artwork_element,
9686 ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
9693 case CA_SET_PLAYER_INVENTORY:
9695 for (i = 0; i < MAX_PLAYERS; i++)
9697 struct PlayerInfo *player = &stored_player[i];
9700 if (trigger_player_bits & (1 << i))
9702 int inventory_element = action_arg_element;
9704 if (action_arg == CA_ARG_ELEMENT_TARGET ||
9705 action_arg == CA_ARG_ELEMENT_TRIGGER ||
9706 action_arg == CA_ARG_ELEMENT_ACTION)
9708 int element = inventory_element;
9709 int collect_count = element_info[element].collect_count_initial;
9711 if (!IS_CUSTOM_ELEMENT(element))
9714 if (collect_count == 0)
9715 player->inventory_infinite_element = element;
9717 for (k = 0; k < collect_count; k++)
9718 if (player->inventory_size < MAX_INVENTORY_SIZE)
9719 player->inventory_element[player->inventory_size++] =
9722 else if (action_arg == CA_ARG_INVENTORY_RM_TARGET ||
9723 action_arg == CA_ARG_INVENTORY_RM_TRIGGER ||
9724 action_arg == CA_ARG_INVENTORY_RM_ACTION)
9726 if (player->inventory_infinite_element != EL_UNDEFINED &&
9727 IS_EQUAL_OR_IN_GROUP(player->inventory_infinite_element,
9728 action_arg_element_raw))
9729 player->inventory_infinite_element = EL_UNDEFINED;
9731 for (k = 0, j = 0; j < player->inventory_size; j++)
9733 if (!IS_EQUAL_OR_IN_GROUP(player->inventory_element[j],
9734 action_arg_element_raw))
9735 player->inventory_element[k++] = player->inventory_element[j];
9738 player->inventory_size = k;
9740 else if (action_arg == CA_ARG_INVENTORY_RM_FIRST)
9742 if (player->inventory_size > 0)
9744 for (j = 0; j < player->inventory_size - 1; j++)
9745 player->inventory_element[j] = player->inventory_element[j + 1];
9747 player->inventory_size--;
9750 else if (action_arg == CA_ARG_INVENTORY_RM_LAST)
9752 if (player->inventory_size > 0)
9753 player->inventory_size--;
9755 else if (action_arg == CA_ARG_INVENTORY_RM_ALL)
9757 player->inventory_infinite_element = EL_UNDEFINED;
9758 player->inventory_size = 0;
9760 else if (action_arg == CA_ARG_INVENTORY_RESET)
9762 player->inventory_infinite_element = EL_UNDEFINED;
9763 player->inventory_size = 0;
9765 if (level.use_initial_inventory[i])
9767 for (j = 0; j < level.initial_inventory_size[i]; j++)
9769 int element = level.initial_inventory_content[i][j];
9770 int collect_count = element_info[element].collect_count_initial;
9772 if (!IS_CUSTOM_ELEMENT(element))
9775 if (collect_count == 0)
9776 player->inventory_infinite_element = element;
9778 for (k = 0; k < collect_count; k++)
9779 if (player->inventory_size < MAX_INVENTORY_SIZE)
9780 player->inventory_element[player->inventory_size++] =
9791 /* ---------- CE actions ---------------------------------------------- */
9793 case CA_SET_CE_VALUE:
9795 int last_ce_value = CustomValue[x][y];
9797 CustomValue[x][y] = action_arg_number_new;
9799 if (CustomValue[x][y] != last_ce_value)
9801 CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_CHANGES);
9802 CheckTriggeredElementChange(x, y, element, CE_VALUE_CHANGES_OF_X);
9804 if (CustomValue[x][y] == 0)
9806 CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
9807 CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
9814 case CA_SET_CE_SCORE:
9816 int last_ce_score = ei->collect_score;
9818 ei->collect_score = action_arg_number_new;
9820 if (ei->collect_score != last_ce_score)
9822 CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
9823 CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
9825 if (ei->collect_score == 0)
9829 CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
9830 CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
9833 This is a very special case that seems to be a mixture between
9834 CheckElementChange() and CheckTriggeredElementChange(): while
9835 the first one only affects single elements that are triggered
9836 directly, the second one affects multiple elements in the playfield
9837 that are triggered indirectly by another element. This is a third
9838 case: Changing the CE score always affects multiple identical CEs,
9839 so every affected CE must be checked, not only the single CE for
9840 which the CE score was changed in the first place (as every instance
9841 of that CE shares the same CE score, and therefore also can change)!
9843 SCAN_PLAYFIELD(xx, yy)
9845 if (Feld[xx][yy] == element)
9846 CheckElementChange(xx, yy, element, EL_UNDEFINED,
9847 CE_SCORE_GETS_ZERO);
9855 case CA_SET_CE_ARTWORK:
9857 int artwork_element = action_arg_element;
9858 boolean reset_frame = FALSE;
9861 if (action_arg == CA_ARG_ELEMENT_RESET)
9862 artwork_element = (ei->use_gfx_element ? ei->gfx_element_initial :
9865 if (ei->gfx_element != artwork_element)
9868 ei->gfx_element = artwork_element;
9870 SCAN_PLAYFIELD(xx, yy)
9872 if (Feld[xx][yy] == element)
9876 ResetGfxAnimation(xx, yy);
9877 ResetRandomAnimationValue(xx, yy);
9880 TEST_DrawLevelField(xx, yy);
9887 /* ---------- engine actions ------------------------------------------ */
9889 case CA_SET_ENGINE_SCAN_MODE:
9891 InitPlayfieldScanMode(action_arg);
9901 static void CreateFieldExt(int x, int y, int element, boolean is_change)
9903 int old_element = Feld[x][y];
9904 int new_element = GetElementFromGroupElement(element);
9905 int previous_move_direction = MovDir[x][y];
9906 int last_ce_value = CustomValue[x][y];
9907 boolean player_explosion_protected = PLAYER_EXPLOSION_PROTECTED(x, y);
9908 boolean new_element_is_player = ELEM_IS_PLAYER(new_element);
9909 boolean add_player_onto_element = (new_element_is_player &&
9910 new_element != EL_SOKOBAN_FIELD_PLAYER &&
9911 IS_WALKABLE(old_element));
9913 if (!add_player_onto_element)
9915 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
9916 RemoveMovingField(x, y);
9920 Feld[x][y] = new_element;
9922 if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
9923 MovDir[x][y] = previous_move_direction;
9925 if (element_info[new_element].use_last_ce_value)
9926 CustomValue[x][y] = last_ce_value;
9928 InitField_WithBug1(x, y, FALSE);
9930 new_element = Feld[x][y]; /* element may have changed */
9932 ResetGfxAnimation(x, y);
9933 ResetRandomAnimationValue(x, y);
9935 TEST_DrawLevelField(x, y);
9937 if (GFX_CRUMBLED(new_element))
9938 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
9941 /* check if element under the player changes from accessible to unaccessible
9942 (needed for special case of dropping element which then changes) */
9943 /* (must be checked after creating new element for walkable group elements) */
9944 if (IS_PLAYER(x, y) && !player_explosion_protected &&
9945 IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
9952 /* "ChangeCount" not set yet to allow "entered by player" change one time */
9953 if (new_element_is_player)
9954 RelocatePlayer(x, y, new_element);
9957 ChangeCount[x][y]++; /* count number of changes in the same frame */
9959 TestIfBadThingTouchesPlayer(x, y);
9960 TestIfPlayerTouchesCustomElement(x, y);
9961 TestIfElementTouchesCustomElement(x, y);
9964 static void CreateField(int x, int y, int element)
9966 CreateFieldExt(x, y, element, FALSE);
9969 static void CreateElementFromChange(int x, int y, int element)
9971 element = GET_VALID_RUNTIME_ELEMENT(element);
9973 if (game.engine_version >= VERSION_IDENT(3,2,0,7))
9975 int old_element = Feld[x][y];
9977 /* prevent changed element from moving in same engine frame
9978 unless both old and new element can either fall or move */
9979 if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
9980 (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
9984 CreateFieldExt(x, y, element, TRUE);
9987 static boolean ChangeElement(int x, int y, int element, int page)
9989 struct ElementInfo *ei = &element_info[element];
9990 struct ElementChangeInfo *change = &ei->change_page[page];
9991 int ce_value = CustomValue[x][y];
9992 int ce_score = ei->collect_score;
9994 int old_element = Feld[x][y];
9996 /* always use default change event to prevent running into a loop */
9997 if (ChangeEvent[x][y] == -1)
9998 ChangeEvent[x][y] = CE_DELAY;
10000 if (ChangeEvent[x][y] == CE_DELAY)
10002 /* reset actual trigger element, trigger player and action element */
10003 change->actual_trigger_element = EL_EMPTY;
10004 change->actual_trigger_player = EL_EMPTY;
10005 change->actual_trigger_player_bits = CH_PLAYER_NONE;
10006 change->actual_trigger_side = CH_SIDE_NONE;
10007 change->actual_trigger_ce_value = 0;
10008 change->actual_trigger_ce_score = 0;
10011 /* do not change elements more than a specified maximum number of changes */
10012 if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
10015 ChangeCount[x][y]++; /* count number of changes in the same frame */
10017 if (change->explode)
10024 if (change->use_target_content)
10026 boolean complete_replace = TRUE;
10027 boolean can_replace[3][3];
10030 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10033 boolean is_walkable;
10034 boolean is_diggable;
10035 boolean is_collectible;
10036 boolean is_removable;
10037 boolean is_destructible;
10038 int ex = x + xx - 1;
10039 int ey = y + yy - 1;
10040 int content_element = change->target_content.e[xx][yy];
10043 can_replace[xx][yy] = TRUE;
10045 if (ex == x && ey == y) /* do not check changing element itself */
10048 if (content_element == EL_EMPTY_SPACE)
10050 can_replace[xx][yy] = FALSE; /* do not replace border with space */
10055 if (!IN_LEV_FIELD(ex, ey))
10057 can_replace[xx][yy] = FALSE;
10058 complete_replace = FALSE;
10065 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10066 e = MovingOrBlocked2Element(ex, ey);
10068 is_empty = (IS_FREE(ex, ey) ||
10069 (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
10071 is_walkable = (is_empty || IS_WALKABLE(e));
10072 is_diggable = (is_empty || IS_DIGGABLE(e));
10073 is_collectible = (is_empty || IS_COLLECTIBLE(e));
10074 is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
10075 is_removable = (is_diggable || is_collectible);
10077 can_replace[xx][yy] =
10078 (((change->replace_when == CP_WHEN_EMPTY && is_empty) ||
10079 (change->replace_when == CP_WHEN_WALKABLE && is_walkable) ||
10080 (change->replace_when == CP_WHEN_DIGGABLE && is_diggable) ||
10081 (change->replace_when == CP_WHEN_COLLECTIBLE && is_collectible) ||
10082 (change->replace_when == CP_WHEN_REMOVABLE && is_removable) ||
10083 (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
10084 !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
10086 if (!can_replace[xx][yy])
10087 complete_replace = FALSE;
10090 if (!change->only_if_complete || complete_replace)
10092 boolean something_has_changed = FALSE;
10094 if (change->only_if_complete && change->use_random_replace &&
10095 RND(100) < change->random_percentage)
10098 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10100 int ex = x + xx - 1;
10101 int ey = y + yy - 1;
10102 int content_element;
10104 if (can_replace[xx][yy] && (!change->use_random_replace ||
10105 RND(100) < change->random_percentage))
10107 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10108 RemoveMovingField(ex, ey);
10110 ChangeEvent[ex][ey] = ChangeEvent[x][y];
10112 content_element = change->target_content.e[xx][yy];
10113 target_element = GET_TARGET_ELEMENT(element, content_element, change,
10114 ce_value, ce_score);
10116 CreateElementFromChange(ex, ey, target_element);
10118 something_has_changed = TRUE;
10120 /* for symmetry reasons, freeze newly created border elements */
10121 if (ex != x || ey != y)
10122 Stop[ex][ey] = TRUE; /* no more moving in this frame */
10126 if (something_has_changed)
10128 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10129 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10135 target_element = GET_TARGET_ELEMENT(element, change->target_element, change,
10136 ce_value, ce_score);
10138 if (element == EL_DIAGONAL_GROWING ||
10139 element == EL_DIAGONAL_SHRINKING)
10141 target_element = Store[x][y];
10143 Store[x][y] = EL_EMPTY;
10146 CreateElementFromChange(x, y, target_element);
10148 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10149 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10152 /* this uses direct change before indirect change */
10153 CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
10158 static void HandleElementChange(int x, int y, int page)
10160 int element = MovingOrBlocked2Element(x, y);
10161 struct ElementInfo *ei = &element_info[element];
10162 struct ElementChangeInfo *change = &ei->change_page[page];
10163 boolean handle_action_before_change = FALSE;
10166 if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
10167 !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
10170 printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
10171 x, y, element, element_info[element].token_name);
10172 printf("HandleElementChange(): This should never happen!\n");
10177 /* this can happen with classic bombs on walkable, changing elements */
10178 if (!CAN_CHANGE_OR_HAS_ACTION(element))
10183 if (ChangeDelay[x][y] == 0) /* initialize element change */
10185 ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
10187 if (change->can_change)
10189 /* !!! not clear why graphic animation should be reset at all here !!! */
10190 /* !!! UPDATE: but is needed for correct Snake Bite tail animation !!! */
10191 /* !!! SOLUTION: do not reset if graphics engine set to 4 or above !!! */
10194 GRAPHICAL BUG ADDRESSED BY CHECKING GRAPHICS ENGINE VERSION:
10196 When using an animation frame delay of 1 (this only happens with
10197 "sp_zonk.moving.left/right" in the classic graphics), the default
10198 (non-moving) animation shows wrong animation frames (while the
10199 moving animation, like "sp_zonk.moving.left/right", is correct,
10200 so this graphical bug never shows up with the classic graphics).
10201 For an animation with 4 frames, this causes wrong frames 0,0,1,2
10202 be drawn instead of the correct frames 0,1,2,3. This is caused by
10203 "GfxFrame[][]" being reset *twice* (in two successive frames) after
10204 an element change: First when the change delay ("ChangeDelay[][]")
10205 counter has reached zero after decrementing, then a second time in
10206 the next frame (after "GfxFrame[][]" was already incremented) when
10207 "ChangeDelay[][]" is reset to the initial delay value again.
10209 This causes frame 0 to be drawn twice, while the last frame won't
10210 be drawn anymore, resulting in the wrong frame sequence 0,0,1,2.
10212 As some animations may already be cleverly designed around this bug
10213 (at least the "Snake Bite" snake tail animation does this), it cannot
10214 simply be fixed here without breaking such existing animations.
10215 Unfortunately, it cannot easily be detected if a graphics set was
10216 designed "before" or "after" the bug was fixed. As a workaround,
10217 a new graphics set option "game.graphics_engine_version" was added
10218 to be able to specify the game's major release version for which the
10219 graphics set was designed, which can then be used to decide if the
10220 bugfix should be used (version 4 and above) or not (version 3 or
10221 below, or if no version was specified at all, as with old sets).
10223 (The wrong/fixed animation frames can be tested with the test level set
10224 "test_gfxframe" and level "000", which contains a specially prepared
10225 custom element at level position (x/y) == (11/9) which uses the zonk
10226 animation mentioned above. Using "game.graphics_engine_version: 4"
10227 fixes the wrong animation frames, showing the correct frames 0,1,2,3.
10228 This can also be seen from the debug output for this test element.)
10231 /* when a custom element is about to change (for example by change delay),
10232 do not reset graphic animation when the custom element is moving */
10233 if (game.graphics_engine_version < 4 &&
10236 ResetGfxAnimation(x, y);
10237 ResetRandomAnimationValue(x, y);
10240 if (change->pre_change_function)
10241 change->pre_change_function(x, y);
10245 ChangeDelay[x][y]--;
10247 if (ChangeDelay[x][y] != 0) /* continue element change */
10249 if (change->can_change)
10251 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10253 if (IS_ANIMATED(graphic))
10254 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10256 if (change->change_function)
10257 change->change_function(x, y);
10260 else /* finish element change */
10262 if (ChangePage[x][y] != -1) /* remember page from delayed change */
10264 page = ChangePage[x][y];
10265 ChangePage[x][y] = -1;
10267 change = &ei->change_page[page];
10270 if (IS_MOVING(x, y)) /* never change a running system ;-) */
10272 ChangeDelay[x][y] = 1; /* try change after next move step */
10273 ChangePage[x][y] = page; /* remember page to use for change */
10278 /* special case: set new level random seed before changing element */
10279 if (change->has_action && change->action_type == CA_SET_LEVEL_RANDOM_SEED)
10280 handle_action_before_change = TRUE;
10282 if (change->has_action && handle_action_before_change)
10283 ExecuteCustomElementAction(x, y, element, page);
10285 if (change->can_change)
10287 if (ChangeElement(x, y, element, page))
10289 if (change->post_change_function)
10290 change->post_change_function(x, y);
10294 if (change->has_action && !handle_action_before_change)
10295 ExecuteCustomElementAction(x, y, element, page);
10299 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
10300 int trigger_element,
10302 int trigger_player,
10306 boolean change_done_any = FALSE;
10307 int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
10310 if (!(trigger_events[trigger_element][trigger_event]))
10313 RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
10315 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
10317 int element = EL_CUSTOM_START + i;
10318 boolean change_done = FALSE;
10321 if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
10322 !HAS_ANY_CHANGE_EVENT(element, trigger_event))
10325 for (p = 0; p < element_info[element].num_change_pages; p++)
10327 struct ElementChangeInfo *change = &element_info[element].change_page[p];
10329 if (change->can_change_or_has_action &&
10330 change->has_event[trigger_event] &&
10331 change->trigger_side & trigger_side &&
10332 change->trigger_player & trigger_player &&
10333 change->trigger_page & trigger_page_bits &&
10334 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
10336 change->actual_trigger_element = trigger_element;
10337 change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
10338 change->actual_trigger_player_bits = trigger_player;
10339 change->actual_trigger_side = trigger_side;
10340 change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
10341 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10343 if ((change->can_change && !change_done) || change->has_action)
10347 SCAN_PLAYFIELD(x, y)
10349 if (Feld[x][y] == element)
10351 if (change->can_change && !change_done)
10353 /* if element already changed in this frame, not only prevent
10354 another element change (checked in ChangeElement()), but
10355 also prevent additional element actions for this element */
10357 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
10358 !level.use_action_after_change_bug)
10361 ChangeDelay[x][y] = 1;
10362 ChangeEvent[x][y] = trigger_event;
10364 HandleElementChange(x, y, p);
10366 else if (change->has_action)
10368 /* if element already changed in this frame, not only prevent
10369 another element change (checked in ChangeElement()), but
10370 also prevent additional element actions for this element */
10372 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
10373 !level.use_action_after_change_bug)
10376 ExecuteCustomElementAction(x, y, element, p);
10377 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10382 if (change->can_change)
10384 change_done = TRUE;
10385 change_done_any = TRUE;
10392 RECURSION_LOOP_DETECTION_END();
10394 return change_done_any;
10397 static boolean CheckElementChangeExt(int x, int y,
10399 int trigger_element,
10401 int trigger_player,
10404 boolean change_done = FALSE;
10407 if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
10408 !HAS_ANY_CHANGE_EVENT(element, trigger_event))
10411 if (Feld[x][y] == EL_BLOCKED)
10413 Blocked2Moving(x, y, &x, &y);
10414 element = Feld[x][y];
10417 /* check if element has already changed or is about to change after moving */
10418 if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
10419 Feld[x][y] != element) ||
10421 (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
10422 (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
10423 ChangePage[x][y] != -1)))
10426 RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
10428 for (p = 0; p < element_info[element].num_change_pages; p++)
10430 struct ElementChangeInfo *change = &element_info[element].change_page[p];
10432 /* check trigger element for all events where the element that is checked
10433 for changing interacts with a directly adjacent element -- this is
10434 different to element changes that affect other elements to change on the
10435 whole playfield (which is handeld by CheckTriggeredElementChangeExt()) */
10436 boolean check_trigger_element =
10437 (trigger_event == CE_TOUCHING_X ||
10438 trigger_event == CE_HITTING_X ||
10439 trigger_event == CE_HIT_BY_X ||
10440 trigger_event == CE_DIGGING_X); /* this one was forgotten until 3.2.3 */
10442 if (change->can_change_or_has_action &&
10443 change->has_event[trigger_event] &&
10444 change->trigger_side & trigger_side &&
10445 change->trigger_player & trigger_player &&
10446 (!check_trigger_element ||
10447 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
10449 change->actual_trigger_element = trigger_element;
10450 change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
10451 change->actual_trigger_player_bits = trigger_player;
10452 change->actual_trigger_side = trigger_side;
10453 change->actual_trigger_ce_value = CustomValue[x][y];
10454 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10456 /* special case: trigger element not at (x,y) position for some events */
10457 if (check_trigger_element)
10469 { 0, 0 }, { 0, 0 }, { 0, 0 },
10473 int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
10474 int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
10476 change->actual_trigger_ce_value = CustomValue[xx][yy];
10477 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10480 if (change->can_change && !change_done)
10482 ChangeDelay[x][y] = 1;
10483 ChangeEvent[x][y] = trigger_event;
10485 HandleElementChange(x, y, p);
10487 change_done = TRUE;
10489 else if (change->has_action)
10491 ExecuteCustomElementAction(x, y, element, p);
10492 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10497 RECURSION_LOOP_DETECTION_END();
10499 return change_done;
10502 static void PlayPlayerSound(struct PlayerInfo *player)
10504 int jx = player->jx, jy = player->jy;
10505 int sound_element = player->artwork_element;
10506 int last_action = player->last_action_waiting;
10507 int action = player->action_waiting;
10509 if (player->is_waiting)
10511 if (action != last_action)
10512 PlayLevelSoundElementAction(jx, jy, sound_element, action);
10514 PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
10518 if (action != last_action)
10519 StopSound(element_info[sound_element].sound[last_action]);
10521 if (last_action == ACTION_SLEEPING)
10522 PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
10526 static void PlayAllPlayersSound()
10530 for (i = 0; i < MAX_PLAYERS; i++)
10531 if (stored_player[i].active)
10532 PlayPlayerSound(&stored_player[i]);
10535 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
10537 boolean last_waiting = player->is_waiting;
10538 int move_dir = player->MovDir;
10540 player->dir_waiting = move_dir;
10541 player->last_action_waiting = player->action_waiting;
10545 if (!last_waiting) /* not waiting -> waiting */
10547 player->is_waiting = TRUE;
10549 player->frame_counter_bored =
10551 game.player_boring_delay_fixed +
10552 GetSimpleRandom(game.player_boring_delay_random);
10553 player->frame_counter_sleeping =
10555 game.player_sleeping_delay_fixed +
10556 GetSimpleRandom(game.player_sleeping_delay_random);
10558 InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
10561 if (game.player_sleeping_delay_fixed +
10562 game.player_sleeping_delay_random > 0 &&
10563 player->anim_delay_counter == 0 &&
10564 player->post_delay_counter == 0 &&
10565 FrameCounter >= player->frame_counter_sleeping)
10566 player->is_sleeping = TRUE;
10567 else if (game.player_boring_delay_fixed +
10568 game.player_boring_delay_random > 0 &&
10569 FrameCounter >= player->frame_counter_bored)
10570 player->is_bored = TRUE;
10572 player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
10573 player->is_bored ? ACTION_BORING :
10576 if (player->is_sleeping && player->use_murphy)
10578 /* special case for sleeping Murphy when leaning against non-free tile */
10580 if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
10581 (Feld[player->jx - 1][player->jy] != EL_EMPTY &&
10582 !IS_MOVING(player->jx - 1, player->jy)))
10583 move_dir = MV_LEFT;
10584 else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
10585 (Feld[player->jx + 1][player->jy] != EL_EMPTY &&
10586 !IS_MOVING(player->jx + 1, player->jy)))
10587 move_dir = MV_RIGHT;
10589 player->is_sleeping = FALSE;
10591 player->dir_waiting = move_dir;
10594 if (player->is_sleeping)
10596 if (player->num_special_action_sleeping > 0)
10598 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
10600 int last_special_action = player->special_action_sleeping;
10601 int num_special_action = player->num_special_action_sleeping;
10602 int special_action =
10603 (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
10604 last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
10605 last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
10606 last_special_action + 1 : ACTION_SLEEPING);
10607 int special_graphic =
10608 el_act_dir2img(player->artwork_element, special_action, move_dir);
10610 player->anim_delay_counter =
10611 graphic_info[special_graphic].anim_delay_fixed +
10612 GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
10613 player->post_delay_counter =
10614 graphic_info[special_graphic].post_delay_fixed +
10615 GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
10617 player->special_action_sleeping = special_action;
10620 if (player->anim_delay_counter > 0)
10622 player->action_waiting = player->special_action_sleeping;
10623 player->anim_delay_counter--;
10625 else if (player->post_delay_counter > 0)
10627 player->post_delay_counter--;
10631 else if (player->is_bored)
10633 if (player->num_special_action_bored > 0)
10635 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
10637 int special_action =
10638 ACTION_BORING_1 + GetSimpleRandom(player->num_special_action_bored);
10639 int special_graphic =
10640 el_act_dir2img(player->artwork_element, special_action, move_dir);
10642 player->anim_delay_counter =
10643 graphic_info[special_graphic].anim_delay_fixed +
10644 GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
10645 player->post_delay_counter =
10646 graphic_info[special_graphic].post_delay_fixed +
10647 GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
10649 player->special_action_bored = special_action;
10652 if (player->anim_delay_counter > 0)
10654 player->action_waiting = player->special_action_bored;
10655 player->anim_delay_counter--;
10657 else if (player->post_delay_counter > 0)
10659 player->post_delay_counter--;
10664 else if (last_waiting) /* waiting -> not waiting */
10666 player->is_waiting = FALSE;
10667 player->is_bored = FALSE;
10668 player->is_sleeping = FALSE;
10670 player->frame_counter_bored = -1;
10671 player->frame_counter_sleeping = -1;
10673 player->anim_delay_counter = 0;
10674 player->post_delay_counter = 0;
10676 player->dir_waiting = player->MovDir;
10677 player->action_waiting = ACTION_DEFAULT;
10679 player->special_action_bored = ACTION_DEFAULT;
10680 player->special_action_sleeping = ACTION_DEFAULT;
10684 static void CheckSaveEngineSnapshot(struct PlayerInfo *player)
10686 if ((!player->is_moving && player->was_moving) ||
10687 (player->MovPos == 0 && player->was_moving) ||
10688 (player->is_snapping && !player->was_snapping) ||
10689 (player->is_dropping && !player->was_dropping))
10691 if (!CheckSaveEngineSnapshotToList())
10694 player->was_moving = FALSE;
10695 player->was_snapping = TRUE;
10696 player->was_dropping = TRUE;
10700 if (player->is_moving)
10701 player->was_moving = TRUE;
10703 if (!player->is_snapping)
10704 player->was_snapping = FALSE;
10706 if (!player->is_dropping)
10707 player->was_dropping = FALSE;
10711 static void CheckSingleStepMode(struct PlayerInfo *player)
10713 if (tape.single_step && tape.recording && !tape.pausing)
10715 /* as it is called "single step mode", just return to pause mode when the
10716 player stopped moving after one tile (or never starts moving at all) */
10717 if (!player->is_moving && !player->is_pushing)
10719 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
10720 SnapField(player, 0, 0); /* stop snapping */
10724 CheckSaveEngineSnapshot(player);
10727 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
10729 int left = player_action & JOY_LEFT;
10730 int right = player_action & JOY_RIGHT;
10731 int up = player_action & JOY_UP;
10732 int down = player_action & JOY_DOWN;
10733 int button1 = player_action & JOY_BUTTON_1;
10734 int button2 = player_action & JOY_BUTTON_2;
10735 int dx = (left ? -1 : right ? 1 : 0);
10736 int dy = (up ? -1 : down ? 1 : 0);
10738 if (!player->active || tape.pausing)
10744 SnapField(player, dx, dy);
10748 DropElement(player);
10750 MovePlayer(player, dx, dy);
10753 CheckSingleStepMode(player);
10755 SetPlayerWaiting(player, FALSE);
10757 return player_action;
10761 /* no actions for this player (no input at player's configured device) */
10763 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
10764 SnapField(player, 0, 0);
10765 CheckGravityMovementWhenNotMoving(player);
10767 if (player->MovPos == 0)
10768 SetPlayerWaiting(player, TRUE);
10770 if (player->MovPos == 0) /* needed for tape.playing */
10771 player->is_moving = FALSE;
10773 player->is_dropping = FALSE;
10774 player->is_dropping_pressed = FALSE;
10775 player->drop_pressed_delay = 0;
10777 CheckSingleStepMode(player);
10783 static void CheckLevelTime()
10787 /* !!! SAME CODE AS IN "GameActions()" -- FIX THIS !!! */
10788 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
10790 if (level.native_em_level->lev->home == 0) /* all players at home */
10792 PlayerWins(local_player);
10794 AllPlayersGone = TRUE;
10796 level.native_em_level->lev->home = -1;
10799 if (level.native_em_level->ply[0]->alive == 0 &&
10800 level.native_em_level->ply[1]->alive == 0 &&
10801 level.native_em_level->ply[2]->alive == 0 &&
10802 level.native_em_level->ply[3]->alive == 0) /* all dead */
10803 AllPlayersGone = TRUE;
10805 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
10807 if (game_sp.LevelSolved &&
10808 !game_sp.GameOver) /* game won */
10810 PlayerWins(local_player);
10812 game_sp.GameOver = TRUE;
10814 AllPlayersGone = TRUE;
10817 if (game_sp.GameOver) /* game lost */
10818 AllPlayersGone = TRUE;
10821 if (TimeFrames >= FRAMES_PER_SECOND)
10826 for (i = 0; i < MAX_PLAYERS; i++)
10828 struct PlayerInfo *player = &stored_player[i];
10830 if (SHIELD_ON(player))
10832 player->shield_normal_time_left--;
10834 if (player->shield_deadly_time_left > 0)
10835 player->shield_deadly_time_left--;
10839 if (!local_player->LevelSolved && !level.use_step_counter)
10847 if (TimeLeft <= 10 && setup.time_limit)
10848 PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
10850 /* this does not make sense: game_panel_controls[GAME_PANEL_TIME].value
10851 is reset from other values in UpdateGameDoorValues() -- FIX THIS */
10853 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
10855 if (!TimeLeft && setup.time_limit)
10857 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
10858 level.native_em_level->lev->killed_out_of_time = TRUE;
10860 for (i = 0; i < MAX_PLAYERS; i++)
10861 KillPlayer(&stored_player[i]);
10864 else if (game.no_time_limit && !AllPlayersGone) /* level w/o time limit */
10866 game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
10869 level.native_em_level->lev->time =
10870 (game.no_time_limit ? TimePlayed : TimeLeft);
10873 if (tape.recording || tape.playing)
10874 DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
10877 if (tape.recording || tape.playing)
10878 DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
10880 UpdateAndDisplayGameControlValues();
10883 void AdvanceFrameAndPlayerCounters(int player_nr)
10887 /* advance frame counters (global frame counter and time frame counter) */
10891 /* advance player counters (counters for move delay, move animation etc.) */
10892 for (i = 0; i < MAX_PLAYERS; i++)
10894 boolean advance_player_counters = (player_nr == -1 || player_nr == i);
10895 int move_delay_value = stored_player[i].move_delay_value;
10896 int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
10898 if (!advance_player_counters) /* not all players may be affected */
10901 if (move_frames == 0) /* less than one move per game frame */
10903 int stepsize = TILEX / move_delay_value;
10904 int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
10905 int count = (stored_player[i].is_moving ?
10906 ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
10908 if (count % delay == 0)
10912 stored_player[i].Frame += move_frames;
10914 if (stored_player[i].MovPos != 0)
10915 stored_player[i].StepFrame += move_frames;
10917 if (stored_player[i].move_delay > 0)
10918 stored_player[i].move_delay--;
10920 /* due to bugs in previous versions, counter must count up, not down */
10921 if (stored_player[i].push_delay != -1)
10922 stored_player[i].push_delay++;
10924 if (stored_player[i].drop_delay > 0)
10925 stored_player[i].drop_delay--;
10927 if (stored_player[i].is_dropping_pressed)
10928 stored_player[i].drop_pressed_delay++;
10932 void StartGameActions(boolean init_network_game, boolean record_tape,
10935 unsigned int new_random_seed = InitRND(random_seed);
10938 TapeStartRecording(new_random_seed);
10940 #if defined(NETWORK_AVALIABLE)
10941 if (init_network_game)
10943 SendToServer_StartPlaying();
10952 void GameActionsExt()
10955 static unsigned int game_frame_delay = 0;
10957 unsigned int game_frame_delay_value;
10958 byte *recorded_player_action;
10959 byte summarized_player_action = 0;
10960 byte tape_action[MAX_PLAYERS];
10963 /* detect endless loops, caused by custom element programming */
10964 if (recursion_loop_detected && recursion_loop_depth == 0)
10966 char *message = getStringCat3("Internal Error! Element ",
10967 EL_NAME(recursion_loop_element),
10968 " caused endless loop! Quit the game?");
10970 Error(ERR_WARN, "element '%s' caused endless loop in game engine",
10971 EL_NAME(recursion_loop_element));
10973 RequestQuitGameExt(FALSE, level_editor_test_game, message);
10975 recursion_loop_detected = FALSE; /* if game should be continued */
10982 if (game.restart_level)
10983 StartGameActions(options.network, setup.autorecord, level.random_seed);
10985 /* !!! SAME CODE AS IN "CheckLevelTime()" -- FIX THIS !!! */
10986 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
10988 if (level.native_em_level->lev->home == 0) /* all players at home */
10990 PlayerWins(local_player);
10992 AllPlayersGone = TRUE;
10994 level.native_em_level->lev->home = -1;
10997 if (level.native_em_level->ply[0]->alive == 0 &&
10998 level.native_em_level->ply[1]->alive == 0 &&
10999 level.native_em_level->ply[2]->alive == 0 &&
11000 level.native_em_level->ply[3]->alive == 0) /* all dead */
11001 AllPlayersGone = TRUE;
11003 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11005 if (game_sp.LevelSolved &&
11006 !game_sp.GameOver) /* game won */
11008 PlayerWins(local_player);
11010 game_sp.GameOver = TRUE;
11012 AllPlayersGone = TRUE;
11015 if (game_sp.GameOver) /* game lost */
11016 AllPlayersGone = TRUE;
11019 if (local_player->LevelSolved && !local_player->LevelSolved_GameEnd)
11022 if (AllPlayersGone && !TAPE_IS_STOPPED(tape))
11025 if (game_status != GAME_MODE_PLAYING) /* status might have changed */
11028 game_frame_delay_value =
11029 (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
11031 if (tape.playing && tape.warp_forward && !tape.pausing)
11032 game_frame_delay_value = 0;
11034 SetVideoFrameDelay(game_frame_delay_value);
11038 /* ---------- main game synchronization point ---------- */
11040 int skip = WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11042 printf("::: skip == %d\n", skip);
11045 /* ---------- main game synchronization point ---------- */
11047 WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11051 if (network_playing && !network_player_action_received)
11053 /* try to get network player actions in time */
11055 #if defined(NETWORK_AVALIABLE)
11056 /* last chance to get network player actions without main loop delay */
11057 HandleNetworking();
11060 /* game was quit by network peer */
11061 if (game_status != GAME_MODE_PLAYING)
11064 if (!network_player_action_received)
11065 return; /* failed to get network player actions in time */
11067 /* do not yet reset "network_player_action_received" (for tape.pausing) */
11073 /* at this point we know that we really continue executing the game */
11075 network_player_action_received = FALSE;
11077 /* when playing tape, read previously recorded player input from tape data */
11078 recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
11080 /* TapePlayAction() may return NULL when toggling to "pause before death" */
11084 if (tape.set_centered_player)
11086 game.centered_player_nr_next = tape.centered_player_nr_next;
11087 game.set_centered_player = TRUE;
11090 for (i = 0; i < MAX_PLAYERS; i++)
11092 summarized_player_action |= stored_player[i].action;
11094 if (!network_playing && (game.team_mode || tape.playing))
11095 stored_player[i].effective_action = stored_player[i].action;
11098 #if defined(NETWORK_AVALIABLE)
11099 if (network_playing)
11100 SendToServer_MovePlayer(summarized_player_action);
11103 // summarize all actions at local players mapped input device position
11104 // (this allows using different input devices in single player mode)
11105 if (!options.network && !game.team_mode)
11106 stored_player[map_player_action[local_player->index_nr]].effective_action =
11107 summarized_player_action;
11109 if (tape.recording &&
11111 setup.input_on_focus &&
11112 game.centered_player_nr != -1)
11114 for (i = 0; i < MAX_PLAYERS; i++)
11115 stored_player[i].effective_action =
11116 (i == game.centered_player_nr ? summarized_player_action : 0);
11119 if (recorded_player_action != NULL)
11120 for (i = 0; i < MAX_PLAYERS; i++)
11121 stored_player[i].effective_action = recorded_player_action[i];
11123 for (i = 0; i < MAX_PLAYERS; i++)
11125 tape_action[i] = stored_player[i].effective_action;
11127 /* (this may happen in the RND game engine if a player was not present on
11128 the playfield on level start, but appeared later from a custom element */
11129 if (setup.team_mode &&
11132 !tape.player_participates[i])
11133 tape.player_participates[i] = TRUE;
11136 /* only record actions from input devices, but not programmed actions */
11137 if (tape.recording)
11138 TapeRecordAction(tape_action);
11140 #if USE_NEW_PLAYER_ASSIGNMENTS
11141 // !!! also map player actions in single player mode !!!
11142 // if (game.team_mode)
11145 byte mapped_action[MAX_PLAYERS];
11147 #if DEBUG_PLAYER_ACTIONS
11149 for (i = 0; i < MAX_PLAYERS; i++)
11150 printf(" %d, ", stored_player[i].effective_action);
11153 for (i = 0; i < MAX_PLAYERS; i++)
11154 mapped_action[i] = stored_player[map_player_action[i]].effective_action;
11156 for (i = 0; i < MAX_PLAYERS; i++)
11157 stored_player[i].effective_action = mapped_action[i];
11159 #if DEBUG_PLAYER_ACTIONS
11161 for (i = 0; i < MAX_PLAYERS; i++)
11162 printf(" %d, ", stored_player[i].effective_action);
11166 #if DEBUG_PLAYER_ACTIONS
11170 for (i = 0; i < MAX_PLAYERS; i++)
11171 printf(" %d, ", stored_player[i].effective_action);
11177 for (i = 0; i < MAX_PLAYERS; i++)
11179 // allow engine snapshot in case of changed movement attempt
11180 if ((game.snapshot.last_action[i] & KEY_MOTION) !=
11181 (stored_player[i].effective_action & KEY_MOTION))
11182 game.snapshot.changed_action = TRUE;
11184 // allow engine snapshot in case of snapping/dropping attempt
11185 if ((game.snapshot.last_action[i] & KEY_BUTTON) == 0 &&
11186 (stored_player[i].effective_action & KEY_BUTTON) != 0)
11187 game.snapshot.changed_action = TRUE;
11189 game.snapshot.last_action[i] = stored_player[i].effective_action;
11192 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11194 GameActions_EM_Main();
11196 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11198 GameActions_SP_Main();
11202 GameActions_RND_Main();
11205 BlitScreenToBitmap(backbuffer);
11209 AdvanceFrameAndPlayerCounters(-1); /* advance counters for all players */
11211 if (options.debug) /* calculate frames per second */
11213 static unsigned int fps_counter = 0;
11214 static int fps_frames = 0;
11215 unsigned int fps_delay_ms = Counter() - fps_counter;
11219 if (fps_delay_ms >= 500) /* calculate fps every 0.5 seconds */
11221 global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
11224 fps_counter = Counter();
11227 redraw_mask |= REDRAW_FPS;
11231 static void GameActions_CheckSaveEngineSnapshot()
11233 if (!game.snapshot.save_snapshot)
11236 // clear flag for saving snapshot _before_ saving snapshot
11237 game.snapshot.save_snapshot = FALSE;
11239 SaveEngineSnapshotToList();
11246 GameActions_CheckSaveEngineSnapshot();
11249 void GameActions_EM_Main()
11251 byte effective_action[MAX_PLAYERS];
11252 boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11255 for (i = 0; i < MAX_PLAYERS; i++)
11256 effective_action[i] = stored_player[i].effective_action;
11258 GameActions_EM(effective_action, warp_mode);
11261 void GameActions_SP_Main()
11263 byte effective_action[MAX_PLAYERS];
11264 boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11267 for (i = 0; i < MAX_PLAYERS; i++)
11268 effective_action[i] = stored_player[i].effective_action;
11270 GameActions_SP(effective_action, warp_mode);
11273 void GameActions_RND_Main()
11278 void GameActions_RND()
11280 int magic_wall_x = 0, magic_wall_y = 0;
11281 int i, x, y, element, graphic, last_gfx_frame;
11283 InitPlayfieldScanModeVars();
11285 if (game.engine_version >= VERSION_IDENT(3,2,0,7))
11287 SCAN_PLAYFIELD(x, y)
11289 ChangeCount[x][y] = 0;
11290 ChangeEvent[x][y] = -1;
11294 if (game.set_centered_player)
11296 boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
11298 /* switching to "all players" only possible if all players fit to screen */
11299 if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
11301 game.centered_player_nr_next = game.centered_player_nr;
11302 game.set_centered_player = FALSE;
11305 /* do not switch focus to non-existing (or non-active) player */
11306 if (game.centered_player_nr_next >= 0 &&
11307 !stored_player[game.centered_player_nr_next].active)
11309 game.centered_player_nr_next = game.centered_player_nr;
11310 game.set_centered_player = FALSE;
11314 if (game.set_centered_player &&
11315 ScreenMovPos == 0) /* screen currently aligned at tile position */
11319 if (game.centered_player_nr_next == -1)
11321 setScreenCenteredToAllPlayers(&sx, &sy);
11325 sx = stored_player[game.centered_player_nr_next].jx;
11326 sy = stored_player[game.centered_player_nr_next].jy;
11329 game.centered_player_nr = game.centered_player_nr_next;
11330 game.set_centered_player = FALSE;
11332 DrawRelocateScreen(0, 0, sx, sy, MV_NONE, TRUE, setup.quick_switch);
11333 DrawGameDoorValues();
11336 for (i = 0; i < MAX_PLAYERS; i++)
11338 int actual_player_action = stored_player[i].effective_action;
11341 /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
11342 - rnd_equinox_tetrachloride 048
11343 - rnd_equinox_tetrachloride_ii 096
11344 - rnd_emanuel_schmieg 002
11345 - doctor_sloan_ww 001, 020
11347 if (stored_player[i].MovPos == 0)
11348 CheckGravityMovement(&stored_player[i]);
11351 /* overwrite programmed action with tape action */
11352 if (stored_player[i].programmed_action)
11353 actual_player_action = stored_player[i].programmed_action;
11355 PlayerActions(&stored_player[i], actual_player_action);
11357 ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
11360 ScrollScreen(NULL, SCROLL_GO_ON);
11362 /* for backwards compatibility, the following code emulates a fixed bug that
11363 occured when pushing elements (causing elements that just made their last
11364 pushing step to already (if possible) make their first falling step in the
11365 same game frame, which is bad); this code is also needed to use the famous
11366 "spring push bug" which is used in older levels and might be wanted to be
11367 used also in newer levels, but in this case the buggy pushing code is only
11368 affecting the "spring" element and no other elements */
11370 if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
11372 for (i = 0; i < MAX_PLAYERS; i++)
11374 struct PlayerInfo *player = &stored_player[i];
11375 int x = player->jx;
11376 int y = player->jy;
11378 if (player->active && player->is_pushing && player->is_moving &&
11380 (game.engine_version < VERSION_IDENT(2,2,0,7) ||
11381 Feld[x][y] == EL_SPRING))
11383 ContinueMoving(x, y);
11385 /* continue moving after pushing (this is actually a bug) */
11386 if (!IS_MOVING(x, y))
11387 Stop[x][y] = FALSE;
11392 SCAN_PLAYFIELD(x, y)
11394 ChangeCount[x][y] = 0;
11395 ChangeEvent[x][y] = -1;
11397 /* this must be handled before main playfield loop */
11398 if (Feld[x][y] == EL_PLAYER_IS_LEAVING)
11401 if (MovDelay[x][y] <= 0)
11405 if (Feld[x][y] == EL_ELEMENT_SNAPPING)
11408 if (MovDelay[x][y] <= 0)
11411 TEST_DrawLevelField(x, y);
11413 TestIfElementTouchesCustomElement(x, y); /* for empty space */
11418 if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
11420 printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
11421 printf("GameActions(): This should never happen!\n");
11423 ChangePage[x][y] = -1;
11427 Stop[x][y] = FALSE;
11428 if (WasJustMoving[x][y] > 0)
11429 WasJustMoving[x][y]--;
11430 if (WasJustFalling[x][y] > 0)
11431 WasJustFalling[x][y]--;
11432 if (CheckCollision[x][y] > 0)
11433 CheckCollision[x][y]--;
11434 if (CheckImpact[x][y] > 0)
11435 CheckImpact[x][y]--;
11439 /* reset finished pushing action (not done in ContinueMoving() to allow
11440 continuous pushing animation for elements with zero push delay) */
11441 if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
11443 ResetGfxAnimation(x, y);
11444 TEST_DrawLevelField(x, y);
11448 if (IS_BLOCKED(x, y))
11452 Blocked2Moving(x, y, &oldx, &oldy);
11453 if (!IS_MOVING(oldx, oldy))
11455 printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
11456 printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
11457 printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
11458 printf("GameActions(): This should never happen!\n");
11464 SCAN_PLAYFIELD(x, y)
11466 element = Feld[x][y];
11467 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11468 last_gfx_frame = GfxFrame[x][y];
11470 ResetGfxFrame(x, y);
11472 if (GfxFrame[x][y] != last_gfx_frame && !Stop[x][y])
11473 DrawLevelGraphicAnimation(x, y, graphic);
11475 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
11476 IS_NEXT_FRAME(GfxFrame[x][y], graphic))
11477 ResetRandomAnimationValue(x, y);
11479 SetRandomAnimationValue(x, y);
11481 PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
11483 if (IS_INACTIVE(element))
11485 if (IS_ANIMATED(graphic))
11486 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11491 /* this may take place after moving, so 'element' may have changed */
11492 if (IS_CHANGING(x, y) &&
11493 (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
11495 int page = element_info[element].event_page_nr[CE_DELAY];
11497 HandleElementChange(x, y, page);
11499 element = Feld[x][y];
11500 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11503 if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
11507 element = Feld[x][y];
11508 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11510 if (IS_ANIMATED(graphic) &&
11511 !IS_MOVING(x, y) &&
11513 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11515 if (IS_GEM(element) || element == EL_SP_INFOTRON)
11516 TEST_DrawTwinkleOnField(x, y);
11518 else if (element == EL_ACID)
11521 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11523 else if ((element == EL_EXIT_OPEN ||
11524 element == EL_EM_EXIT_OPEN ||
11525 element == EL_SP_EXIT_OPEN ||
11526 element == EL_STEEL_EXIT_OPEN ||
11527 element == EL_EM_STEEL_EXIT_OPEN ||
11528 element == EL_SP_TERMINAL ||
11529 element == EL_SP_TERMINAL_ACTIVE ||
11530 element == EL_EXTRA_TIME ||
11531 element == EL_SHIELD_NORMAL ||
11532 element == EL_SHIELD_DEADLY) &&
11533 IS_ANIMATED(graphic))
11534 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11535 else if (IS_MOVING(x, y))
11536 ContinueMoving(x, y);
11537 else if (IS_ACTIVE_BOMB(element))
11538 CheckDynamite(x, y);
11539 else if (element == EL_AMOEBA_GROWING)
11540 AmoebeWaechst(x, y);
11541 else if (element == EL_AMOEBA_SHRINKING)
11542 AmoebaDisappearing(x, y);
11544 #if !USE_NEW_AMOEBA_CODE
11545 else if (IS_AMOEBALIVE(element))
11546 AmoebeAbleger(x, y);
11549 else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
11551 else if (element == EL_EXIT_CLOSED)
11553 else if (element == EL_EM_EXIT_CLOSED)
11555 else if (element == EL_STEEL_EXIT_CLOSED)
11556 CheckExitSteel(x, y);
11557 else if (element == EL_EM_STEEL_EXIT_CLOSED)
11558 CheckExitSteelEM(x, y);
11559 else if (element == EL_SP_EXIT_CLOSED)
11561 else if (element == EL_EXPANDABLE_WALL_GROWING ||
11562 element == EL_EXPANDABLE_STEELWALL_GROWING)
11563 MauerWaechst(x, y);
11564 else if (element == EL_EXPANDABLE_WALL ||
11565 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
11566 element == EL_EXPANDABLE_WALL_VERTICAL ||
11567 element == EL_EXPANDABLE_WALL_ANY ||
11568 element == EL_BD_EXPANDABLE_WALL)
11569 MauerAbleger(x, y);
11570 else if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
11571 element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
11572 element == EL_EXPANDABLE_STEELWALL_ANY)
11573 MauerAblegerStahl(x, y);
11574 else if (element == EL_FLAMES)
11575 CheckForDragon(x, y);
11576 else if (element == EL_EXPLOSION)
11577 ; /* drawing of correct explosion animation is handled separately */
11578 else if (element == EL_ELEMENT_SNAPPING ||
11579 element == EL_DIAGONAL_SHRINKING ||
11580 element == EL_DIAGONAL_GROWING)
11582 graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
11584 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11586 else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
11587 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11589 if (IS_BELT_ACTIVE(element))
11590 PlayLevelSoundAction(x, y, ACTION_ACTIVE);
11592 if (game.magic_wall_active)
11594 int jx = local_player->jx, jy = local_player->jy;
11596 /* play the element sound at the position nearest to the player */
11597 if ((element == EL_MAGIC_WALL_FULL ||
11598 element == EL_MAGIC_WALL_ACTIVE ||
11599 element == EL_MAGIC_WALL_EMPTYING ||
11600 element == EL_BD_MAGIC_WALL_FULL ||
11601 element == EL_BD_MAGIC_WALL_ACTIVE ||
11602 element == EL_BD_MAGIC_WALL_EMPTYING ||
11603 element == EL_DC_MAGIC_WALL_FULL ||
11604 element == EL_DC_MAGIC_WALL_ACTIVE ||
11605 element == EL_DC_MAGIC_WALL_EMPTYING) &&
11606 ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
11614 #if USE_NEW_AMOEBA_CODE
11615 /* new experimental amoeba growth stuff */
11616 if (!(FrameCounter % 8))
11618 static unsigned int random = 1684108901;
11620 for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
11622 x = RND(lev_fieldx);
11623 y = RND(lev_fieldy);
11624 element = Feld[x][y];
11626 if (!IS_PLAYER(x,y) &&
11627 (element == EL_EMPTY ||
11628 CAN_GROW_INTO(element) ||
11629 element == EL_QUICKSAND_EMPTY ||
11630 element == EL_QUICKSAND_FAST_EMPTY ||
11631 element == EL_ACID_SPLASH_LEFT ||
11632 element == EL_ACID_SPLASH_RIGHT))
11634 if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
11635 (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
11636 (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
11637 (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
11638 Feld[x][y] = EL_AMOEBA_DROP;
11641 random = random * 129 + 1;
11646 game.explosions_delayed = FALSE;
11648 SCAN_PLAYFIELD(x, y)
11650 element = Feld[x][y];
11652 if (ExplodeField[x][y])
11653 Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
11654 else if (element == EL_EXPLOSION)
11655 Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
11657 ExplodeField[x][y] = EX_TYPE_NONE;
11660 game.explosions_delayed = TRUE;
11662 if (game.magic_wall_active)
11664 if (!(game.magic_wall_time_left % 4))
11666 int element = Feld[magic_wall_x][magic_wall_y];
11668 if (element == EL_BD_MAGIC_WALL_FULL ||
11669 element == EL_BD_MAGIC_WALL_ACTIVE ||
11670 element == EL_BD_MAGIC_WALL_EMPTYING)
11671 PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
11672 else if (element == EL_DC_MAGIC_WALL_FULL ||
11673 element == EL_DC_MAGIC_WALL_ACTIVE ||
11674 element == EL_DC_MAGIC_WALL_EMPTYING)
11675 PlayLevelSound(magic_wall_x, magic_wall_y, SND_DC_MAGIC_WALL_ACTIVE);
11677 PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
11680 if (game.magic_wall_time_left > 0)
11682 game.magic_wall_time_left--;
11684 if (!game.magic_wall_time_left)
11686 SCAN_PLAYFIELD(x, y)
11688 element = Feld[x][y];
11690 if (element == EL_MAGIC_WALL_ACTIVE ||
11691 element == EL_MAGIC_WALL_FULL)
11693 Feld[x][y] = EL_MAGIC_WALL_DEAD;
11694 TEST_DrawLevelField(x, y);
11696 else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
11697 element == EL_BD_MAGIC_WALL_FULL)
11699 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
11700 TEST_DrawLevelField(x, y);
11702 else if (element == EL_DC_MAGIC_WALL_ACTIVE ||
11703 element == EL_DC_MAGIC_WALL_FULL)
11705 Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
11706 TEST_DrawLevelField(x, y);
11710 game.magic_wall_active = FALSE;
11715 if (game.light_time_left > 0)
11717 game.light_time_left--;
11719 if (game.light_time_left == 0)
11720 RedrawAllLightSwitchesAndInvisibleElements();
11723 if (game.timegate_time_left > 0)
11725 game.timegate_time_left--;
11727 if (game.timegate_time_left == 0)
11728 CloseAllOpenTimegates();
11731 if (game.lenses_time_left > 0)
11733 game.lenses_time_left--;
11735 if (game.lenses_time_left == 0)
11736 RedrawAllInvisibleElementsForLenses();
11739 if (game.magnify_time_left > 0)
11741 game.magnify_time_left--;
11743 if (game.magnify_time_left == 0)
11744 RedrawAllInvisibleElementsForMagnifier();
11747 for (i = 0; i < MAX_PLAYERS; i++)
11749 struct PlayerInfo *player = &stored_player[i];
11751 if (SHIELD_ON(player))
11753 if (player->shield_deadly_time_left)
11754 PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
11755 else if (player->shield_normal_time_left)
11756 PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
11760 #if USE_DELAYED_GFX_REDRAW
11761 SCAN_PLAYFIELD(x, y)
11763 if (GfxRedraw[x][y] != GFX_REDRAW_NONE)
11765 /* !!! PROBLEM: THIS REDRAWS THE PLAYFIELD _AFTER_ THE SCAN, BUT TILES
11766 !!! MAY HAVE CHANGED AFTER BEING DRAWN DURING PLAYFIELD SCAN !!! */
11768 if (GfxRedraw[x][y] & GFX_REDRAW_TILE)
11769 DrawLevelField(x, y);
11771 if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED)
11772 DrawLevelFieldCrumbled(x, y);
11774 if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS)
11775 DrawLevelFieldCrumbledNeighbours(x, y);
11777 if (GfxRedraw[x][y] & GFX_REDRAW_TILE_TWINKLED)
11778 DrawTwinkleOnField(x, y);
11781 GfxRedraw[x][y] = GFX_REDRAW_NONE;
11786 PlayAllPlayersSound();
11788 if (local_player->show_envelope != 0 && local_player->MovPos == 0)
11790 ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
11792 local_player->show_envelope = 0;
11795 /* use random number generator in every frame to make it less predictable */
11796 if (game.engine_version >= VERSION_IDENT(3,1,1,0))
11800 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
11802 int min_x = x, min_y = y, max_x = x, max_y = y;
11805 for (i = 0; i < MAX_PLAYERS; i++)
11807 int jx = stored_player[i].jx, jy = stored_player[i].jy;
11809 if (!stored_player[i].active || &stored_player[i] == player)
11812 min_x = MIN(min_x, jx);
11813 min_y = MIN(min_y, jy);
11814 max_x = MAX(max_x, jx);
11815 max_y = MAX(max_y, jy);
11818 return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
11821 static boolean AllPlayersInVisibleScreen()
11825 for (i = 0; i < MAX_PLAYERS; i++)
11827 int jx = stored_player[i].jx, jy = stored_player[i].jy;
11829 if (!stored_player[i].active)
11832 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
11839 void ScrollLevel(int dx, int dy)
11841 int scroll_offset = 2 * TILEX_VAR;
11844 BlitBitmap(drawto_field, drawto_field,
11845 FX + TILEX_VAR * (dx == -1) - scroll_offset,
11846 FY + TILEY_VAR * (dy == -1) - scroll_offset,
11847 SXSIZE - TILEX_VAR * (dx != 0) + 2 * scroll_offset,
11848 SYSIZE - TILEY_VAR * (dy != 0) + 2 * scroll_offset,
11849 FX + TILEX_VAR * (dx == 1) - scroll_offset,
11850 FY + TILEY_VAR * (dy == 1) - scroll_offset);
11854 x = (dx == 1 ? BX1 : BX2);
11855 for (y = BY1; y <= BY2; y++)
11856 DrawScreenField(x, y);
11861 y = (dy == 1 ? BY1 : BY2);
11862 for (x = BX1; x <= BX2; x++)
11863 DrawScreenField(x, y);
11866 redraw_mask |= REDRAW_FIELD;
11869 static boolean canFallDown(struct PlayerInfo *player)
11871 int jx = player->jx, jy = player->jy;
11873 return (IN_LEV_FIELD(jx, jy + 1) &&
11874 (IS_FREE(jx, jy + 1) ||
11875 (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
11876 IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
11877 !IS_WALKABLE_INSIDE(Feld[jx][jy]));
11880 static boolean canPassField(int x, int y, int move_dir)
11882 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
11883 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
11884 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
11885 int nextx = x + dx;
11886 int nexty = y + dy;
11887 int element = Feld[x][y];
11889 return (IS_PASSABLE_FROM(element, opposite_dir) &&
11890 !CAN_MOVE(element) &&
11891 IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
11892 IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
11893 (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
11896 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
11898 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
11899 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
11900 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
11904 return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
11905 IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
11906 (IS_DIGGABLE(Feld[newx][newy]) ||
11907 IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
11908 canPassField(newx, newy, move_dir)));
11911 static void CheckGravityMovement(struct PlayerInfo *player)
11913 if (player->gravity && !player->programmed_action)
11915 int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
11916 int move_dir_vertical = player->effective_action & MV_VERTICAL;
11917 boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
11918 int jx = player->jx, jy = player->jy;
11919 boolean player_is_moving_to_valid_field =
11920 (!player_is_snapping &&
11921 (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
11922 canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
11923 boolean player_can_fall_down = canFallDown(player);
11925 if (player_can_fall_down &&
11926 !player_is_moving_to_valid_field)
11927 player->programmed_action = MV_DOWN;
11931 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
11933 return CheckGravityMovement(player);
11935 if (player->gravity && !player->programmed_action)
11937 int jx = player->jx, jy = player->jy;
11938 boolean field_under_player_is_free =
11939 (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
11940 boolean player_is_standing_on_valid_field =
11941 (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
11942 (IS_WALKABLE(Feld[jx][jy]) &&
11943 !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
11945 if (field_under_player_is_free && !player_is_standing_on_valid_field)
11946 player->programmed_action = MV_DOWN;
11951 MovePlayerOneStep()
11952 -----------------------------------------------------------------------------
11953 dx, dy: direction (non-diagonal) to try to move the player to
11954 real_dx, real_dy: direction as read from input device (can be diagonal)
11957 boolean MovePlayerOneStep(struct PlayerInfo *player,
11958 int dx, int dy, int real_dx, int real_dy)
11960 int jx = player->jx, jy = player->jy;
11961 int new_jx = jx + dx, new_jy = jy + dy;
11963 boolean player_can_move = !player->cannot_move;
11965 if (!player->active || (!dx && !dy))
11966 return MP_NO_ACTION;
11968 player->MovDir = (dx < 0 ? MV_LEFT :
11969 dx > 0 ? MV_RIGHT :
11971 dy > 0 ? MV_DOWN : MV_NONE);
11973 if (!IN_LEV_FIELD(new_jx, new_jy))
11974 return MP_NO_ACTION;
11976 if (!player_can_move)
11978 if (player->MovPos == 0)
11980 player->is_moving = FALSE;
11981 player->is_digging = FALSE;
11982 player->is_collecting = FALSE;
11983 player->is_snapping = FALSE;
11984 player->is_pushing = FALSE;
11988 if (!options.network && game.centered_player_nr == -1 &&
11989 !AllPlayersInSight(player, new_jx, new_jy))
11990 return MP_NO_ACTION;
11992 can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
11993 if (can_move != MP_MOVING)
11996 /* check if DigField() has caused relocation of the player */
11997 if (player->jx != jx || player->jy != jy)
11998 return MP_NO_ACTION; /* <-- !!! CHECK THIS [-> MP_ACTION ?] !!! */
12000 StorePlayer[jx][jy] = 0;
12001 player->last_jx = jx;
12002 player->last_jy = jy;
12003 player->jx = new_jx;
12004 player->jy = new_jy;
12005 StorePlayer[new_jx][new_jy] = player->element_nr;
12007 if (player->move_delay_value_next != -1)
12009 player->move_delay_value = player->move_delay_value_next;
12010 player->move_delay_value_next = -1;
12014 (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
12016 player->step_counter++;
12018 PlayerVisit[jx][jy] = FrameCounter;
12020 player->is_moving = TRUE;
12023 /* should better be called in MovePlayer(), but this breaks some tapes */
12024 ScrollPlayer(player, SCROLL_INIT);
12030 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
12032 int jx = player->jx, jy = player->jy;
12033 int old_jx = jx, old_jy = jy;
12034 int moved = MP_NO_ACTION;
12036 if (!player->active)
12041 if (player->MovPos == 0)
12043 player->is_moving = FALSE;
12044 player->is_digging = FALSE;
12045 player->is_collecting = FALSE;
12046 player->is_snapping = FALSE;
12047 player->is_pushing = FALSE;
12053 if (player->move_delay > 0)
12056 player->move_delay = -1; /* set to "uninitialized" value */
12058 /* store if player is automatically moved to next field */
12059 player->is_auto_moving = (player->programmed_action != MV_NONE);
12061 /* remove the last programmed player action */
12062 player->programmed_action = 0;
12064 if (player->MovPos)
12066 /* should only happen if pre-1.2 tape recordings are played */
12067 /* this is only for backward compatibility */
12069 int original_move_delay_value = player->move_delay_value;
12072 printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%d]\n",
12076 /* scroll remaining steps with finest movement resolution */
12077 player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
12079 while (player->MovPos)
12081 ScrollPlayer(player, SCROLL_GO_ON);
12082 ScrollScreen(NULL, SCROLL_GO_ON);
12084 AdvanceFrameAndPlayerCounters(player->index_nr);
12087 BackToFront_WithFrameDelay(0);
12090 player->move_delay_value = original_move_delay_value;
12093 player->is_active = FALSE;
12095 if (player->last_move_dir & MV_HORIZONTAL)
12097 if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
12098 moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
12102 if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
12103 moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
12106 if (!moved && !player->is_active)
12108 player->is_moving = FALSE;
12109 player->is_digging = FALSE;
12110 player->is_collecting = FALSE;
12111 player->is_snapping = FALSE;
12112 player->is_pushing = FALSE;
12118 if (moved & MP_MOVING && !ScreenMovPos &&
12119 (player->index_nr == game.centered_player_nr ||
12120 game.centered_player_nr == -1))
12122 int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
12123 int offset = game.scroll_delay_value;
12125 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12127 /* actual player has left the screen -- scroll in that direction */
12128 if (jx != old_jx) /* player has moved horizontally */
12129 scroll_x += (jx - old_jx);
12130 else /* player has moved vertically */
12131 scroll_y += (jy - old_jy);
12135 if (jx != old_jx) /* player has moved horizontally */
12137 if ((player->MovDir == MV_LEFT && scroll_x > jx - MIDPOSX + offset) ||
12138 (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
12139 scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
12141 /* don't scroll over playfield boundaries */
12142 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
12143 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
12145 /* don't scroll more than one field at a time */
12146 scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
12148 /* don't scroll against the player's moving direction */
12149 if ((player->MovDir == MV_LEFT && scroll_x > old_scroll_x) ||
12150 (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
12151 scroll_x = old_scroll_x;
12153 else /* player has moved vertically */
12155 if ((player->MovDir == MV_UP && scroll_y > jy - MIDPOSY + offset) ||
12156 (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
12157 scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
12159 /* don't scroll over playfield boundaries */
12160 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
12161 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
12163 /* don't scroll more than one field at a time */
12164 scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
12166 /* don't scroll against the player's moving direction */
12167 if ((player->MovDir == MV_UP && scroll_y > old_scroll_y) ||
12168 (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
12169 scroll_y = old_scroll_y;
12173 if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
12175 if (!options.network && game.centered_player_nr == -1 &&
12176 !AllPlayersInVisibleScreen())
12178 scroll_x = old_scroll_x;
12179 scroll_y = old_scroll_y;
12183 ScrollScreen(player, SCROLL_INIT);
12184 ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
12189 player->StepFrame = 0;
12191 if (moved & MP_MOVING)
12193 if (old_jx != jx && old_jy == jy)
12194 player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
12195 else if (old_jx == jx && old_jy != jy)
12196 player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
12198 TEST_DrawLevelField(jx, jy); /* for "crumbled sand" */
12200 player->last_move_dir = player->MovDir;
12201 player->is_moving = TRUE;
12202 player->is_snapping = FALSE;
12203 player->is_switching = FALSE;
12204 player->is_dropping = FALSE;
12205 player->is_dropping_pressed = FALSE;
12206 player->drop_pressed_delay = 0;
12209 /* should better be called here than above, but this breaks some tapes */
12210 ScrollPlayer(player, SCROLL_INIT);
12215 CheckGravityMovementWhenNotMoving(player);
12217 player->is_moving = FALSE;
12219 /* at this point, the player is allowed to move, but cannot move right now
12220 (e.g. because of something blocking the way) -- ensure that the player
12221 is also allowed to move in the next frame (in old versions before 3.1.1,
12222 the player was forced to wait again for eight frames before next try) */
12224 if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12225 player->move_delay = 0; /* allow direct movement in the next frame */
12228 if (player->move_delay == -1) /* not yet initialized by DigField() */
12229 player->move_delay = player->move_delay_value;
12231 if (game.engine_version < VERSION_IDENT(3,0,7,0))
12233 TestIfPlayerTouchesBadThing(jx, jy);
12234 TestIfPlayerTouchesCustomElement(jx, jy);
12237 if (!player->active)
12238 RemovePlayer(player);
12243 void ScrollPlayer(struct PlayerInfo *player, int mode)
12245 int jx = player->jx, jy = player->jy;
12246 int last_jx = player->last_jx, last_jy = player->last_jy;
12247 int move_stepsize = TILEX / player->move_delay_value;
12249 if (!player->active)
12252 if (player->MovPos == 0 && mode == SCROLL_GO_ON) /* player not moving */
12255 if (mode == SCROLL_INIT)
12257 player->actual_frame_counter = FrameCounter;
12258 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
12260 if ((player->block_last_field || player->block_delay_adjustment > 0) &&
12261 Feld[last_jx][last_jy] == EL_EMPTY)
12263 int last_field_block_delay = 0; /* start with no blocking at all */
12264 int block_delay_adjustment = player->block_delay_adjustment;
12266 /* if player blocks last field, add delay for exactly one move */
12267 if (player->block_last_field)
12269 last_field_block_delay += player->move_delay_value;
12271 /* when blocking enabled, prevent moving up despite gravity */
12272 if (player->gravity && player->MovDir == MV_UP)
12273 block_delay_adjustment = -1;
12276 /* add block delay adjustment (also possible when not blocking) */
12277 last_field_block_delay += block_delay_adjustment;
12279 Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
12280 MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
12283 if (player->MovPos != 0) /* player has not yet reached destination */
12286 else if (!FrameReached(&player->actual_frame_counter, 1))
12289 if (player->MovPos != 0)
12291 player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
12292 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
12294 /* before DrawPlayer() to draw correct player graphic for this case */
12295 if (player->MovPos == 0)
12296 CheckGravityMovement(player);
12299 if (player->MovPos == 0) /* player reached destination field */
12301 if (player->move_delay_reset_counter > 0)
12303 player->move_delay_reset_counter--;
12305 if (player->move_delay_reset_counter == 0)
12307 /* continue with normal speed after quickly moving through gate */
12308 HALVE_PLAYER_SPEED(player);
12310 /* be able to make the next move without delay */
12311 player->move_delay = 0;
12315 player->last_jx = jx;
12316 player->last_jy = jy;
12318 if (Feld[jx][jy] == EL_EXIT_OPEN ||
12319 Feld[jx][jy] == EL_EM_EXIT_OPEN ||
12320 Feld[jx][jy] == EL_EM_EXIT_OPENING ||
12321 Feld[jx][jy] == EL_STEEL_EXIT_OPEN ||
12322 Feld[jx][jy] == EL_EM_STEEL_EXIT_OPEN ||
12323 Feld[jx][jy] == EL_EM_STEEL_EXIT_OPENING ||
12324 Feld[jx][jy] == EL_SP_EXIT_OPEN ||
12325 Feld[jx][jy] == EL_SP_EXIT_OPENING) /* <-- special case */
12327 DrawPlayer(player); /* needed here only to cleanup last field */
12328 RemovePlayer(player);
12330 if (local_player->friends_still_needed == 0 ||
12331 IS_SP_ELEMENT(Feld[jx][jy]))
12332 PlayerWins(player);
12335 /* this breaks one level: "machine", level 000 */
12337 int move_direction = player->MovDir;
12338 int enter_side = MV_DIR_OPPOSITE(move_direction);
12339 int leave_side = move_direction;
12340 int old_jx = last_jx;
12341 int old_jy = last_jy;
12342 int old_element = Feld[old_jx][old_jy];
12343 int new_element = Feld[jx][jy];
12345 if (IS_CUSTOM_ELEMENT(old_element))
12346 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
12348 player->index_bit, leave_side);
12350 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
12351 CE_PLAYER_LEAVES_X,
12352 player->index_bit, leave_side);
12354 if (IS_CUSTOM_ELEMENT(new_element))
12355 CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
12356 player->index_bit, enter_side);
12358 CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
12359 CE_PLAYER_ENTERS_X,
12360 player->index_bit, enter_side);
12362 CheckTriggeredElementChangeBySide(jx, jy, player->initial_element,
12363 CE_MOVE_OF_X, move_direction);
12366 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
12368 TestIfPlayerTouchesBadThing(jx, jy);
12369 TestIfPlayerTouchesCustomElement(jx, jy);
12371 /* needed because pushed element has not yet reached its destination,
12372 so it would trigger a change event at its previous field location */
12373 if (!player->is_pushing)
12374 TestIfElementTouchesCustomElement(jx, jy); /* for empty space */
12376 if (!player->active)
12377 RemovePlayer(player);
12380 if (!local_player->LevelSolved && level.use_step_counter)
12390 if (TimeLeft <= 10 && setup.time_limit)
12391 PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
12393 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
12395 DisplayGameControlValues();
12397 if (!TimeLeft && setup.time_limit)
12398 for (i = 0; i < MAX_PLAYERS; i++)
12399 KillPlayer(&stored_player[i]);
12401 else if (game.no_time_limit && !AllPlayersGone) /* level w/o time limit */
12403 game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
12405 DisplayGameControlValues();
12409 if (tape.single_step && tape.recording && !tape.pausing &&
12410 !player->programmed_action)
12411 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
12413 if (!player->programmed_action)
12414 CheckSaveEngineSnapshot(player);
12418 void ScrollScreen(struct PlayerInfo *player, int mode)
12420 static unsigned int screen_frame_counter = 0;
12422 if (mode == SCROLL_INIT)
12424 /* set scrolling step size according to actual player's moving speed */
12425 ScrollStepSize = TILEX / player->move_delay_value;
12427 screen_frame_counter = FrameCounter;
12428 ScreenMovDir = player->MovDir;
12429 ScreenMovPos = player->MovPos;
12430 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
12433 else if (!FrameReached(&screen_frame_counter, 1))
12438 ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
12439 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
12440 redraw_mask |= REDRAW_FIELD;
12443 ScreenMovDir = MV_NONE;
12446 void TestIfPlayerTouchesCustomElement(int x, int y)
12448 static int xy[4][2] =
12455 static int trigger_sides[4][2] =
12457 /* center side border side */
12458 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
12459 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
12460 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
12461 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
12463 static int touch_dir[4] =
12465 MV_LEFT | MV_RIGHT,
12470 int center_element = Feld[x][y]; /* should always be non-moving! */
12473 for (i = 0; i < NUM_DIRECTIONS; i++)
12475 int xx = x + xy[i][0];
12476 int yy = y + xy[i][1];
12477 int center_side = trigger_sides[i][0];
12478 int border_side = trigger_sides[i][1];
12479 int border_element;
12481 if (!IN_LEV_FIELD(xx, yy))
12484 if (IS_PLAYER(x, y)) /* player found at center element */
12486 struct PlayerInfo *player = PLAYERINFO(x, y);
12488 if (game.engine_version < VERSION_IDENT(3,0,7,0))
12489 border_element = Feld[xx][yy]; /* may be moving! */
12490 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
12491 border_element = Feld[xx][yy];
12492 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
12493 border_element = MovingOrBlocked2Element(xx, yy);
12495 continue; /* center and border element do not touch */
12497 CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
12498 player->index_bit, border_side);
12499 CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
12500 CE_PLAYER_TOUCHES_X,
12501 player->index_bit, border_side);
12504 /* use player element that is initially defined in the level playfield,
12505 not the player element that corresponds to the runtime player number
12506 (example: a level that contains EL_PLAYER_3 as the only player would
12507 incorrectly give EL_PLAYER_1 for "player->element_nr") */
12508 int player_element = PLAYERINFO(x, y)->initial_element;
12510 CheckElementChangeBySide(xx, yy, border_element, player_element,
12511 CE_TOUCHING_X, border_side);
12514 else if (IS_PLAYER(xx, yy)) /* player found at border element */
12516 struct PlayerInfo *player = PLAYERINFO(xx, yy);
12518 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
12520 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
12521 continue; /* center and border element do not touch */
12524 CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
12525 player->index_bit, center_side);
12526 CheckTriggeredElementChangeByPlayer(x, y, center_element,
12527 CE_PLAYER_TOUCHES_X,
12528 player->index_bit, center_side);
12531 /* use player element that is initially defined in the level playfield,
12532 not the player element that corresponds to the runtime player number
12533 (example: a level that contains EL_PLAYER_3 as the only player would
12534 incorrectly give EL_PLAYER_1 for "player->element_nr") */
12535 int player_element = PLAYERINFO(xx, yy)->initial_element;
12537 CheckElementChangeBySide(x, y, center_element, player_element,
12538 CE_TOUCHING_X, center_side);
12546 void TestIfElementTouchesCustomElement(int x, int y)
12548 static int xy[4][2] =
12555 static int trigger_sides[4][2] =
12557 /* center side border side */
12558 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
12559 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
12560 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
12561 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
12563 static int touch_dir[4] =
12565 MV_LEFT | MV_RIGHT,
12570 boolean change_center_element = FALSE;
12571 int center_element = Feld[x][y]; /* should always be non-moving! */
12572 int border_element_old[NUM_DIRECTIONS];
12575 for (i = 0; i < NUM_DIRECTIONS; i++)
12577 int xx = x + xy[i][0];
12578 int yy = y + xy[i][1];
12579 int border_element;
12581 border_element_old[i] = -1;
12583 if (!IN_LEV_FIELD(xx, yy))
12586 if (game.engine_version < VERSION_IDENT(3,0,7,0))
12587 border_element = Feld[xx][yy]; /* may be moving! */
12588 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
12589 border_element = Feld[xx][yy];
12590 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
12591 border_element = MovingOrBlocked2Element(xx, yy);
12593 continue; /* center and border element do not touch */
12595 border_element_old[i] = border_element;
12598 for (i = 0; i < NUM_DIRECTIONS; i++)
12600 int xx = x + xy[i][0];
12601 int yy = y + xy[i][1];
12602 int center_side = trigger_sides[i][0];
12603 int border_element = border_element_old[i];
12605 if (border_element == -1)
12608 /* check for change of border element */
12609 CheckElementChangeBySide(xx, yy, border_element, center_element,
12610 CE_TOUCHING_X, center_side);
12612 /* (center element cannot be player, so we dont have to check this here) */
12615 for (i = 0; i < NUM_DIRECTIONS; i++)
12617 int xx = x + xy[i][0];
12618 int yy = y + xy[i][1];
12619 int border_side = trigger_sides[i][1];
12620 int border_element = border_element_old[i];
12622 if (border_element == -1)
12625 /* check for change of center element (but change it only once) */
12626 if (!change_center_element)
12627 change_center_element =
12628 CheckElementChangeBySide(x, y, center_element, border_element,
12629 CE_TOUCHING_X, border_side);
12631 if (IS_PLAYER(xx, yy))
12633 /* use player element that is initially defined in the level playfield,
12634 not the player element that corresponds to the runtime player number
12635 (example: a level that contains EL_PLAYER_3 as the only player would
12636 incorrectly give EL_PLAYER_1 for "player->element_nr") */
12637 int player_element = PLAYERINFO(xx, yy)->initial_element;
12639 CheckElementChangeBySide(x, y, center_element, player_element,
12640 CE_TOUCHING_X, border_side);
12645 void TestIfElementHitsCustomElement(int x, int y, int direction)
12647 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
12648 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
12649 int hitx = x + dx, hity = y + dy;
12650 int hitting_element = Feld[x][y];
12651 int touched_element;
12653 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
12656 touched_element = (IN_LEV_FIELD(hitx, hity) ?
12657 MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
12659 if (IN_LEV_FIELD(hitx, hity))
12661 int opposite_direction = MV_DIR_OPPOSITE(direction);
12662 int hitting_side = direction;
12663 int touched_side = opposite_direction;
12664 boolean object_hit = (!IS_MOVING(hitx, hity) ||
12665 MovDir[hitx][hity] != direction ||
12666 ABS(MovPos[hitx][hity]) <= TILEY / 2);
12672 CheckElementChangeBySide(x, y, hitting_element, touched_element,
12673 CE_HITTING_X, touched_side);
12675 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
12676 CE_HIT_BY_X, hitting_side);
12678 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
12679 CE_HIT_BY_SOMETHING, opposite_direction);
12681 if (IS_PLAYER(hitx, hity))
12683 /* use player element that is initially defined in the level playfield,
12684 not the player element that corresponds to the runtime player number
12685 (example: a level that contains EL_PLAYER_3 as the only player would
12686 incorrectly give EL_PLAYER_1 for "player->element_nr") */
12687 int player_element = PLAYERINFO(hitx, hity)->initial_element;
12689 CheckElementChangeBySide(x, y, hitting_element, player_element,
12690 CE_HITTING_X, touched_side);
12695 /* "hitting something" is also true when hitting the playfield border */
12696 CheckElementChangeBySide(x, y, hitting_element, touched_element,
12697 CE_HITTING_SOMETHING, direction);
12700 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
12702 int i, kill_x = -1, kill_y = -1;
12704 int bad_element = -1;
12705 static int test_xy[4][2] =
12712 static int test_dir[4] =
12720 for (i = 0; i < NUM_DIRECTIONS; i++)
12722 int test_x, test_y, test_move_dir, test_element;
12724 test_x = good_x + test_xy[i][0];
12725 test_y = good_y + test_xy[i][1];
12727 if (!IN_LEV_FIELD(test_x, test_y))
12731 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
12733 test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
12735 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
12736 2nd case: DONT_TOUCH style bad thing does not move away from good thing
12738 if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
12739 (DONT_TOUCH(test_element) && test_move_dir != test_dir[i]))
12743 bad_element = test_element;
12749 if (kill_x != -1 || kill_y != -1)
12751 if (IS_PLAYER(good_x, good_y))
12753 struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
12755 if (player->shield_deadly_time_left > 0 &&
12756 !IS_INDESTRUCTIBLE(bad_element))
12757 Bang(kill_x, kill_y);
12758 else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
12759 KillPlayer(player);
12762 Bang(good_x, good_y);
12766 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
12768 int i, kill_x = -1, kill_y = -1;
12769 int bad_element = Feld[bad_x][bad_y];
12770 static int test_xy[4][2] =
12777 static int touch_dir[4] =
12779 MV_LEFT | MV_RIGHT,
12784 static int test_dir[4] =
12792 if (bad_element == EL_EXPLOSION) /* skip just exploding bad things */
12795 for (i = 0; i < NUM_DIRECTIONS; i++)
12797 int test_x, test_y, test_move_dir, test_element;
12799 test_x = bad_x + test_xy[i][0];
12800 test_y = bad_y + test_xy[i][1];
12802 if (!IN_LEV_FIELD(test_x, test_y))
12806 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
12808 test_element = Feld[test_x][test_y];
12810 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
12811 2nd case: DONT_TOUCH style bad thing does not move away from good thing
12813 if ((DONT_RUN_INTO(bad_element) && bad_move_dir == test_dir[i]) ||
12814 (DONT_TOUCH(bad_element) && test_move_dir != test_dir[i]))
12816 /* good thing is player or penguin that does not move away */
12817 if (IS_PLAYER(test_x, test_y))
12819 struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
12821 if (bad_element == EL_ROBOT && player->is_moving)
12822 continue; /* robot does not kill player if he is moving */
12824 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
12826 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
12827 continue; /* center and border element do not touch */
12835 else if (test_element == EL_PENGUIN)
12845 if (kill_x != -1 || kill_y != -1)
12847 if (IS_PLAYER(kill_x, kill_y))
12849 struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
12851 if (player->shield_deadly_time_left > 0 &&
12852 !IS_INDESTRUCTIBLE(bad_element))
12853 Bang(bad_x, bad_y);
12854 else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
12855 KillPlayer(player);
12858 Bang(kill_x, kill_y);
12862 void TestIfGoodThingGetsHitByBadThing(int bad_x, int bad_y, int bad_move_dir)
12864 int bad_element = Feld[bad_x][bad_y];
12865 int dx = (bad_move_dir == MV_LEFT ? -1 : bad_move_dir == MV_RIGHT ? +1 : 0);
12866 int dy = (bad_move_dir == MV_UP ? -1 : bad_move_dir == MV_DOWN ? +1 : 0);
12867 int test_x = bad_x + dx, test_y = bad_y + dy;
12868 int test_move_dir, test_element;
12869 int kill_x = -1, kill_y = -1;
12871 if (!IN_LEV_FIELD(test_x, test_y))
12875 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
12877 test_element = Feld[test_x][test_y];
12879 if (test_move_dir != bad_move_dir)
12881 /* good thing can be player or penguin that does not move away */
12882 if (IS_PLAYER(test_x, test_y))
12884 struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
12886 /* (note: in comparison to DONT_RUN_TO and DONT_TOUCH, also handle the
12887 player as being hit when he is moving towards the bad thing, because
12888 the "get hit by" condition would be lost after the player stops) */
12889 if (player->MovPos != 0 && player->MovDir == bad_move_dir)
12890 return; /* player moves away from bad thing */
12895 else if (test_element == EL_PENGUIN)
12902 if (kill_x != -1 || kill_y != -1)
12904 if (IS_PLAYER(kill_x, kill_y))
12906 struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
12908 if (player->shield_deadly_time_left > 0 &&
12909 !IS_INDESTRUCTIBLE(bad_element))
12910 Bang(bad_x, bad_y);
12911 else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
12912 KillPlayer(player);
12915 Bang(kill_x, kill_y);
12919 void TestIfPlayerTouchesBadThing(int x, int y)
12921 TestIfGoodThingHitsBadThing(x, y, MV_NONE);
12924 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
12926 TestIfGoodThingHitsBadThing(x, y, move_dir);
12929 void TestIfBadThingTouchesPlayer(int x, int y)
12931 TestIfBadThingHitsGoodThing(x, y, MV_NONE);
12934 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
12936 TestIfBadThingHitsGoodThing(x, y, move_dir);
12939 void TestIfFriendTouchesBadThing(int x, int y)
12941 TestIfGoodThingHitsBadThing(x, y, MV_NONE);
12944 void TestIfBadThingTouchesFriend(int x, int y)
12946 TestIfBadThingHitsGoodThing(x, y, MV_NONE);
12949 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
12951 int i, kill_x = bad_x, kill_y = bad_y;
12952 static int xy[4][2] =
12960 for (i = 0; i < NUM_DIRECTIONS; i++)
12964 x = bad_x + xy[i][0];
12965 y = bad_y + xy[i][1];
12966 if (!IN_LEV_FIELD(x, y))
12969 element = Feld[x][y];
12970 if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
12971 element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
12979 if (kill_x != bad_x || kill_y != bad_y)
12980 Bang(bad_x, bad_y);
12983 void KillPlayer(struct PlayerInfo *player)
12985 int jx = player->jx, jy = player->jy;
12987 if (!player->active)
12991 printf("::: 0: killed == %d, active == %d, reanimated == %d\n",
12992 player->killed, player->active, player->reanimated);
12995 /* the following code was introduced to prevent an infinite loop when calling
12997 -> CheckTriggeredElementChangeExt()
12998 -> ExecuteCustomElementAction()
13000 -> (infinitely repeating the above sequence of function calls)
13001 which occurs when killing the player while having a CE with the setting
13002 "kill player X when explosion of <player X>"; the solution using a new
13003 field "player->killed" was chosen for backwards compatibility, although
13004 clever use of the fields "player->active" etc. would probably also work */
13006 if (player->killed)
13010 player->killed = TRUE;
13012 /* remove accessible field at the player's position */
13013 Feld[jx][jy] = EL_EMPTY;
13015 /* deactivate shield (else Bang()/Explode() would not work right) */
13016 player->shield_normal_time_left = 0;
13017 player->shield_deadly_time_left = 0;
13020 printf("::: 1: killed == %d, active == %d, reanimated == %d\n",
13021 player->killed, player->active, player->reanimated);
13027 printf("::: 2: killed == %d, active == %d, reanimated == %d\n",
13028 player->killed, player->active, player->reanimated);
13031 if (player->reanimated) /* killed player may have been reanimated */
13032 player->killed = player->reanimated = FALSE;
13034 BuryPlayer(player);
13037 static void KillPlayerUnlessEnemyProtected(int x, int y)
13039 if (!PLAYER_ENEMY_PROTECTED(x, y))
13040 KillPlayer(PLAYERINFO(x, y));
13043 static void KillPlayerUnlessExplosionProtected(int x, int y)
13045 if (!PLAYER_EXPLOSION_PROTECTED(x, y))
13046 KillPlayer(PLAYERINFO(x, y));
13049 void BuryPlayer(struct PlayerInfo *player)
13051 int jx = player->jx, jy = player->jy;
13053 if (!player->active)
13056 PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
13057 PlayLevelSound(jx, jy, SND_GAME_LOSING);
13059 player->GameOver = TRUE;
13060 RemovePlayer(player);
13063 void RemovePlayer(struct PlayerInfo *player)
13065 int jx = player->jx, jy = player->jy;
13066 int i, found = FALSE;
13068 player->present = FALSE;
13069 player->active = FALSE;
13071 if (!ExplodeField[jx][jy])
13072 StorePlayer[jx][jy] = 0;
13074 if (player->is_moving)
13075 TEST_DrawLevelField(player->last_jx, player->last_jy);
13077 for (i = 0; i < MAX_PLAYERS; i++)
13078 if (stored_player[i].active)
13082 AllPlayersGone = TRUE;
13088 static void setFieldForSnapping(int x, int y, int element, int direction)
13090 struct ElementInfo *ei = &element_info[element];
13091 int direction_bit = MV_DIR_TO_BIT(direction);
13092 int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
13093 int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
13094 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
13096 Feld[x][y] = EL_ELEMENT_SNAPPING;
13097 MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
13099 ResetGfxAnimation(x, y);
13101 GfxElement[x][y] = element;
13102 GfxAction[x][y] = action;
13103 GfxDir[x][y] = direction;
13104 GfxFrame[x][y] = -1;
13108 =============================================================================
13109 checkDiagonalPushing()
13110 -----------------------------------------------------------------------------
13111 check if diagonal input device direction results in pushing of object
13112 (by checking if the alternative direction is walkable, diggable, ...)
13113 =============================================================================
13116 static boolean checkDiagonalPushing(struct PlayerInfo *player,
13117 int x, int y, int real_dx, int real_dy)
13119 int jx, jy, dx, dy, xx, yy;
13121 if (real_dx == 0 || real_dy == 0) /* no diagonal direction => push */
13124 /* diagonal direction: check alternative direction */
13129 xx = jx + (dx == 0 ? real_dx : 0);
13130 yy = jy + (dy == 0 ? real_dy : 0);
13132 return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
13136 =============================================================================
13138 -----------------------------------------------------------------------------
13139 x, y: field next to player (non-diagonal) to try to dig to
13140 real_dx, real_dy: direction as read from input device (can be diagonal)
13141 =============================================================================
13144 static int DigField(struct PlayerInfo *player,
13145 int oldx, int oldy, int x, int y,
13146 int real_dx, int real_dy, int mode)
13148 boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
13149 boolean player_was_pushing = player->is_pushing;
13150 boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
13151 boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
13152 int jx = oldx, jy = oldy;
13153 int dx = x - jx, dy = y - jy;
13154 int nextx = x + dx, nexty = y + dy;
13155 int move_direction = (dx == -1 ? MV_LEFT :
13156 dx == +1 ? MV_RIGHT :
13158 dy == +1 ? MV_DOWN : MV_NONE);
13159 int opposite_direction = MV_DIR_OPPOSITE(move_direction);
13160 int dig_side = MV_DIR_OPPOSITE(move_direction);
13161 int old_element = Feld[jx][jy];
13162 int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
13165 if (is_player) /* function can also be called by EL_PENGUIN */
13167 if (player->MovPos == 0)
13169 player->is_digging = FALSE;
13170 player->is_collecting = FALSE;
13173 if (player->MovPos == 0) /* last pushing move finished */
13174 player->is_pushing = FALSE;
13176 if (mode == DF_NO_PUSH) /* player just stopped pushing */
13178 player->is_switching = FALSE;
13179 player->push_delay = -1;
13181 return MP_NO_ACTION;
13185 if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
13186 old_element = Back[jx][jy];
13188 /* in case of element dropped at player position, check background */
13189 else if (Back[jx][jy] != EL_EMPTY &&
13190 game.engine_version >= VERSION_IDENT(2,2,0,0))
13191 old_element = Back[jx][jy];
13193 if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
13194 return MP_NO_ACTION; /* field has no opening in this direction */
13196 if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
13197 return MP_NO_ACTION; /* field has no opening in this direction */
13199 if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
13203 Feld[jx][jy] = player->artwork_element;
13204 InitMovingField(jx, jy, MV_DOWN);
13205 Store[jx][jy] = EL_ACID;
13206 ContinueMoving(jx, jy);
13207 BuryPlayer(player);
13209 return MP_DONT_RUN_INTO;
13212 if (player_can_move && DONT_RUN_INTO(element))
13214 TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
13216 return MP_DONT_RUN_INTO;
13219 if (IS_MOVING(x, y) || IS_PLAYER(x, y))
13220 return MP_NO_ACTION;
13222 collect_count = element_info[element].collect_count_initial;
13224 if (!is_player && !IS_COLLECTIBLE(element)) /* penguin cannot collect it */
13225 return MP_NO_ACTION;
13227 if (game.engine_version < VERSION_IDENT(2,2,0,0))
13228 player_can_move = player_can_move_or_snap;
13230 if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
13231 game.engine_version >= VERSION_IDENT(2,2,0,0))
13233 CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
13234 player->index_bit, dig_side);
13235 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13236 player->index_bit, dig_side);
13238 if (element == EL_DC_LANDMINE)
13241 if (Feld[x][y] != element) /* field changed by snapping */
13244 return MP_NO_ACTION;
13247 if (player->gravity && is_player && !player->is_auto_moving &&
13248 canFallDown(player) && move_direction != MV_DOWN &&
13249 !canMoveToValidFieldWithGravity(jx, jy, move_direction))
13250 return MP_NO_ACTION; /* player cannot walk here due to gravity */
13252 if (player_can_move &&
13253 IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
13255 int sound_element = SND_ELEMENT(element);
13256 int sound_action = ACTION_WALKING;
13258 if (IS_RND_GATE(element))
13260 if (!player->key[RND_GATE_NR(element)])
13261 return MP_NO_ACTION;
13263 else if (IS_RND_GATE_GRAY(element))
13265 if (!player->key[RND_GATE_GRAY_NR(element)])
13266 return MP_NO_ACTION;
13268 else if (IS_RND_GATE_GRAY_ACTIVE(element))
13270 if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
13271 return MP_NO_ACTION;
13273 else if (element == EL_EXIT_OPEN ||
13274 element == EL_EM_EXIT_OPEN ||
13275 element == EL_EM_EXIT_OPENING ||
13276 element == EL_STEEL_EXIT_OPEN ||
13277 element == EL_EM_STEEL_EXIT_OPEN ||
13278 element == EL_EM_STEEL_EXIT_OPENING ||
13279 element == EL_SP_EXIT_OPEN ||
13280 element == EL_SP_EXIT_OPENING)
13282 sound_action = ACTION_PASSING; /* player is passing exit */
13284 else if (element == EL_EMPTY)
13286 sound_action = ACTION_MOVING; /* nothing to walk on */
13289 /* play sound from background or player, whatever is available */
13290 if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
13291 PlayLevelSoundElementAction(x, y, sound_element, sound_action);
13293 PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
13295 else if (player_can_move &&
13296 IS_PASSABLE(element) && canPassField(x, y, move_direction))
13298 if (!ACCESS_FROM(element, opposite_direction))
13299 return MP_NO_ACTION; /* field not accessible from this direction */
13301 if (CAN_MOVE(element)) /* only fixed elements can be passed! */
13302 return MP_NO_ACTION;
13304 if (IS_EM_GATE(element))
13306 if (!player->key[EM_GATE_NR(element)])
13307 return MP_NO_ACTION;
13309 else if (IS_EM_GATE_GRAY(element))
13311 if (!player->key[EM_GATE_GRAY_NR(element)])
13312 return MP_NO_ACTION;
13314 else if (IS_EM_GATE_GRAY_ACTIVE(element))
13316 if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
13317 return MP_NO_ACTION;
13319 else if (IS_EMC_GATE(element))
13321 if (!player->key[EMC_GATE_NR(element)])
13322 return MP_NO_ACTION;
13324 else if (IS_EMC_GATE_GRAY(element))
13326 if (!player->key[EMC_GATE_GRAY_NR(element)])
13327 return MP_NO_ACTION;
13329 else if (IS_EMC_GATE_GRAY_ACTIVE(element))
13331 if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
13332 return MP_NO_ACTION;
13334 else if (element == EL_DC_GATE_WHITE ||
13335 element == EL_DC_GATE_WHITE_GRAY ||
13336 element == EL_DC_GATE_WHITE_GRAY_ACTIVE)
13338 if (player->num_white_keys == 0)
13339 return MP_NO_ACTION;
13341 player->num_white_keys--;
13343 else if (IS_SP_PORT(element))
13345 if (element == EL_SP_GRAVITY_PORT_LEFT ||
13346 element == EL_SP_GRAVITY_PORT_RIGHT ||
13347 element == EL_SP_GRAVITY_PORT_UP ||
13348 element == EL_SP_GRAVITY_PORT_DOWN)
13349 player->gravity = !player->gravity;
13350 else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
13351 element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
13352 element == EL_SP_GRAVITY_ON_PORT_UP ||
13353 element == EL_SP_GRAVITY_ON_PORT_DOWN)
13354 player->gravity = TRUE;
13355 else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
13356 element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
13357 element == EL_SP_GRAVITY_OFF_PORT_UP ||
13358 element == EL_SP_GRAVITY_OFF_PORT_DOWN)
13359 player->gravity = FALSE;
13362 /* automatically move to the next field with double speed */
13363 player->programmed_action = move_direction;
13365 if (player->move_delay_reset_counter == 0)
13367 player->move_delay_reset_counter = 2; /* two double speed steps */
13369 DOUBLE_PLAYER_SPEED(player);
13372 PlayLevelSoundAction(x, y, ACTION_PASSING);
13374 else if (player_can_move_or_snap && IS_DIGGABLE(element))
13378 if (mode != DF_SNAP)
13380 GfxElement[x][y] = GFX_ELEMENT(element);
13381 player->is_digging = TRUE;
13384 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
13386 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
13387 player->index_bit, dig_side);
13389 if (mode == DF_SNAP)
13391 if (level.block_snap_field)
13392 setFieldForSnapping(x, y, element, move_direction);
13394 TestIfElementTouchesCustomElement(x, y); /* for empty space */
13396 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13397 player->index_bit, dig_side);
13400 else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
13404 if (is_player && mode != DF_SNAP)
13406 GfxElement[x][y] = element;
13407 player->is_collecting = TRUE;
13410 if (element == EL_SPEED_PILL)
13412 player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
13414 else if (element == EL_EXTRA_TIME && level.time > 0)
13416 TimeLeft += level.extra_time;
13418 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
13420 DisplayGameControlValues();
13422 else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
13424 player->shield_normal_time_left += level.shield_normal_time;
13425 if (element == EL_SHIELD_DEADLY)
13426 player->shield_deadly_time_left += level.shield_deadly_time;
13428 else if (element == EL_DYNAMITE ||
13429 element == EL_EM_DYNAMITE ||
13430 element == EL_SP_DISK_RED)
13432 if (player->inventory_size < MAX_INVENTORY_SIZE)
13433 player->inventory_element[player->inventory_size++] = element;
13435 DrawGameDoorValues();
13437 else if (element == EL_DYNABOMB_INCREASE_NUMBER)
13439 player->dynabomb_count++;
13440 player->dynabombs_left++;
13442 else if (element == EL_DYNABOMB_INCREASE_SIZE)
13444 player->dynabomb_size++;
13446 else if (element == EL_DYNABOMB_INCREASE_POWER)
13448 player->dynabomb_xl = TRUE;
13450 else if (IS_KEY(element))
13452 player->key[KEY_NR(element)] = TRUE;
13454 DrawGameDoorValues();
13456 else if (element == EL_DC_KEY_WHITE)
13458 player->num_white_keys++;
13460 /* display white keys? */
13461 /* DrawGameDoorValues(); */
13463 else if (IS_ENVELOPE(element))
13465 player->show_envelope = element;
13467 else if (element == EL_EMC_LENSES)
13469 game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
13471 RedrawAllInvisibleElementsForLenses();
13473 else if (element == EL_EMC_MAGNIFIER)
13475 game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
13477 RedrawAllInvisibleElementsForMagnifier();
13479 else if (IS_DROPPABLE(element) ||
13480 IS_THROWABLE(element)) /* can be collected and dropped */
13484 if (collect_count == 0)
13485 player->inventory_infinite_element = element;
13487 for (i = 0; i < collect_count; i++)
13488 if (player->inventory_size < MAX_INVENTORY_SIZE)
13489 player->inventory_element[player->inventory_size++] = element;
13491 DrawGameDoorValues();
13493 else if (collect_count > 0)
13495 local_player->gems_still_needed -= collect_count;
13496 if (local_player->gems_still_needed < 0)
13497 local_player->gems_still_needed = 0;
13499 game.snapshot.collected_item = TRUE;
13501 game_panel_controls[GAME_PANEL_GEMS].value = local_player->gems_still_needed;
13503 DisplayGameControlValues();
13506 RaiseScoreElement(element);
13507 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
13510 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
13511 player->index_bit, dig_side);
13513 if (mode == DF_SNAP)
13515 if (level.block_snap_field)
13516 setFieldForSnapping(x, y, element, move_direction);
13518 TestIfElementTouchesCustomElement(x, y); /* for empty space */
13520 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13521 player->index_bit, dig_side);
13524 else if (player_can_move_or_snap && IS_PUSHABLE(element))
13526 if (mode == DF_SNAP && element != EL_BD_ROCK)
13527 return MP_NO_ACTION;
13529 if (CAN_FALL(element) && dy)
13530 return MP_NO_ACTION;
13532 if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
13533 !(element == EL_SPRING && level.use_spring_bug))
13534 return MP_NO_ACTION;
13536 if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
13537 ((move_direction & MV_VERTICAL &&
13538 ((element_info[element].move_pattern & MV_LEFT &&
13539 IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
13540 (element_info[element].move_pattern & MV_RIGHT &&
13541 IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
13542 (move_direction & MV_HORIZONTAL &&
13543 ((element_info[element].move_pattern & MV_UP &&
13544 IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
13545 (element_info[element].move_pattern & MV_DOWN &&
13546 IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
13547 return MP_NO_ACTION;
13549 /* do not push elements already moving away faster than player */
13550 if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
13551 ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
13552 return MP_NO_ACTION;
13554 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
13556 if (player->push_delay_value == -1 || !player_was_pushing)
13557 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13559 else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
13561 if (player->push_delay_value == -1)
13562 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13564 else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
13566 if (!player->is_pushing)
13567 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13570 player->is_pushing = TRUE;
13571 player->is_active = TRUE;
13573 if (!(IN_LEV_FIELD(nextx, nexty) &&
13574 (IS_FREE(nextx, nexty) ||
13575 (IS_SB_ELEMENT(element) &&
13576 Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY) ||
13577 (IS_CUSTOM_ELEMENT(element) &&
13578 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty)))))
13579 return MP_NO_ACTION;
13581 if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
13582 return MP_NO_ACTION;
13584 if (player->push_delay == -1) /* new pushing; restart delay */
13585 player->push_delay = 0;
13587 if (player->push_delay < player->push_delay_value &&
13588 !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
13589 element != EL_SPRING && element != EL_BALLOON)
13591 /* make sure that there is no move delay before next try to push */
13592 if (game.engine_version >= VERSION_IDENT(3,0,7,1))
13593 player->move_delay = 0;
13595 return MP_NO_ACTION;
13598 if (IS_CUSTOM_ELEMENT(element) &&
13599 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty))
13601 if (!DigFieldByCE(nextx, nexty, element))
13602 return MP_NO_ACTION;
13605 if (IS_SB_ELEMENT(element))
13607 if (element == EL_SOKOBAN_FIELD_FULL)
13609 Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
13610 local_player->sokobanfields_still_needed++;
13613 if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
13615 Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
13616 local_player->sokobanfields_still_needed--;
13619 Feld[x][y] = EL_SOKOBAN_OBJECT;
13621 if (Back[x][y] == Back[nextx][nexty])
13622 PlayLevelSoundAction(x, y, ACTION_PUSHING);
13623 else if (Back[x][y] != 0)
13624 PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
13627 PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
13630 if (local_player->sokobanfields_still_needed == 0 &&
13631 (game.emulation == EMU_SOKOBAN || level.auto_exit_sokoban))
13633 PlayerWins(player);
13635 PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
13639 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
13641 InitMovingField(x, y, move_direction);
13642 GfxAction[x][y] = ACTION_PUSHING;
13644 if (mode == DF_SNAP)
13645 ContinueMoving(x, y);
13647 MovPos[x][y] = (dx != 0 ? dx : dy);
13649 Pushed[x][y] = TRUE;
13650 Pushed[nextx][nexty] = TRUE;
13652 if (game.engine_version < VERSION_IDENT(2,2,0,7))
13653 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13655 player->push_delay_value = -1; /* get new value later */
13657 /* check for element change _after_ element has been pushed */
13658 if (game.use_change_when_pushing_bug)
13660 CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
13661 player->index_bit, dig_side);
13662 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
13663 player->index_bit, dig_side);
13666 else if (IS_SWITCHABLE(element))
13668 if (PLAYER_SWITCHING(player, x, y))
13670 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
13671 player->index_bit, dig_side);
13676 player->is_switching = TRUE;
13677 player->switch_x = x;
13678 player->switch_y = y;
13680 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
13682 if (element == EL_ROBOT_WHEEL)
13684 Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
13688 game.robot_wheel_active = TRUE;
13690 TEST_DrawLevelField(x, y);
13692 else if (element == EL_SP_TERMINAL)
13696 SCAN_PLAYFIELD(xx, yy)
13698 if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
13702 else if (Feld[xx][yy] == EL_SP_TERMINAL)
13704 Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
13706 ResetGfxAnimation(xx, yy);
13707 TEST_DrawLevelField(xx, yy);
13711 else if (IS_BELT_SWITCH(element))
13713 ToggleBeltSwitch(x, y);
13715 else if (element == EL_SWITCHGATE_SWITCH_UP ||
13716 element == EL_SWITCHGATE_SWITCH_DOWN ||
13717 element == EL_DC_SWITCHGATE_SWITCH_UP ||
13718 element == EL_DC_SWITCHGATE_SWITCH_DOWN)
13720 ToggleSwitchgateSwitch(x, y);
13722 else if (element == EL_LIGHT_SWITCH ||
13723 element == EL_LIGHT_SWITCH_ACTIVE)
13725 ToggleLightSwitch(x, y);
13727 else if (element == EL_TIMEGATE_SWITCH ||
13728 element == EL_DC_TIMEGATE_SWITCH)
13730 ActivateTimegateSwitch(x, y);
13732 else if (element == EL_BALLOON_SWITCH_LEFT ||
13733 element == EL_BALLOON_SWITCH_RIGHT ||
13734 element == EL_BALLOON_SWITCH_UP ||
13735 element == EL_BALLOON_SWITCH_DOWN ||
13736 element == EL_BALLOON_SWITCH_NONE ||
13737 element == EL_BALLOON_SWITCH_ANY)
13739 game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT ? MV_LEFT :
13740 element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
13741 element == EL_BALLOON_SWITCH_UP ? MV_UP :
13742 element == EL_BALLOON_SWITCH_DOWN ? MV_DOWN :
13743 element == EL_BALLOON_SWITCH_NONE ? MV_NONE :
13746 else if (element == EL_LAMP)
13748 Feld[x][y] = EL_LAMP_ACTIVE;
13749 local_player->lights_still_needed--;
13751 ResetGfxAnimation(x, y);
13752 TEST_DrawLevelField(x, y);
13754 else if (element == EL_TIME_ORB_FULL)
13756 Feld[x][y] = EL_TIME_ORB_EMPTY;
13758 if (level.time > 0 || level.use_time_orb_bug)
13760 TimeLeft += level.time_orb_time;
13761 game.no_time_limit = FALSE;
13763 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
13765 DisplayGameControlValues();
13768 ResetGfxAnimation(x, y);
13769 TEST_DrawLevelField(x, y);
13771 else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
13772 element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
13776 game.ball_state = !game.ball_state;
13778 SCAN_PLAYFIELD(xx, yy)
13780 int e = Feld[xx][yy];
13782 if (game.ball_state)
13784 if (e == EL_EMC_MAGIC_BALL)
13785 CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
13786 else if (e == EL_EMC_MAGIC_BALL_SWITCH)
13787 CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
13791 if (e == EL_EMC_MAGIC_BALL_ACTIVE)
13792 CreateField(xx, yy, EL_EMC_MAGIC_BALL);
13793 else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
13794 CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
13799 CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
13800 player->index_bit, dig_side);
13802 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
13803 player->index_bit, dig_side);
13805 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
13806 player->index_bit, dig_side);
13812 if (!PLAYER_SWITCHING(player, x, y))
13814 player->is_switching = TRUE;
13815 player->switch_x = x;
13816 player->switch_y = y;
13818 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
13819 player->index_bit, dig_side);
13820 CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
13821 player->index_bit, dig_side);
13823 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
13824 player->index_bit, dig_side);
13825 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
13826 player->index_bit, dig_side);
13829 CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
13830 player->index_bit, dig_side);
13831 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
13832 player->index_bit, dig_side);
13834 return MP_NO_ACTION;
13837 player->push_delay = -1;
13839 if (is_player) /* function can also be called by EL_PENGUIN */
13841 if (Feld[x][y] != element) /* really digged/collected something */
13843 player->is_collecting = !player->is_digging;
13844 player->is_active = TRUE;
13851 static boolean DigFieldByCE(int x, int y, int digging_element)
13853 int element = Feld[x][y];
13855 if (!IS_FREE(x, y))
13857 int action = (IS_DIGGABLE(element) ? ACTION_DIGGING :
13858 IS_COLLECTIBLE(element) ? ACTION_COLLECTING :
13861 /* no element can dig solid indestructible elements */
13862 if (IS_INDESTRUCTIBLE(element) &&
13863 !IS_DIGGABLE(element) &&
13864 !IS_COLLECTIBLE(element))
13867 if (AmoebaNr[x][y] &&
13868 (element == EL_AMOEBA_FULL ||
13869 element == EL_BD_AMOEBA ||
13870 element == EL_AMOEBA_GROWING))
13872 AmoebaCnt[AmoebaNr[x][y]]--;
13873 AmoebaCnt2[AmoebaNr[x][y]]--;
13876 if (IS_MOVING(x, y))
13877 RemoveMovingField(x, y);
13881 TEST_DrawLevelField(x, y);
13884 /* if digged element was about to explode, prevent the explosion */
13885 ExplodeField[x][y] = EX_TYPE_NONE;
13887 PlayLevelSoundAction(x, y, action);
13890 Store[x][y] = EL_EMPTY;
13892 /* this makes it possible to leave the removed element again */
13893 if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element)))
13894 Store[x][y] = element;
13899 static boolean SnapField(struct PlayerInfo *player, int dx, int dy)
13901 int jx = player->jx, jy = player->jy;
13902 int x = jx + dx, y = jy + dy;
13903 int snap_direction = (dx == -1 ? MV_LEFT :
13904 dx == +1 ? MV_RIGHT :
13906 dy == +1 ? MV_DOWN : MV_NONE);
13907 boolean can_continue_snapping = (level.continuous_snapping &&
13908 WasJustFalling[x][y] < CHECK_DELAY_FALLING);
13910 if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
13913 if (!player->active || !IN_LEV_FIELD(x, y))
13921 if (player->MovPos == 0)
13922 player->is_pushing = FALSE;
13924 player->is_snapping = FALSE;
13926 if (player->MovPos == 0)
13928 player->is_moving = FALSE;
13929 player->is_digging = FALSE;
13930 player->is_collecting = FALSE;
13936 /* prevent snapping with already pressed snap key when not allowed */
13937 if (player->is_snapping && !can_continue_snapping)
13940 player->MovDir = snap_direction;
13942 if (player->MovPos == 0)
13944 player->is_moving = FALSE;
13945 player->is_digging = FALSE;
13946 player->is_collecting = FALSE;
13949 player->is_dropping = FALSE;
13950 player->is_dropping_pressed = FALSE;
13951 player->drop_pressed_delay = 0;
13953 if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
13956 player->is_snapping = TRUE;
13957 player->is_active = TRUE;
13959 if (player->MovPos == 0)
13961 player->is_moving = FALSE;
13962 player->is_digging = FALSE;
13963 player->is_collecting = FALSE;
13966 if (player->MovPos != 0) /* prevent graphic bugs in versions < 2.2.0 */
13967 TEST_DrawLevelField(player->last_jx, player->last_jy);
13969 TEST_DrawLevelField(x, y);
13974 static boolean DropElement(struct PlayerInfo *player)
13976 int old_element, new_element;
13977 int dropx = player->jx, dropy = player->jy;
13978 int drop_direction = player->MovDir;
13979 int drop_side = drop_direction;
13980 int drop_element = get_next_dropped_element(player);
13982 player->is_dropping_pressed = TRUE;
13984 /* do not drop an element on top of another element; when holding drop key
13985 pressed without moving, dropped element must move away before the next
13986 element can be dropped (this is especially important if the next element
13987 is dynamite, which can be placed on background for historical reasons) */
13988 if (PLAYER_DROPPING(player, dropx, dropy) && Feld[dropx][dropy] != EL_EMPTY)
13991 if (IS_THROWABLE(drop_element))
13993 dropx += GET_DX_FROM_DIR(drop_direction);
13994 dropy += GET_DY_FROM_DIR(drop_direction);
13996 if (!IN_LEV_FIELD(dropx, dropy))
14000 old_element = Feld[dropx][dropy]; /* old element at dropping position */
14001 new_element = drop_element; /* default: no change when dropping */
14003 /* check if player is active, not moving and ready to drop */
14004 if (!player->active || player->MovPos || player->drop_delay > 0)
14007 /* check if player has anything that can be dropped */
14008 if (new_element == EL_UNDEFINED)
14011 /* check if drop key was pressed long enough for EM style dynamite */
14012 if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
14015 /* check if anything can be dropped at the current position */
14016 if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
14019 /* collected custom elements can only be dropped on empty fields */
14020 if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
14023 if (old_element != EL_EMPTY)
14024 Back[dropx][dropy] = old_element; /* store old element on this field */
14026 ResetGfxAnimation(dropx, dropy);
14027 ResetRandomAnimationValue(dropx, dropy);
14029 if (player->inventory_size > 0 ||
14030 player->inventory_infinite_element != EL_UNDEFINED)
14032 if (player->inventory_size > 0)
14034 player->inventory_size--;
14036 DrawGameDoorValues();
14038 if (new_element == EL_DYNAMITE)
14039 new_element = EL_DYNAMITE_ACTIVE;
14040 else if (new_element == EL_EM_DYNAMITE)
14041 new_element = EL_EM_DYNAMITE_ACTIVE;
14042 else if (new_element == EL_SP_DISK_RED)
14043 new_element = EL_SP_DISK_RED_ACTIVE;
14046 Feld[dropx][dropy] = new_element;
14048 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
14049 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
14050 el2img(Feld[dropx][dropy]), 0);
14052 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
14054 /* needed if previous element just changed to "empty" in the last frame */
14055 ChangeCount[dropx][dropy] = 0; /* allow at least one more change */
14057 CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
14058 player->index_bit, drop_side);
14059 CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
14061 player->index_bit, drop_side);
14063 TestIfElementTouchesCustomElement(dropx, dropy);
14065 else /* player is dropping a dyna bomb */
14067 player->dynabombs_left--;
14069 Feld[dropx][dropy] = new_element;
14071 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
14072 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
14073 el2img(Feld[dropx][dropy]), 0);
14075 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
14078 if (Feld[dropx][dropy] == new_element) /* uninitialized unless CE change */
14079 InitField_WithBug1(dropx, dropy, FALSE);
14081 new_element = Feld[dropx][dropy]; /* element might have changed */
14083 if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
14084 element_info[new_element].move_pattern == MV_WHEN_DROPPED)
14086 if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
14087 MovDir[dropx][dropy] = drop_direction;
14089 ChangeCount[dropx][dropy] = 0; /* allow at least one more change */
14091 /* do not cause impact style collision by dropping elements that can fall */
14092 CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
14095 player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
14096 player->is_dropping = TRUE;
14098 player->drop_pressed_delay = 0;
14099 player->is_dropping_pressed = FALSE;
14101 player->drop_x = dropx;
14102 player->drop_y = dropy;
14107 /* ------------------------------------------------------------------------- */
14108 /* game sound playing functions */
14109 /* ------------------------------------------------------------------------- */
14111 static int *loop_sound_frame = NULL;
14112 static int *loop_sound_volume = NULL;
14114 void InitPlayLevelSound()
14116 int num_sounds = getSoundListSize();
14118 checked_free(loop_sound_frame);
14119 checked_free(loop_sound_volume);
14121 loop_sound_frame = checked_calloc(num_sounds * sizeof(int));
14122 loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
14125 static void PlayLevelSound(int x, int y, int nr)
14127 int sx = SCREENX(x), sy = SCREENY(y);
14128 int volume, stereo_position;
14129 int max_distance = 8;
14130 int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
14132 if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
14133 (!setup.sound_loops && IS_LOOP_SOUND(nr)))
14136 if (!IN_LEV_FIELD(x, y) ||
14137 sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
14138 sy < -max_distance || sy >= SCR_FIELDY + max_distance)
14141 volume = SOUND_MAX_VOLUME;
14143 if (!IN_SCR_FIELD(sx, sy))
14145 int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
14146 int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
14148 volume -= volume * (dx > dy ? dx : dy) / max_distance;
14151 stereo_position = (SOUND_MAX_LEFT +
14152 (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
14153 (SCR_FIELDX + 2 * max_distance));
14155 if (IS_LOOP_SOUND(nr))
14157 /* This assures that quieter loop sounds do not overwrite louder ones,
14158 while restarting sound volume comparison with each new game frame. */
14160 if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
14163 loop_sound_volume[nr] = volume;
14164 loop_sound_frame[nr] = FrameCounter;
14167 PlaySoundExt(nr, volume, stereo_position, type);
14170 static void PlayLevelSoundNearest(int x, int y, int sound_action)
14172 PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
14173 x > LEVELX(BX2) ? LEVELX(BX2) : x,
14174 y < LEVELY(BY1) ? LEVELY(BY1) :
14175 y > LEVELY(BY2) ? LEVELY(BY2) : y,
14179 static void PlayLevelSoundAction(int x, int y, int action)
14181 PlayLevelSoundElementAction(x, y, Feld[x][y], action);
14184 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
14186 int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
14188 if (sound_effect != SND_UNDEFINED)
14189 PlayLevelSound(x, y, sound_effect);
14192 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
14195 int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
14197 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14198 PlayLevelSound(x, y, sound_effect);
14201 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
14203 int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
14205 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14206 PlayLevelSound(x, y, sound_effect);
14209 static void StopLevelSoundActionIfLoop(int x, int y, int action)
14211 int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
14213 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14214 StopSound(sound_effect);
14217 static void PlayLevelMusic()
14219 if (levelset.music[level_nr] != MUS_UNDEFINED)
14220 PlayMusic(levelset.music[level_nr]); /* from config file */
14222 PlayMusic(MAP_NOCONF_MUSIC(level_nr)); /* from music dir */
14225 void PlayLevelSound_EM(int xx, int yy, int element_em, int sample)
14227 int element = (element_em > -1 ? map_element_EM_to_RND(element_em) : 0);
14228 int offset = (BorderElement == EL_STEELWALL ? 1 : 0);
14229 int x = xx - 1 - offset;
14230 int y = yy - 1 - offset;
14235 PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
14239 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14243 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14247 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14251 PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
14255 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14259 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14262 case SAMPLE_android_clone:
14263 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14266 case SAMPLE_android_move:
14267 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14270 case SAMPLE_spring:
14271 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14275 PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
14279 PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
14282 case SAMPLE_eater_eat:
14283 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14287 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14290 case SAMPLE_collect:
14291 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
14294 case SAMPLE_diamond:
14295 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14298 case SAMPLE_squash:
14299 /* !!! CHECK THIS !!! */
14301 PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
14303 PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
14307 case SAMPLE_wonderfall:
14308 PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
14312 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14316 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14320 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14324 PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
14328 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14332 PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
14335 case SAMPLE_wonder:
14336 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14340 PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
14343 case SAMPLE_exit_open:
14344 PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
14347 case SAMPLE_exit_leave:
14348 PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
14351 case SAMPLE_dynamite:
14352 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14356 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14360 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
14364 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14368 PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
14372 PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
14376 PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
14380 PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
14385 void PlayLevelSound_SP(int xx, int yy, int element_sp, int action_sp)
14387 int element = map_element_SP_to_RND(element_sp);
14388 int action = map_action_SP_to_RND(action_sp);
14389 int offset = (setup.sp_show_border_elements ? 0 : 1);
14390 int x = xx - offset;
14391 int y = yy - offset;
14393 PlayLevelSoundElementAction(x, y, element, action);
14396 void RaiseScore(int value)
14398 local_player->score += value;
14400 game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
14402 DisplayGameControlValues();
14405 void RaiseScoreElement(int element)
14410 case EL_BD_DIAMOND:
14411 case EL_EMERALD_YELLOW:
14412 case EL_EMERALD_RED:
14413 case EL_EMERALD_PURPLE:
14414 case EL_SP_INFOTRON:
14415 RaiseScore(level.score[SC_EMERALD]);
14418 RaiseScore(level.score[SC_DIAMOND]);
14421 RaiseScore(level.score[SC_CRYSTAL]);
14424 RaiseScore(level.score[SC_PEARL]);
14427 case EL_BD_BUTTERFLY:
14428 case EL_SP_ELECTRON:
14429 RaiseScore(level.score[SC_BUG]);
14432 case EL_BD_FIREFLY:
14433 case EL_SP_SNIKSNAK:
14434 RaiseScore(level.score[SC_SPACESHIP]);
14437 case EL_DARK_YAMYAM:
14438 RaiseScore(level.score[SC_YAMYAM]);
14441 RaiseScore(level.score[SC_ROBOT]);
14444 RaiseScore(level.score[SC_PACMAN]);
14447 RaiseScore(level.score[SC_NUT]);
14450 case EL_EM_DYNAMITE:
14451 case EL_SP_DISK_RED:
14452 case EL_DYNABOMB_INCREASE_NUMBER:
14453 case EL_DYNABOMB_INCREASE_SIZE:
14454 case EL_DYNABOMB_INCREASE_POWER:
14455 RaiseScore(level.score[SC_DYNAMITE]);
14457 case EL_SHIELD_NORMAL:
14458 case EL_SHIELD_DEADLY:
14459 RaiseScore(level.score[SC_SHIELD]);
14461 case EL_EXTRA_TIME:
14462 RaiseScore(level.extra_time_score);
14476 case EL_DC_KEY_WHITE:
14477 RaiseScore(level.score[SC_KEY]);
14480 RaiseScore(element_info[element].collect_score);
14485 void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message)
14487 if (skip_request || Request(message, REQ_ASK | REQ_STAY_CLOSED))
14489 /* closing door required in case of envelope style request dialogs */
14491 CloseDoor(DOOR_CLOSE_1);
14493 #if defined(NETWORK_AVALIABLE)
14494 if (options.network)
14495 SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
14500 FadeSkipNextFadeIn();
14502 SetGameStatus(GAME_MODE_MAIN);
14507 else /* continue playing the game */
14509 if (tape.playing && tape.deactivate_display)
14510 TapeDeactivateDisplayOff(TRUE);
14512 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
14514 if (tape.playing && tape.deactivate_display)
14515 TapeDeactivateDisplayOn();
14519 void RequestQuitGame(boolean ask_if_really_quit)
14521 boolean quick_quit = (!ask_if_really_quit || level_editor_test_game);
14522 boolean skip_request = AllPlayersGone || quick_quit;
14524 RequestQuitGameExt(skip_request, quick_quit,
14525 "Do you really want to quit the game?");
14529 /* ------------------------------------------------------------------------- */
14530 /* random generator functions */
14531 /* ------------------------------------------------------------------------- */
14533 unsigned int InitEngineRandom_RND(int seed)
14535 game.num_random_calls = 0;
14537 return InitEngineRandom(seed);
14540 unsigned int RND(int max)
14544 game.num_random_calls++;
14546 return GetEngineRandom(max);
14553 /* ------------------------------------------------------------------------- */
14554 /* game engine snapshot handling functions */
14555 /* ------------------------------------------------------------------------- */
14557 struct EngineSnapshotInfo
14559 /* runtime values for custom element collect score */
14560 int collect_score[NUM_CUSTOM_ELEMENTS];
14562 /* runtime values for group element choice position */
14563 int choice_pos[NUM_GROUP_ELEMENTS];
14565 /* runtime values for belt position animations */
14566 int belt_graphic[4][NUM_BELT_PARTS];
14567 int belt_anim_mode[4][NUM_BELT_PARTS];
14570 static struct EngineSnapshotInfo engine_snapshot_rnd;
14571 static char *snapshot_level_identifier = NULL;
14572 static int snapshot_level_nr = -1;
14574 static void SaveEngineSnapshotValues_RND()
14576 static int belt_base_active_element[4] =
14578 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
14579 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
14580 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
14581 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
14585 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
14587 int element = EL_CUSTOM_START + i;
14589 engine_snapshot_rnd.collect_score[i] = element_info[element].collect_score;
14592 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
14594 int element = EL_GROUP_START + i;
14596 engine_snapshot_rnd.choice_pos[i] = element_info[element].group->choice_pos;
14599 for (i = 0; i < 4; i++)
14601 for (j = 0; j < NUM_BELT_PARTS; j++)
14603 int element = belt_base_active_element[i] + j;
14604 int graphic = el2img(element);
14605 int anim_mode = graphic_info[graphic].anim_mode;
14607 engine_snapshot_rnd.belt_graphic[i][j] = graphic;
14608 engine_snapshot_rnd.belt_anim_mode[i][j] = anim_mode;
14613 static void LoadEngineSnapshotValues_RND()
14615 unsigned int num_random_calls = game.num_random_calls;
14618 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
14620 int element = EL_CUSTOM_START + i;
14622 element_info[element].collect_score = engine_snapshot_rnd.collect_score[i];
14625 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
14627 int element = EL_GROUP_START + i;
14629 element_info[element].group->choice_pos = engine_snapshot_rnd.choice_pos[i];
14632 for (i = 0; i < 4; i++)
14634 for (j = 0; j < NUM_BELT_PARTS; j++)
14636 int graphic = engine_snapshot_rnd.belt_graphic[i][j];
14637 int anim_mode = engine_snapshot_rnd.belt_anim_mode[i][j];
14639 graphic_info[graphic].anim_mode = anim_mode;
14643 if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
14645 InitRND(tape.random_seed);
14646 for (i = 0; i < num_random_calls; i++)
14650 if (game.num_random_calls != num_random_calls)
14652 Error(ERR_INFO, "number of random calls out of sync");
14653 Error(ERR_INFO, "number of random calls should be %d", num_random_calls);
14654 Error(ERR_INFO, "number of random calls is %d", game.num_random_calls);
14655 Error(ERR_EXIT, "this should not happen -- please debug");
14659 void FreeEngineSnapshotSingle()
14661 FreeSnapshotSingle();
14663 setString(&snapshot_level_identifier, NULL);
14664 snapshot_level_nr = -1;
14667 void FreeEngineSnapshotList()
14669 FreeSnapshotList();
14672 ListNode *SaveEngineSnapshotBuffers()
14674 ListNode *buffers = NULL;
14676 /* copy some special values to a structure better suited for the snapshot */
14678 if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
14679 SaveEngineSnapshotValues_RND();
14680 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
14681 SaveEngineSnapshotValues_EM();
14682 if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
14683 SaveEngineSnapshotValues_SP(&buffers);
14685 /* save values stored in special snapshot structure */
14687 if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
14688 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
14689 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
14690 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
14691 if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
14692 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_sp));
14694 /* save further RND engine values */
14696 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(stored_player));
14697 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(game));
14698 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(tape));
14700 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ZX));
14701 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ZY));
14702 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExitX));
14703 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExitY));
14705 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(FrameCounter));
14706 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeFrames));
14707 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimePlayed));
14708 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeLeft));
14709 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TapeTime));
14711 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovDir));
14712 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovPos));
14713 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenGfxPos));
14715 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScrollStepSize));
14717 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AllPlayersGone));
14719 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt));
14720 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2));
14722 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Feld));
14723 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovPos));
14724 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDir));
14725 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDelay));
14726 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeDelay));
14727 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangePage));
14728 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CustomValue));
14729 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store));
14730 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store2));
14731 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(StorePlayer));
14732 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Back));
14733 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaNr));
14734 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustMoving));
14735 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustFalling));
14736 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckCollision));
14737 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckImpact));
14738 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Stop));
14739 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Pushed));
14741 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeCount));
14742 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeEvent));
14744 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodePhase));
14745 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeDelay));
14746 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeField));
14748 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(RunnerVisit));
14749 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(PlayerVisit));
14751 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxFrame));
14752 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxRandom));
14753 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxElement));
14754 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxAction));
14755 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxDir));
14757 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_x));
14758 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_y));
14761 ListNode *node = engine_snapshot_list_rnd;
14764 while (node != NULL)
14766 num_bytes += ((struct EngineSnapshotNodeInfo *)node->content)->size;
14771 printf("::: size of engine snapshot: %d bytes\n", num_bytes);
14777 void SaveEngineSnapshotSingle()
14779 ListNode *buffers = SaveEngineSnapshotBuffers();
14781 /* finally save all snapshot buffers to single snapshot */
14782 SaveSnapshotSingle(buffers);
14784 /* save level identification information */
14785 setString(&snapshot_level_identifier, leveldir_current->identifier);
14786 snapshot_level_nr = level_nr;
14789 boolean CheckSaveEngineSnapshotToList()
14791 boolean save_snapshot =
14792 ((game.snapshot.mode == SNAPSHOT_MODE_EVERY_STEP) ||
14793 (game.snapshot.mode == SNAPSHOT_MODE_EVERY_MOVE &&
14794 game.snapshot.changed_action) ||
14795 (game.snapshot.mode == SNAPSHOT_MODE_EVERY_COLLECT &&
14796 game.snapshot.collected_item));
14798 game.snapshot.changed_action = FALSE;
14799 game.snapshot.collected_item = FALSE;
14800 game.snapshot.save_snapshot = save_snapshot;
14802 return save_snapshot;
14805 void SaveEngineSnapshotToList()
14807 if (game.snapshot.mode == SNAPSHOT_MODE_OFF ||
14811 ListNode *buffers = SaveEngineSnapshotBuffers();
14813 /* finally save all snapshot buffers to snapshot list */
14814 SaveSnapshotToList(buffers);
14817 void SaveEngineSnapshotToListInitial()
14819 FreeEngineSnapshotList();
14821 SaveEngineSnapshotToList();
14824 void LoadEngineSnapshotValues()
14826 /* restore special values from snapshot structure */
14828 if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
14829 LoadEngineSnapshotValues_RND();
14830 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
14831 LoadEngineSnapshotValues_EM();
14832 if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
14833 LoadEngineSnapshotValues_SP();
14836 void LoadEngineSnapshotSingle()
14838 LoadSnapshotSingle();
14840 LoadEngineSnapshotValues();
14843 void LoadEngineSnapshot_Undo(int steps)
14845 LoadSnapshotFromList_Older(steps);
14847 LoadEngineSnapshotValues();
14850 void LoadEngineSnapshot_Redo(int steps)
14852 LoadSnapshotFromList_Newer(steps);
14854 LoadEngineSnapshotValues();
14857 boolean CheckEngineSnapshotSingle()
14859 return (strEqual(snapshot_level_identifier, leveldir_current->identifier) &&
14860 snapshot_level_nr == level_nr);
14863 boolean CheckEngineSnapshotList()
14865 return CheckSnapshotList();
14869 /* ---------- new game button stuff ---------------------------------------- */
14877 } gamebutton_info[NUM_GAME_BUTTONS] =
14880 IMG_GFX_GAME_BUTTON_STOP, &game.button.stop,
14881 GAME_CTRL_ID_STOP, "stop game"
14884 IMG_GFX_GAME_BUTTON_PAUSE, &game.button.pause,
14885 GAME_CTRL_ID_PAUSE, "pause game"
14888 IMG_GFX_GAME_BUTTON_PLAY, &game.button.play,
14889 GAME_CTRL_ID_PLAY, "play game"
14892 IMG_GFX_GAME_BUTTON_UNDO, &game.button.undo,
14893 GAME_CTRL_ID_UNDO, "undo step"
14896 IMG_GFX_GAME_BUTTON_REDO, &game.button.redo,
14897 GAME_CTRL_ID_REDO, "redo step"
14900 IMG_GFX_GAME_BUTTON_SAVE, &game.button.save,
14901 GAME_CTRL_ID_SAVE, "save game"
14904 IMG_GFX_GAME_BUTTON_PAUSE2, &game.button.pause2,
14905 GAME_CTRL_ID_PAUSE2, "pause game"
14908 IMG_GFX_GAME_BUTTON_LOAD, &game.button.load,
14909 GAME_CTRL_ID_LOAD, "load game"
14912 IMG_GFX_GAME_BUTTON_SOUND_MUSIC, &game.button.sound_music,
14913 SOUND_CTRL_ID_MUSIC, "background music on/off"
14916 IMG_GFX_GAME_BUTTON_SOUND_LOOPS, &game.button.sound_loops,
14917 SOUND_CTRL_ID_LOOPS, "sound loops on/off"
14920 IMG_GFX_GAME_BUTTON_SOUND_SIMPLE, &game.button.sound_simple,
14921 SOUND_CTRL_ID_SIMPLE, "normal sounds on/off"
14925 void CreateGameButtons()
14929 for (i = 0; i < NUM_GAME_BUTTONS; i++)
14931 struct GraphicInfo *gfx = &graphic_info[gamebutton_info[i].graphic];
14932 struct XY *pos = gamebutton_info[i].pos;
14933 struct GadgetInfo *gi;
14936 unsigned int event_mask;
14937 int base_x = (tape.show_game_buttons ? VX : DX);
14938 int base_y = (tape.show_game_buttons ? VY : DY);
14939 int gd_x = gfx->src_x;
14940 int gd_y = gfx->src_y;
14941 int gd_xp = gfx->src_x + gfx->pressed_xoffset;
14942 int gd_yp = gfx->src_y + gfx->pressed_yoffset;
14943 int gd_xa = gfx->src_x + gfx->active_xoffset;
14944 int gd_ya = gfx->src_y + gfx->active_yoffset;
14945 int gd_xap = gfx->src_x + gfx->active_xoffset + gfx->pressed_xoffset;
14946 int gd_yap = gfx->src_y + gfx->active_yoffset + gfx->pressed_yoffset;
14949 if (gfx->bitmap == NULL)
14951 game_gadget[id] = NULL;
14956 if (id == GAME_CTRL_ID_STOP ||
14957 id == GAME_CTRL_ID_PLAY ||
14958 id == GAME_CTRL_ID_SAVE ||
14959 id == GAME_CTRL_ID_LOAD)
14961 button_type = GD_TYPE_NORMAL_BUTTON;
14963 event_mask = GD_EVENT_RELEASED;
14965 else if (id == GAME_CTRL_ID_UNDO ||
14966 id == GAME_CTRL_ID_REDO)
14968 button_type = GD_TYPE_NORMAL_BUTTON;
14970 event_mask = GD_EVENT_PRESSED | GD_EVENT_REPEATED;
14974 button_type = GD_TYPE_CHECK_BUTTON;
14976 ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
14977 (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
14978 (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
14979 event_mask = GD_EVENT_PRESSED;
14982 gi = CreateGadget(GDI_CUSTOM_ID, id,
14983 GDI_INFO_TEXT, gamebutton_info[i].infotext,
14984 GDI_X, base_x + GDI_ACTIVE_POS(pos->x),
14985 GDI_Y, base_y + GDI_ACTIVE_POS(pos->y),
14986 GDI_WIDTH, gfx->width,
14987 GDI_HEIGHT, gfx->height,
14988 GDI_TYPE, button_type,
14989 GDI_STATE, GD_BUTTON_UNPRESSED,
14990 GDI_CHECKED, checked,
14991 GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
14992 GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
14993 GDI_ALT_DESIGN_UNPRESSED, gfx->bitmap, gd_xa, gd_ya,
14994 GDI_ALT_DESIGN_PRESSED, gfx->bitmap, gd_xap, gd_yap,
14995 GDI_DIRECT_DRAW, FALSE,
14996 GDI_EVENT_MASK, event_mask,
14997 GDI_CALLBACK_ACTION, HandleGameButtons,
15001 Error(ERR_EXIT, "cannot create gadget");
15003 game_gadget[id] = gi;
15007 void FreeGameButtons()
15011 for (i = 0; i < NUM_GAME_BUTTONS; i++)
15012 FreeGadget(game_gadget[i]);
15015 static void UnmapGameButtonsAtSamePosition(int id)
15019 for (i = 0; i < NUM_GAME_BUTTONS; i++)
15021 gamebutton_info[i].pos->x == gamebutton_info[id].pos->x &&
15022 gamebutton_info[i].pos->y == gamebutton_info[id].pos->y)
15023 UnmapGadget(game_gadget[i]);
15026 static void UnmapGameButtonsAtSamePosition_All()
15028 if (setup.show_snapshot_buttons)
15030 UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_SAVE);
15031 UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE2);
15032 UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_LOAD);
15036 UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_STOP);
15037 UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE);
15038 UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PLAY);
15042 static void MapGameButtonsAtSamePosition(int id)
15046 for (i = 0; i < NUM_GAME_BUTTONS; i++)
15048 gamebutton_info[i].pos->x == gamebutton_info[id].pos->x &&
15049 gamebutton_info[i].pos->y == gamebutton_info[id].pos->y)
15050 MapGadget(game_gadget[i]);
15052 UnmapGameButtonsAtSamePosition_All();
15055 void MapUndoRedoButtons()
15057 UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
15058 UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
15060 MapGadget(game_gadget[GAME_CTRL_ID_UNDO]);
15061 MapGadget(game_gadget[GAME_CTRL_ID_REDO]);
15063 ModifyGadget(game_gadget[GAME_CTRL_ID_PAUSE2], GDI_CHECKED, TRUE, GDI_END);
15066 void UnmapUndoRedoButtons()
15068 UnmapGadget(game_gadget[GAME_CTRL_ID_UNDO]);
15069 UnmapGadget(game_gadget[GAME_CTRL_ID_REDO]);
15071 MapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
15072 MapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
15074 ModifyGadget(game_gadget[GAME_CTRL_ID_PAUSE2], GDI_CHECKED, FALSE, GDI_END);
15077 void MapGameButtons()
15081 for (i = 0; i < NUM_GAME_BUTTONS; i++)
15082 if (i != GAME_CTRL_ID_UNDO &&
15083 i != GAME_CTRL_ID_REDO)
15084 MapGadget(game_gadget[i]);
15086 UnmapGameButtonsAtSamePosition_All();
15088 RedrawGameButtons();
15091 void UnmapGameButtons()
15095 for (i = 0; i < NUM_GAME_BUTTONS; i++)
15096 UnmapGadget(game_gadget[i]);
15099 void RedrawGameButtons()
15103 for (i = 0; i < NUM_GAME_BUTTONS; i++)
15104 RedrawGadget(game_gadget[i]);
15106 // RedrawGadget() may have set REDRAW_ALL if buttons are defined off-area
15107 redraw_mask &= ~REDRAW_ALL;
15110 void GameUndoRedoExt()
15112 ClearPlayerAction();
15114 tape.pausing = TRUE;
15117 UpdateAndDisplayGameControlValues();
15119 DrawCompleteVideoDisplay();
15120 DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
15121 DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
15122 DrawVideoDisplay(VIDEO_STATE_1STEP(tape.single_step), 0);
15127 void GameUndo(int steps)
15129 if (!CheckEngineSnapshotList())
15132 LoadEngineSnapshot_Undo(steps);
15137 void GameRedo(int steps)
15139 if (!CheckEngineSnapshotList())
15142 LoadEngineSnapshot_Redo(steps);
15147 static void HandleGameButtonsExt(int id, int button)
15149 static boolean game_undo_executed = FALSE;
15150 int steps = BUTTON_STEPSIZE(button);
15151 boolean handle_game_buttons =
15152 (game_status == GAME_MODE_PLAYING ||
15153 (game_status == GAME_MODE_MAIN && tape.show_game_buttons));
15155 if (!handle_game_buttons)
15160 case GAME_CTRL_ID_STOP:
15161 if (game_status == GAME_MODE_MAIN)
15167 RequestQuitGame(TRUE);
15171 case GAME_CTRL_ID_PAUSE:
15172 case GAME_CTRL_ID_PAUSE2:
15173 if (options.network && game_status == GAME_MODE_PLAYING)
15175 #if defined(NETWORK_AVALIABLE)
15177 SendToServer_ContinuePlaying();
15179 SendToServer_PausePlaying();
15183 TapeTogglePause(TAPE_TOGGLE_MANUAL);
15185 game_undo_executed = FALSE;
15189 case GAME_CTRL_ID_PLAY:
15190 if (game_status == GAME_MODE_MAIN)
15192 StartGameActions(options.network, setup.autorecord, level.random_seed);
15194 else if (tape.pausing)
15196 #if defined(NETWORK_AVALIABLE)
15197 if (options.network)
15198 SendToServer_ContinuePlaying();
15201 TapeTogglePause(TAPE_TOGGLE_MANUAL | TAPE_TOGGLE_PLAY_PAUSE);
15205 case GAME_CTRL_ID_UNDO:
15206 // Important: When using "save snapshot when collecting an item" mode,
15207 // load last (current) snapshot for first "undo" after pressing "pause"
15208 // (else the last-but-one snapshot would be loaded, because the snapshot
15209 // pointer already points to the last snapshot when pressing "pause",
15210 // which is fine for "every step/move" mode, but not for "every collect")
15211 if (game.snapshot.mode == SNAPSHOT_MODE_EVERY_COLLECT &&
15212 !game_undo_executed)
15215 game_undo_executed = TRUE;
15220 case GAME_CTRL_ID_REDO:
15224 case GAME_CTRL_ID_SAVE:
15228 case GAME_CTRL_ID_LOAD:
15232 case SOUND_CTRL_ID_MUSIC:
15233 if (setup.sound_music)
15235 setup.sound_music = FALSE;
15239 else if (audio.music_available)
15241 setup.sound = setup.sound_music = TRUE;
15243 SetAudioMode(setup.sound);
15249 case SOUND_CTRL_ID_LOOPS:
15250 if (setup.sound_loops)
15251 setup.sound_loops = FALSE;
15252 else if (audio.loops_available)
15254 setup.sound = setup.sound_loops = TRUE;
15256 SetAudioMode(setup.sound);
15260 case SOUND_CTRL_ID_SIMPLE:
15261 if (setup.sound_simple)
15262 setup.sound_simple = FALSE;
15263 else if (audio.sound_available)
15265 setup.sound = setup.sound_simple = TRUE;
15267 SetAudioMode(setup.sound);
15276 static void HandleGameButtons(struct GadgetInfo *gi)
15278 HandleGameButtonsExt(gi->custom_id, gi->event.button);
15281 void HandleSoundButtonKeys(Key key)
15284 if (key == setup.shortcut.sound_simple)
15285 ClickOnGadget(game_gadget[SOUND_CTRL_ID_SIMPLE], MB_LEFTBUTTON);
15286 else if (key == setup.shortcut.sound_loops)
15287 ClickOnGadget(game_gadget[SOUND_CTRL_ID_LOOPS], MB_LEFTBUTTON);
15288 else if (key == setup.shortcut.sound_music)
15289 ClickOnGadget(game_gadget[SOUND_CTRL_ID_MUSIC], MB_LEFTBUTTON);