1 // ============================================================================
2 // Rocks'n'Diamonds - McDuffin Strikes Back!
3 // ----------------------------------------------------------------------------
4 // (c) 1995-2014 by Artsoft Entertainment
7 // http://www.artsoft.org/
8 // ----------------------------------------------------------------------------
10 // ============================================================================
12 #include "libgame/libgame.h"
26 #define DEBUG_INIT_PLAYER 1
27 #define DEBUG_PLAYER_ACTIONS 0
29 /* EXPERIMENTAL STUFF */
30 #define USE_NEW_AMOEBA_CODE FALSE
32 /* EXPERIMENTAL STUFF */
33 #define USE_QUICKSAND_BD_ROCK_BUGFIX 0
34 #define USE_QUICKSAND_IMPACT_BUGFIX 0
35 #define USE_DELAYED_GFX_REDRAW 0
36 #define USE_NEW_PLAYER_ASSIGNMENTS 1
38 #if USE_DELAYED_GFX_REDRAW
39 #define TEST_DrawLevelField(x, y) \
40 GfxRedraw[x][y] |= GFX_REDRAW_TILE
41 #define TEST_DrawLevelFieldCrumbled(x, y) \
42 GfxRedraw[x][y] |= GFX_REDRAW_TILE_CRUMBLED
43 #define TEST_DrawLevelFieldCrumbledNeighbours(x, y) \
44 GfxRedraw[x][y] |= GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS
45 #define TEST_DrawTwinkleOnField(x, y) \
46 GfxRedraw[x][y] |= GFX_REDRAW_TILE_TWINKLED
48 #define TEST_DrawLevelField(x, y) \
50 #define TEST_DrawLevelFieldCrumbled(x, y) \
51 DrawLevelFieldCrumbled(x, y)
52 #define TEST_DrawLevelFieldCrumbledNeighbours(x, y) \
53 DrawLevelFieldCrumbledNeighbours(x, y)
54 #define TEST_DrawTwinkleOnField(x, y) \
55 DrawTwinkleOnField(x, y)
64 /* for MovePlayer() */
65 #define MP_NO_ACTION 0
68 #define MP_DONT_RUN_INTO (MP_MOVING | MP_ACTION)
70 /* for ScrollPlayer() */
72 #define SCROLL_GO_ON 1
74 /* for Bang()/Explode() */
75 #define EX_PHASE_START 0
76 #define EX_TYPE_NONE 0
77 #define EX_TYPE_NORMAL (1 << 0)
78 #define EX_TYPE_CENTER (1 << 1)
79 #define EX_TYPE_BORDER (1 << 2)
80 #define EX_TYPE_CROSS (1 << 3)
81 #define EX_TYPE_DYNA (1 << 4)
82 #define EX_TYPE_SINGLE_TILE (EX_TYPE_CENTER | EX_TYPE_BORDER)
84 #define PANEL_OFF() (local_player->LevelSolved_PanelOff)
85 #define PANEL_DEACTIVATED(p) ((p)->x < 0 || (p)->y < 0 || PANEL_OFF())
86 #define PANEL_XPOS(p) (DX + ALIGNED_TEXT_XPOS(p))
87 #define PANEL_YPOS(p) (DY + ALIGNED_TEXT_YPOS(p))
89 /* game panel display and control definitions */
90 #define GAME_PANEL_LEVEL_NUMBER 0
91 #define GAME_PANEL_GEMS 1
92 #define GAME_PANEL_INVENTORY_COUNT 2
93 #define GAME_PANEL_INVENTORY_FIRST_1 3
94 #define GAME_PANEL_INVENTORY_FIRST_2 4
95 #define GAME_PANEL_INVENTORY_FIRST_3 5
96 #define GAME_PANEL_INVENTORY_FIRST_4 6
97 #define GAME_PANEL_INVENTORY_FIRST_5 7
98 #define GAME_PANEL_INVENTORY_FIRST_6 8
99 #define GAME_PANEL_INVENTORY_FIRST_7 9
100 #define GAME_PANEL_INVENTORY_FIRST_8 10
101 #define GAME_PANEL_INVENTORY_LAST_1 11
102 #define GAME_PANEL_INVENTORY_LAST_2 12
103 #define GAME_PANEL_INVENTORY_LAST_3 13
104 #define GAME_PANEL_INVENTORY_LAST_4 14
105 #define GAME_PANEL_INVENTORY_LAST_5 15
106 #define GAME_PANEL_INVENTORY_LAST_6 16
107 #define GAME_PANEL_INVENTORY_LAST_7 17
108 #define GAME_PANEL_INVENTORY_LAST_8 18
109 #define GAME_PANEL_KEY_1 19
110 #define GAME_PANEL_KEY_2 20
111 #define GAME_PANEL_KEY_3 21
112 #define GAME_PANEL_KEY_4 22
113 #define GAME_PANEL_KEY_5 23
114 #define GAME_PANEL_KEY_6 24
115 #define GAME_PANEL_KEY_7 25
116 #define GAME_PANEL_KEY_8 26
117 #define GAME_PANEL_KEY_WHITE 27
118 #define GAME_PANEL_KEY_WHITE_COUNT 28
119 #define GAME_PANEL_SCORE 29
120 #define GAME_PANEL_HIGHSCORE 30
121 #define GAME_PANEL_TIME 31
122 #define GAME_PANEL_TIME_HH 32
123 #define GAME_PANEL_TIME_MM 33
124 #define GAME_PANEL_TIME_SS 34
125 #define GAME_PANEL_FRAME 35
126 #define GAME_PANEL_SHIELD_NORMAL 36
127 #define GAME_PANEL_SHIELD_NORMAL_TIME 37
128 #define GAME_PANEL_SHIELD_DEADLY 38
129 #define GAME_PANEL_SHIELD_DEADLY_TIME 39
130 #define GAME_PANEL_EXIT 40
131 #define GAME_PANEL_EMC_MAGIC_BALL 41
132 #define GAME_PANEL_EMC_MAGIC_BALL_SWITCH 42
133 #define GAME_PANEL_LIGHT_SWITCH 43
134 #define GAME_PANEL_LIGHT_SWITCH_TIME 44
135 #define GAME_PANEL_TIMEGATE_SWITCH 45
136 #define GAME_PANEL_TIMEGATE_SWITCH_TIME 46
137 #define GAME_PANEL_SWITCHGATE_SWITCH 47
138 #define GAME_PANEL_EMC_LENSES 48
139 #define GAME_PANEL_EMC_LENSES_TIME 49
140 #define GAME_PANEL_EMC_MAGNIFIER 50
141 #define GAME_PANEL_EMC_MAGNIFIER_TIME 51
142 #define GAME_PANEL_BALLOON_SWITCH 52
143 #define GAME_PANEL_DYNABOMB_NUMBER 53
144 #define GAME_PANEL_DYNABOMB_SIZE 54
145 #define GAME_PANEL_DYNABOMB_POWER 55
146 #define GAME_PANEL_PENGUINS 56
147 #define GAME_PANEL_SOKOBAN_OBJECTS 57
148 #define GAME_PANEL_SOKOBAN_FIELDS 58
149 #define GAME_PANEL_ROBOT_WHEEL 59
150 #define GAME_PANEL_CONVEYOR_BELT_1 60
151 #define GAME_PANEL_CONVEYOR_BELT_2 61
152 #define GAME_PANEL_CONVEYOR_BELT_3 62
153 #define GAME_PANEL_CONVEYOR_BELT_4 63
154 #define GAME_PANEL_CONVEYOR_BELT_1_SWITCH 64
155 #define GAME_PANEL_CONVEYOR_BELT_2_SWITCH 65
156 #define GAME_PANEL_CONVEYOR_BELT_3_SWITCH 66
157 #define GAME_PANEL_CONVEYOR_BELT_4_SWITCH 67
158 #define GAME_PANEL_MAGIC_WALL 68
159 #define GAME_PANEL_MAGIC_WALL_TIME 69
160 #define GAME_PANEL_GRAVITY_STATE 70
161 #define GAME_PANEL_GRAPHIC_1 71
162 #define GAME_PANEL_GRAPHIC_2 72
163 #define GAME_PANEL_GRAPHIC_3 73
164 #define GAME_PANEL_GRAPHIC_4 74
165 #define GAME_PANEL_GRAPHIC_5 75
166 #define GAME_PANEL_GRAPHIC_6 76
167 #define GAME_PANEL_GRAPHIC_7 77
168 #define GAME_PANEL_GRAPHIC_8 78
169 #define GAME_PANEL_ELEMENT_1 79
170 #define GAME_PANEL_ELEMENT_2 80
171 #define GAME_PANEL_ELEMENT_3 81
172 #define GAME_PANEL_ELEMENT_4 82
173 #define GAME_PANEL_ELEMENT_5 83
174 #define GAME_PANEL_ELEMENT_6 84
175 #define GAME_PANEL_ELEMENT_7 85
176 #define GAME_PANEL_ELEMENT_8 86
177 #define GAME_PANEL_ELEMENT_COUNT_1 87
178 #define GAME_PANEL_ELEMENT_COUNT_2 88
179 #define GAME_PANEL_ELEMENT_COUNT_3 89
180 #define GAME_PANEL_ELEMENT_COUNT_4 90
181 #define GAME_PANEL_ELEMENT_COUNT_5 91
182 #define GAME_PANEL_ELEMENT_COUNT_6 92
183 #define GAME_PANEL_ELEMENT_COUNT_7 93
184 #define GAME_PANEL_ELEMENT_COUNT_8 94
185 #define GAME_PANEL_CE_SCORE_1 95
186 #define GAME_PANEL_CE_SCORE_2 96
187 #define GAME_PANEL_CE_SCORE_3 97
188 #define GAME_PANEL_CE_SCORE_4 98
189 #define GAME_PANEL_CE_SCORE_5 99
190 #define GAME_PANEL_CE_SCORE_6 100
191 #define GAME_PANEL_CE_SCORE_7 101
192 #define GAME_PANEL_CE_SCORE_8 102
193 #define GAME_PANEL_CE_SCORE_1_ELEMENT 103
194 #define GAME_PANEL_CE_SCORE_2_ELEMENT 104
195 #define GAME_PANEL_CE_SCORE_3_ELEMENT 105
196 #define GAME_PANEL_CE_SCORE_4_ELEMENT 106
197 #define GAME_PANEL_CE_SCORE_5_ELEMENT 107
198 #define GAME_PANEL_CE_SCORE_6_ELEMENT 108
199 #define GAME_PANEL_CE_SCORE_7_ELEMENT 109
200 #define GAME_PANEL_CE_SCORE_8_ELEMENT 110
201 #define GAME_PANEL_PLAYER_NAME 111
202 #define GAME_PANEL_LEVEL_NAME 112
203 #define GAME_PANEL_LEVEL_AUTHOR 113
205 #define NUM_GAME_PANEL_CONTROLS 114
207 struct GamePanelOrderInfo
213 static struct GamePanelOrderInfo game_panel_order[NUM_GAME_PANEL_CONTROLS];
215 struct GamePanelControlInfo
219 struct TextPosInfo *pos;
222 int value, last_value;
223 int frame, last_frame;
228 static struct GamePanelControlInfo game_panel_controls[] =
231 GAME_PANEL_LEVEL_NUMBER,
232 &game.panel.level_number,
241 GAME_PANEL_INVENTORY_COUNT,
242 &game.panel.inventory_count,
246 GAME_PANEL_INVENTORY_FIRST_1,
247 &game.panel.inventory_first[0],
251 GAME_PANEL_INVENTORY_FIRST_2,
252 &game.panel.inventory_first[1],
256 GAME_PANEL_INVENTORY_FIRST_3,
257 &game.panel.inventory_first[2],
261 GAME_PANEL_INVENTORY_FIRST_4,
262 &game.panel.inventory_first[3],
266 GAME_PANEL_INVENTORY_FIRST_5,
267 &game.panel.inventory_first[4],
271 GAME_PANEL_INVENTORY_FIRST_6,
272 &game.panel.inventory_first[5],
276 GAME_PANEL_INVENTORY_FIRST_7,
277 &game.panel.inventory_first[6],
281 GAME_PANEL_INVENTORY_FIRST_8,
282 &game.panel.inventory_first[7],
286 GAME_PANEL_INVENTORY_LAST_1,
287 &game.panel.inventory_last[0],
291 GAME_PANEL_INVENTORY_LAST_2,
292 &game.panel.inventory_last[1],
296 GAME_PANEL_INVENTORY_LAST_3,
297 &game.panel.inventory_last[2],
301 GAME_PANEL_INVENTORY_LAST_4,
302 &game.panel.inventory_last[3],
306 GAME_PANEL_INVENTORY_LAST_5,
307 &game.panel.inventory_last[4],
311 GAME_PANEL_INVENTORY_LAST_6,
312 &game.panel.inventory_last[5],
316 GAME_PANEL_INVENTORY_LAST_7,
317 &game.panel.inventory_last[6],
321 GAME_PANEL_INVENTORY_LAST_8,
322 &game.panel.inventory_last[7],
366 GAME_PANEL_KEY_WHITE,
367 &game.panel.key_white,
371 GAME_PANEL_KEY_WHITE_COUNT,
372 &game.panel.key_white_count,
381 GAME_PANEL_HIGHSCORE,
382 &game.panel.highscore,
411 GAME_PANEL_SHIELD_NORMAL,
412 &game.panel.shield_normal,
416 GAME_PANEL_SHIELD_NORMAL_TIME,
417 &game.panel.shield_normal_time,
421 GAME_PANEL_SHIELD_DEADLY,
422 &game.panel.shield_deadly,
426 GAME_PANEL_SHIELD_DEADLY_TIME,
427 &game.panel.shield_deadly_time,
436 GAME_PANEL_EMC_MAGIC_BALL,
437 &game.panel.emc_magic_ball,
441 GAME_PANEL_EMC_MAGIC_BALL_SWITCH,
442 &game.panel.emc_magic_ball_switch,
446 GAME_PANEL_LIGHT_SWITCH,
447 &game.panel.light_switch,
451 GAME_PANEL_LIGHT_SWITCH_TIME,
452 &game.panel.light_switch_time,
456 GAME_PANEL_TIMEGATE_SWITCH,
457 &game.panel.timegate_switch,
461 GAME_PANEL_TIMEGATE_SWITCH_TIME,
462 &game.panel.timegate_switch_time,
466 GAME_PANEL_SWITCHGATE_SWITCH,
467 &game.panel.switchgate_switch,
471 GAME_PANEL_EMC_LENSES,
472 &game.panel.emc_lenses,
476 GAME_PANEL_EMC_LENSES_TIME,
477 &game.panel.emc_lenses_time,
481 GAME_PANEL_EMC_MAGNIFIER,
482 &game.panel.emc_magnifier,
486 GAME_PANEL_EMC_MAGNIFIER_TIME,
487 &game.panel.emc_magnifier_time,
491 GAME_PANEL_BALLOON_SWITCH,
492 &game.panel.balloon_switch,
496 GAME_PANEL_DYNABOMB_NUMBER,
497 &game.panel.dynabomb_number,
501 GAME_PANEL_DYNABOMB_SIZE,
502 &game.panel.dynabomb_size,
506 GAME_PANEL_DYNABOMB_POWER,
507 &game.panel.dynabomb_power,
512 &game.panel.penguins,
516 GAME_PANEL_SOKOBAN_OBJECTS,
517 &game.panel.sokoban_objects,
521 GAME_PANEL_SOKOBAN_FIELDS,
522 &game.panel.sokoban_fields,
526 GAME_PANEL_ROBOT_WHEEL,
527 &game.panel.robot_wheel,
531 GAME_PANEL_CONVEYOR_BELT_1,
532 &game.panel.conveyor_belt[0],
536 GAME_PANEL_CONVEYOR_BELT_2,
537 &game.panel.conveyor_belt[1],
541 GAME_PANEL_CONVEYOR_BELT_3,
542 &game.panel.conveyor_belt[2],
546 GAME_PANEL_CONVEYOR_BELT_4,
547 &game.panel.conveyor_belt[3],
551 GAME_PANEL_CONVEYOR_BELT_1_SWITCH,
552 &game.panel.conveyor_belt_switch[0],
556 GAME_PANEL_CONVEYOR_BELT_2_SWITCH,
557 &game.panel.conveyor_belt_switch[1],
561 GAME_PANEL_CONVEYOR_BELT_3_SWITCH,
562 &game.panel.conveyor_belt_switch[2],
566 GAME_PANEL_CONVEYOR_BELT_4_SWITCH,
567 &game.panel.conveyor_belt_switch[3],
571 GAME_PANEL_MAGIC_WALL,
572 &game.panel.magic_wall,
576 GAME_PANEL_MAGIC_WALL_TIME,
577 &game.panel.magic_wall_time,
581 GAME_PANEL_GRAVITY_STATE,
582 &game.panel.gravity_state,
586 GAME_PANEL_GRAPHIC_1,
587 &game.panel.graphic[0],
591 GAME_PANEL_GRAPHIC_2,
592 &game.panel.graphic[1],
596 GAME_PANEL_GRAPHIC_3,
597 &game.panel.graphic[2],
601 GAME_PANEL_GRAPHIC_4,
602 &game.panel.graphic[3],
606 GAME_PANEL_GRAPHIC_5,
607 &game.panel.graphic[4],
611 GAME_PANEL_GRAPHIC_6,
612 &game.panel.graphic[5],
616 GAME_PANEL_GRAPHIC_7,
617 &game.panel.graphic[6],
621 GAME_PANEL_GRAPHIC_8,
622 &game.panel.graphic[7],
626 GAME_PANEL_ELEMENT_1,
627 &game.panel.element[0],
631 GAME_PANEL_ELEMENT_2,
632 &game.panel.element[1],
636 GAME_PANEL_ELEMENT_3,
637 &game.panel.element[2],
641 GAME_PANEL_ELEMENT_4,
642 &game.panel.element[3],
646 GAME_PANEL_ELEMENT_5,
647 &game.panel.element[4],
651 GAME_PANEL_ELEMENT_6,
652 &game.panel.element[5],
656 GAME_PANEL_ELEMENT_7,
657 &game.panel.element[6],
661 GAME_PANEL_ELEMENT_8,
662 &game.panel.element[7],
666 GAME_PANEL_ELEMENT_COUNT_1,
667 &game.panel.element_count[0],
671 GAME_PANEL_ELEMENT_COUNT_2,
672 &game.panel.element_count[1],
676 GAME_PANEL_ELEMENT_COUNT_3,
677 &game.panel.element_count[2],
681 GAME_PANEL_ELEMENT_COUNT_4,
682 &game.panel.element_count[3],
686 GAME_PANEL_ELEMENT_COUNT_5,
687 &game.panel.element_count[4],
691 GAME_PANEL_ELEMENT_COUNT_6,
692 &game.panel.element_count[5],
696 GAME_PANEL_ELEMENT_COUNT_7,
697 &game.panel.element_count[6],
701 GAME_PANEL_ELEMENT_COUNT_8,
702 &game.panel.element_count[7],
706 GAME_PANEL_CE_SCORE_1,
707 &game.panel.ce_score[0],
711 GAME_PANEL_CE_SCORE_2,
712 &game.panel.ce_score[1],
716 GAME_PANEL_CE_SCORE_3,
717 &game.panel.ce_score[2],
721 GAME_PANEL_CE_SCORE_4,
722 &game.panel.ce_score[3],
726 GAME_PANEL_CE_SCORE_5,
727 &game.panel.ce_score[4],
731 GAME_PANEL_CE_SCORE_6,
732 &game.panel.ce_score[5],
736 GAME_PANEL_CE_SCORE_7,
737 &game.panel.ce_score[6],
741 GAME_PANEL_CE_SCORE_8,
742 &game.panel.ce_score[7],
746 GAME_PANEL_CE_SCORE_1_ELEMENT,
747 &game.panel.ce_score_element[0],
751 GAME_PANEL_CE_SCORE_2_ELEMENT,
752 &game.panel.ce_score_element[1],
756 GAME_PANEL_CE_SCORE_3_ELEMENT,
757 &game.panel.ce_score_element[2],
761 GAME_PANEL_CE_SCORE_4_ELEMENT,
762 &game.panel.ce_score_element[3],
766 GAME_PANEL_CE_SCORE_5_ELEMENT,
767 &game.panel.ce_score_element[4],
771 GAME_PANEL_CE_SCORE_6_ELEMENT,
772 &game.panel.ce_score_element[5],
776 GAME_PANEL_CE_SCORE_7_ELEMENT,
777 &game.panel.ce_score_element[6],
781 GAME_PANEL_CE_SCORE_8_ELEMENT,
782 &game.panel.ce_score_element[7],
786 GAME_PANEL_PLAYER_NAME,
787 &game.panel.player_name,
791 GAME_PANEL_LEVEL_NAME,
792 &game.panel.level_name,
796 GAME_PANEL_LEVEL_AUTHOR,
797 &game.panel.level_author,
808 /* values for delayed check of falling and moving elements and for collision */
809 #define CHECK_DELAY_MOVING 3
810 #define CHECK_DELAY_FALLING CHECK_DELAY_MOVING
811 #define CHECK_DELAY_COLLISION 2
812 #define CHECK_DELAY_IMPACT CHECK_DELAY_COLLISION
814 /* values for initial player move delay (initial delay counter value) */
815 #define INITIAL_MOVE_DELAY_OFF -1
816 #define INITIAL_MOVE_DELAY_ON 0
818 /* values for player movement speed (which is in fact a delay value) */
819 #define MOVE_DELAY_MIN_SPEED 32
820 #define MOVE_DELAY_NORMAL_SPEED 8
821 #define MOVE_DELAY_HIGH_SPEED 4
822 #define MOVE_DELAY_MAX_SPEED 1
824 #define DOUBLE_MOVE_DELAY(x) (x = (x < MOVE_DELAY_MIN_SPEED ? x * 2 : x))
825 #define HALVE_MOVE_DELAY(x) (x = (x > MOVE_DELAY_MAX_SPEED ? x / 2 : x))
827 #define DOUBLE_PLAYER_SPEED(p) (HALVE_MOVE_DELAY( (p)->move_delay_value))
828 #define HALVE_PLAYER_SPEED(p) (DOUBLE_MOVE_DELAY((p)->move_delay_value))
830 /* values for scroll positions */
831 #define SCROLL_POSITION_X(x) ((x) < SBX_Left + MIDPOSX ? SBX_Left : \
832 (x) > SBX_Right + MIDPOSX ? SBX_Right :\
834 #define SCROLL_POSITION_Y(y) ((y) < SBY_Upper + MIDPOSY ? SBY_Upper :\
835 (y) > SBY_Lower + MIDPOSY ? SBY_Lower :\
838 /* values for other actions */
839 #define MOVE_STEPSIZE_NORMAL (TILEX / MOVE_DELAY_NORMAL_SPEED)
840 #define MOVE_STEPSIZE_MIN (1)
841 #define MOVE_STEPSIZE_MAX (TILEX)
843 #define GET_DX_FROM_DIR(d) ((d) == MV_LEFT ? -1 : (d) == MV_RIGHT ? 1 : 0)
844 #define GET_DY_FROM_DIR(d) ((d) == MV_UP ? -1 : (d) == MV_DOWN ? 1 : 0)
846 #define INIT_GFX_RANDOM() (GetSimpleRandom(1000000))
848 #define GET_NEW_PUSH_DELAY(e) ( (element_info[e].push_delay_fixed) + \
849 RND(element_info[e].push_delay_random))
850 #define GET_NEW_DROP_DELAY(e) ( (element_info[e].drop_delay_fixed) + \
851 RND(element_info[e].drop_delay_random))
852 #define GET_NEW_MOVE_DELAY(e) ( (element_info[e].move_delay_fixed) + \
853 RND(element_info[e].move_delay_random))
854 #define GET_MAX_MOVE_DELAY(e) ( (element_info[e].move_delay_fixed) + \
855 (element_info[e].move_delay_random))
856 #define GET_NEW_CE_VALUE(e) ( (element_info[e].ce_value_fixed_initial) +\
857 RND(element_info[e].ce_value_random_initial))
858 #define GET_CE_SCORE(e) ( (element_info[e].collect_score))
859 #define GET_CHANGE_DELAY(c) ( ((c)->delay_fixed * (c)->delay_frames) + \
860 RND((c)->delay_random * (c)->delay_frames))
861 #define GET_CE_DELAY_VALUE(c) ( ((c)->delay_fixed) + \
862 RND((c)->delay_random))
865 #define GET_VALID_RUNTIME_ELEMENT(e) \
866 ((e) >= NUM_RUNTIME_ELEMENTS ? EL_UNKNOWN : (e))
868 #define RESOLVED_REFERENCE_ELEMENT(be, e) \
869 ((be) + (e) - EL_SELF < EL_CUSTOM_START ? EL_CUSTOM_START : \
870 (be) + (e) - EL_SELF > EL_CUSTOM_END ? EL_CUSTOM_END : \
871 (be) + (e) - EL_SELF)
873 #define GET_PLAYER_FROM_BITS(p) \
874 (EL_PLAYER_1 + ((p) != PLAYER_BITS_ANY ? log_2(p) : 0))
876 #define GET_TARGET_ELEMENT(be, e, ch, cv, cs) \
877 ((e) == EL_TRIGGER_PLAYER ? (ch)->actual_trigger_player : \
878 (e) == EL_TRIGGER_ELEMENT ? (ch)->actual_trigger_element : \
879 (e) == EL_TRIGGER_CE_VALUE ? (ch)->actual_trigger_ce_value : \
880 (e) == EL_TRIGGER_CE_SCORE ? (ch)->actual_trigger_ce_score : \
881 (e) == EL_CURRENT_CE_VALUE ? (cv) : \
882 (e) == EL_CURRENT_CE_SCORE ? (cs) : \
883 (e) >= EL_PREV_CE_8 && (e) <= EL_NEXT_CE_8 ? \
884 RESOLVED_REFERENCE_ELEMENT(be, e) : \
887 #define CAN_GROW_INTO(e) \
888 ((e) == EL_SAND || (IS_DIGGABLE(e) && level.grow_into_diggable))
890 #define ELEMENT_CAN_ENTER_FIELD_BASE_X(x, y, condition) \
891 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
894 #define ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, condition) \
895 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
896 (CAN_MOVE_INTO_ACID(e) && \
897 Feld[x][y] == EL_ACID) || \
900 #define ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, condition) \
901 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) || \
902 (CAN_MOVE_INTO_ACID(e) && \
903 Feld[x][y] == EL_ACID) || \
906 #define ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, condition) \
907 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
909 (CAN_MOVE_INTO_ACID(e) && \
910 Feld[x][y] == EL_ACID) || \
911 (DONT_COLLIDE_WITH(e) && \
913 !PLAYER_ENEMY_PROTECTED(x, y))))
915 #define ELEMENT_CAN_ENTER_FIELD(e, x, y) \
916 ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, 0)
918 #define SATELLITE_CAN_ENTER_FIELD(x, y) \
919 ELEMENT_CAN_ENTER_FIELD_BASE_2(EL_SATELLITE, x, y, 0)
921 #define ANDROID_CAN_ENTER_FIELD(e, x, y) \
922 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, Feld[x][y] == EL_EMC_PLANT)
924 #define ANDROID_CAN_CLONE_FIELD(x, y) \
925 (IN_LEV_FIELD(x, y) && (CAN_BE_CLONED_BY_ANDROID(Feld[x][y]) || \
926 CAN_BE_CLONED_BY_ANDROID(EL_TRIGGER_ELEMENT)))
928 #define ENEMY_CAN_ENTER_FIELD(e, x, y) \
929 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
931 #define YAMYAM_CAN_ENTER_FIELD(e, x, y) \
932 ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, Feld[x][y] == EL_DIAMOND)
934 #define DARK_YAMYAM_CAN_ENTER_FIELD(e, x, y) \
935 ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x,y, IS_FOOD_DARK_YAMYAM(Feld[x][y]))
937 #define PACMAN_CAN_ENTER_FIELD(e, x, y) \
938 ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, IS_AMOEBOID(Feld[x][y]))
940 #define PIG_CAN_ENTER_FIELD(e, x, y) \
941 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, IS_FOOD_PIG(Feld[x][y]))
943 #define PENGUIN_CAN_ENTER_FIELD(e, x, y) \
944 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (Feld[x][y] == EL_EXIT_OPEN || \
945 Feld[x][y] == EL_EM_EXIT_OPEN || \
946 Feld[x][y] == EL_STEEL_EXIT_OPEN || \
947 Feld[x][y] == EL_EM_STEEL_EXIT_OPEN || \
948 IS_FOOD_PENGUIN(Feld[x][y])))
949 #define DRAGON_CAN_ENTER_FIELD(e, x, y) \
950 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
952 #define MOLE_CAN_ENTER_FIELD(e, x, y, condition) \
953 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (condition))
955 #define SPRING_CAN_ENTER_FIELD(e, x, y) \
956 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
958 #define SPRING_CAN_BUMP_FROM_FIELD(x, y) \
959 (IN_LEV_FIELD(x, y) && (Feld[x][y] == EL_EMC_SPRING_BUMPER || \
960 Feld[x][y] == EL_EMC_SPRING_BUMPER_ACTIVE))
962 #define MOVE_ENTER_EL(e) (element_info[e].move_enter_element)
964 #define CE_ENTER_FIELD_COND(e, x, y) \
965 (!IS_PLAYER(x, y) && \
966 IS_EQUAL_OR_IN_GROUP(Feld[x][y], MOVE_ENTER_EL(e)))
968 #define CUSTOM_ELEMENT_CAN_ENTER_FIELD(e, x, y) \
969 ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, CE_ENTER_FIELD_COND(e, x, y))
971 #define IN_LEV_FIELD_AND_IS_FREE(x, y) (IN_LEV_FIELD(x, y) && IS_FREE(x, y))
972 #define IN_LEV_FIELD_AND_NOT_FREE(x, y) (IN_LEV_FIELD(x, y) && !IS_FREE(x, y))
974 #define ACCESS_FROM(e, d) (element_info[e].access_direction &(d))
975 #define IS_WALKABLE_FROM(e, d) (IS_WALKABLE(e) && ACCESS_FROM(e, d))
976 #define IS_PASSABLE_FROM(e, d) (IS_PASSABLE(e) && ACCESS_FROM(e, d))
977 #define IS_ACCESSIBLE_FROM(e, d) (IS_ACCESSIBLE(e) && ACCESS_FROM(e, d))
979 /* game button identifiers */
980 #define GAME_CTRL_ID_STOP 0
981 #define GAME_CTRL_ID_PAUSE 1
982 #define GAME_CTRL_ID_PLAY 2
983 #define GAME_CTRL_ID_UNDO 3
984 #define GAME_CTRL_ID_REDO 4
985 #define GAME_CTRL_ID_SAVE 5
986 #define GAME_CTRL_ID_PAUSE2 6
987 #define GAME_CTRL_ID_LOAD 7
988 #define SOUND_CTRL_ID_MUSIC 8
989 #define SOUND_CTRL_ID_LOOPS 9
990 #define SOUND_CTRL_ID_SIMPLE 10
992 #define NUM_GAME_BUTTONS 11
995 /* forward declaration for internal use */
997 static void CreateField(int, int, int);
999 static void ResetGfxAnimation(int, int);
1001 static void SetPlayerWaiting(struct PlayerInfo *, boolean);
1002 static void AdvanceFrameAndPlayerCounters(int);
1004 static boolean MovePlayerOneStep(struct PlayerInfo *, int, int, int, int);
1005 static boolean MovePlayer(struct PlayerInfo *, int, int);
1006 static void ScrollPlayer(struct PlayerInfo *, int);
1007 static void ScrollScreen(struct PlayerInfo *, int);
1009 static int DigField(struct PlayerInfo *, int, int, int, int, int, int, int);
1010 static boolean DigFieldByCE(int, int, int);
1011 static boolean SnapField(struct PlayerInfo *, int, int);
1012 static boolean DropElement(struct PlayerInfo *);
1014 static void InitBeltMovement(void);
1015 static void CloseAllOpenTimegates(void);
1016 static void CheckGravityMovement(struct PlayerInfo *);
1017 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *);
1018 static void KillPlayerUnlessEnemyProtected(int, int);
1019 static void KillPlayerUnlessExplosionProtected(int, int);
1021 static void TestIfPlayerTouchesCustomElement(int, int);
1022 static void TestIfElementTouchesCustomElement(int, int);
1023 static void TestIfElementHitsCustomElement(int, int, int);
1025 static void HandleElementChange(int, int, int);
1026 static void ExecuteCustomElementAction(int, int, int, int);
1027 static boolean ChangeElement(int, int, int, int);
1029 static boolean CheckTriggeredElementChangeExt(int, int, int, int, int,int,int);
1030 #define CheckTriggeredElementChange(x, y, e, ev) \
1031 CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY,CH_SIDE_ANY, -1)
1032 #define CheckTriggeredElementChangeByPlayer(x, y, e, ev, p, s) \
1033 CheckTriggeredElementChangeExt(x, y, e, ev, p, s, -1)
1034 #define CheckTriggeredElementChangeBySide(x, y, e, ev, s) \
1035 CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, s, -1)
1036 #define CheckTriggeredElementChangeByPage(x, y, e, ev, p) \
1037 CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY, CH_SIDE_ANY, p)
1039 static boolean CheckElementChangeExt(int, int, int, int, int, int, int);
1040 #define CheckElementChange(x, y, e, te, ev) \
1041 CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, CH_SIDE_ANY)
1042 #define CheckElementChangeByPlayer(x, y, e, ev, p, s) \
1043 CheckElementChangeExt(x, y, e, EL_EMPTY, ev, p, s)
1044 #define CheckElementChangeBySide(x, y, e, te, ev, s) \
1045 CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, s)
1047 static void PlayLevelSound(int, int, int);
1048 static void PlayLevelSoundNearest(int, int, int);
1049 static void PlayLevelSoundAction(int, int, int);
1050 static void PlayLevelSoundElementAction(int, int, int, int);
1051 static void PlayLevelSoundElementActionIfLoop(int, int, int, int);
1052 static void PlayLevelSoundActionIfLoop(int, int, int);
1053 static void StopLevelSoundActionIfLoop(int, int, int);
1054 static void PlayLevelMusic();
1055 static void FadeLevelSoundsAndMusic();
1057 static void HandleGameButtons(struct GadgetInfo *);
1059 int AmoebeNachbarNr(int, int);
1060 void AmoebeUmwandeln(int, int);
1061 void ContinueMoving(int, int);
1062 void Bang(int, int);
1063 void InitMovDir(int, int);
1064 void InitAmoebaNr(int, int);
1065 int NewHiScore(void);
1067 void TestIfGoodThingHitsBadThing(int, int, int);
1068 void TestIfBadThingHitsGoodThing(int, int, int);
1069 void TestIfPlayerTouchesBadThing(int, int);
1070 void TestIfPlayerRunsIntoBadThing(int, int, int);
1071 void TestIfBadThingTouchesPlayer(int, int);
1072 void TestIfBadThingRunsIntoPlayer(int, int, int);
1073 void TestIfFriendTouchesBadThing(int, int);
1074 void TestIfBadThingTouchesFriend(int, int);
1075 void TestIfBadThingTouchesOtherBadThing(int, int);
1076 void TestIfGoodThingGetsHitByBadThing(int, int, int);
1078 void KillPlayer(struct PlayerInfo *);
1079 void BuryPlayer(struct PlayerInfo *);
1080 void RemovePlayer(struct PlayerInfo *);
1082 static int getInvisibleActiveFromInvisibleElement(int);
1083 static int getInvisibleFromInvisibleActiveElement(int);
1085 static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
1087 /* for detection of endless loops, caused by custom element programming */
1088 /* (using maximal playfield width x 10 is just a rough approximation) */
1089 #define MAX_ELEMENT_CHANGE_RECURSION_DEPTH (MAX_PLAYFIELD_WIDTH * 10)
1091 #define RECURSION_LOOP_DETECTION_START(e, rc) \
1093 if (recursion_loop_detected) \
1096 if (recursion_loop_depth > MAX_ELEMENT_CHANGE_RECURSION_DEPTH) \
1098 recursion_loop_detected = TRUE; \
1099 recursion_loop_element = (e); \
1102 recursion_loop_depth++; \
1105 #define RECURSION_LOOP_DETECTION_END() \
1107 recursion_loop_depth--; \
1110 static int recursion_loop_depth;
1111 static boolean recursion_loop_detected;
1112 static boolean recursion_loop_element;
1114 static int map_player_action[MAX_PLAYERS];
1117 /* ------------------------------------------------------------------------- */
1118 /* definition of elements that automatically change to other elements after */
1119 /* a specified time, eventually calling a function when changing */
1120 /* ------------------------------------------------------------------------- */
1122 /* forward declaration for changer functions */
1123 static void InitBuggyBase(int, int);
1124 static void WarnBuggyBase(int, int);
1126 static void InitTrap(int, int);
1127 static void ActivateTrap(int, int);
1128 static void ChangeActiveTrap(int, int);
1130 static void InitRobotWheel(int, int);
1131 static void RunRobotWheel(int, int);
1132 static void StopRobotWheel(int, int);
1134 static void InitTimegateWheel(int, int);
1135 static void RunTimegateWheel(int, int);
1137 static void InitMagicBallDelay(int, int);
1138 static void ActivateMagicBall(int, int);
1140 struct ChangingElementInfo
1145 void (*pre_change_function)(int x, int y);
1146 void (*change_function)(int x, int y);
1147 void (*post_change_function)(int x, int y);
1150 static struct ChangingElementInfo change_delay_list[] =
1185 EL_STEEL_EXIT_OPENING,
1193 EL_STEEL_EXIT_CLOSING,
1194 EL_STEEL_EXIT_CLOSED,
1217 EL_EM_STEEL_EXIT_OPENING,
1218 EL_EM_STEEL_EXIT_OPEN,
1225 EL_EM_STEEL_EXIT_CLOSING,
1249 EL_SWITCHGATE_OPENING,
1257 EL_SWITCHGATE_CLOSING,
1258 EL_SWITCHGATE_CLOSED,
1265 EL_TIMEGATE_OPENING,
1273 EL_TIMEGATE_CLOSING,
1282 EL_ACID_SPLASH_LEFT,
1290 EL_ACID_SPLASH_RIGHT,
1299 EL_SP_BUGGY_BASE_ACTIVATING,
1306 EL_SP_BUGGY_BASE_ACTIVATING,
1307 EL_SP_BUGGY_BASE_ACTIVE,
1314 EL_SP_BUGGY_BASE_ACTIVE,
1338 EL_ROBOT_WHEEL_ACTIVE,
1346 EL_TIMEGATE_SWITCH_ACTIVE,
1354 EL_DC_TIMEGATE_SWITCH_ACTIVE,
1355 EL_DC_TIMEGATE_SWITCH,
1362 EL_EMC_MAGIC_BALL_ACTIVE,
1363 EL_EMC_MAGIC_BALL_ACTIVE,
1370 EL_EMC_SPRING_BUMPER_ACTIVE,
1371 EL_EMC_SPRING_BUMPER,
1378 EL_DIAGONAL_SHRINKING,
1386 EL_DIAGONAL_GROWING,
1407 int push_delay_fixed, push_delay_random;
1411 { EL_SPRING, 0, 0 },
1412 { EL_BALLOON, 0, 0 },
1414 { EL_SOKOBAN_OBJECT, 2, 0 },
1415 { EL_SOKOBAN_FIELD_FULL, 2, 0 },
1416 { EL_SATELLITE, 2, 0 },
1417 { EL_SP_DISK_YELLOW, 2, 0 },
1419 { EL_UNDEFINED, 0, 0 },
1427 move_stepsize_list[] =
1429 { EL_AMOEBA_DROP, 2 },
1430 { EL_AMOEBA_DROPPING, 2 },
1431 { EL_QUICKSAND_FILLING, 1 },
1432 { EL_QUICKSAND_EMPTYING, 1 },
1433 { EL_QUICKSAND_FAST_FILLING, 2 },
1434 { EL_QUICKSAND_FAST_EMPTYING, 2 },
1435 { EL_MAGIC_WALL_FILLING, 2 },
1436 { EL_MAGIC_WALL_EMPTYING, 2 },
1437 { EL_BD_MAGIC_WALL_FILLING, 2 },
1438 { EL_BD_MAGIC_WALL_EMPTYING, 2 },
1439 { EL_DC_MAGIC_WALL_FILLING, 2 },
1440 { EL_DC_MAGIC_WALL_EMPTYING, 2 },
1442 { EL_UNDEFINED, 0 },
1450 collect_count_list[] =
1453 { EL_BD_DIAMOND, 1 },
1454 { EL_EMERALD_YELLOW, 1 },
1455 { EL_EMERALD_RED, 1 },
1456 { EL_EMERALD_PURPLE, 1 },
1458 { EL_SP_INFOTRON, 1 },
1462 { EL_UNDEFINED, 0 },
1470 access_direction_list[] =
1472 { EL_TUBE_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1473 { EL_TUBE_VERTICAL, MV_UP | MV_DOWN },
1474 { EL_TUBE_HORIZONTAL, MV_LEFT | MV_RIGHT },
1475 { EL_TUBE_VERTICAL_LEFT, MV_LEFT | MV_UP | MV_DOWN },
1476 { EL_TUBE_VERTICAL_RIGHT, MV_RIGHT | MV_UP | MV_DOWN },
1477 { EL_TUBE_HORIZONTAL_UP, MV_LEFT | MV_RIGHT | MV_UP },
1478 { EL_TUBE_HORIZONTAL_DOWN, MV_LEFT | MV_RIGHT | MV_DOWN },
1479 { EL_TUBE_LEFT_UP, MV_LEFT | MV_UP },
1480 { EL_TUBE_LEFT_DOWN, MV_LEFT | MV_DOWN },
1481 { EL_TUBE_RIGHT_UP, MV_RIGHT | MV_UP },
1482 { EL_TUBE_RIGHT_DOWN, MV_RIGHT | MV_DOWN },
1484 { EL_SP_PORT_LEFT, MV_RIGHT },
1485 { EL_SP_PORT_RIGHT, MV_LEFT },
1486 { EL_SP_PORT_UP, MV_DOWN },
1487 { EL_SP_PORT_DOWN, MV_UP },
1488 { EL_SP_PORT_HORIZONTAL, MV_LEFT | MV_RIGHT },
1489 { EL_SP_PORT_VERTICAL, MV_UP | MV_DOWN },
1490 { EL_SP_PORT_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1491 { EL_SP_GRAVITY_PORT_LEFT, MV_RIGHT },
1492 { EL_SP_GRAVITY_PORT_RIGHT, MV_LEFT },
1493 { EL_SP_GRAVITY_PORT_UP, MV_DOWN },
1494 { EL_SP_GRAVITY_PORT_DOWN, MV_UP },
1495 { EL_SP_GRAVITY_ON_PORT_LEFT, MV_RIGHT },
1496 { EL_SP_GRAVITY_ON_PORT_RIGHT, MV_LEFT },
1497 { EL_SP_GRAVITY_ON_PORT_UP, MV_DOWN },
1498 { EL_SP_GRAVITY_ON_PORT_DOWN, MV_UP },
1499 { EL_SP_GRAVITY_OFF_PORT_LEFT, MV_RIGHT },
1500 { EL_SP_GRAVITY_OFF_PORT_RIGHT, MV_LEFT },
1501 { EL_SP_GRAVITY_OFF_PORT_UP, MV_DOWN },
1502 { EL_SP_GRAVITY_OFF_PORT_DOWN, MV_UP },
1504 { EL_UNDEFINED, MV_NONE }
1507 static boolean trigger_events[MAX_NUM_ELEMENTS][NUM_CHANGE_EVENTS];
1509 #define IS_AUTO_CHANGING(e) (element_info[e].has_change_event[CE_DELAY])
1510 #define IS_JUST_CHANGING(x, y) (ChangeDelay[x][y] != 0)
1511 #define IS_CHANGING(x, y) (IS_AUTO_CHANGING(Feld[x][y]) || \
1512 IS_JUST_CHANGING(x, y))
1514 #define CE_PAGE(e, ce) (element_info[e].event_page[ce])
1516 /* static variables for playfield scan mode (scanning forward or backward) */
1517 static int playfield_scan_start_x = 0;
1518 static int playfield_scan_start_y = 0;
1519 static int playfield_scan_delta_x = 1;
1520 static int playfield_scan_delta_y = 1;
1522 #define SCAN_PLAYFIELD(x, y) for ((y) = playfield_scan_start_y; \
1523 (y) >= 0 && (y) <= lev_fieldy - 1; \
1524 (y) += playfield_scan_delta_y) \
1525 for ((x) = playfield_scan_start_x; \
1526 (x) >= 0 && (x) <= lev_fieldx - 1; \
1527 (x) += playfield_scan_delta_x)
1530 void DEBUG_SetMaximumDynamite()
1534 for (i = 0; i < MAX_INVENTORY_SIZE; i++)
1535 if (local_player->inventory_size < MAX_INVENTORY_SIZE)
1536 local_player->inventory_element[local_player->inventory_size++] =
1541 static void InitPlayfieldScanModeVars()
1543 if (game.use_reverse_scan_direction)
1545 playfield_scan_start_x = lev_fieldx - 1;
1546 playfield_scan_start_y = lev_fieldy - 1;
1548 playfield_scan_delta_x = -1;
1549 playfield_scan_delta_y = -1;
1553 playfield_scan_start_x = 0;
1554 playfield_scan_start_y = 0;
1556 playfield_scan_delta_x = 1;
1557 playfield_scan_delta_y = 1;
1561 static void InitPlayfieldScanMode(int mode)
1563 game.use_reverse_scan_direction =
1564 (mode == CA_ARG_SCAN_MODE_REVERSE ? TRUE : FALSE);
1566 InitPlayfieldScanModeVars();
1569 static int get_move_delay_from_stepsize(int move_stepsize)
1572 MIN(MAX(MOVE_STEPSIZE_MIN, move_stepsize), MOVE_STEPSIZE_MAX);
1574 /* make sure that stepsize value is always a power of 2 */
1575 move_stepsize = (1 << log_2(move_stepsize));
1577 return TILEX / move_stepsize;
1580 static void SetPlayerMoveSpeed(struct PlayerInfo *player, int move_stepsize,
1583 int player_nr = player->index_nr;
1584 int move_delay = get_move_delay_from_stepsize(move_stepsize);
1585 boolean cannot_move = (move_stepsize == STEPSIZE_NOT_MOVING ? TRUE : FALSE);
1587 /* do no immediately change move delay -- the player might just be moving */
1588 player->move_delay_value_next = move_delay;
1590 /* information if player can move must be set separately */
1591 player->cannot_move = cannot_move;
1595 player->move_delay = game.initial_move_delay[player_nr];
1596 player->move_delay_value = game.initial_move_delay_value[player_nr];
1598 player->move_delay_value_next = -1;
1600 player->move_delay_reset_counter = 0;
1604 void GetPlayerConfig()
1606 GameFrameDelay = setup.game_frame_delay;
1608 if (!audio.sound_available)
1609 setup.sound_simple = FALSE;
1611 if (!audio.loops_available)
1612 setup.sound_loops = FALSE;
1614 if (!audio.music_available)
1615 setup.sound_music = FALSE;
1617 if (!video.fullscreen_available)
1618 setup.fullscreen = FALSE;
1620 setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
1622 SetAudioMode(setup.sound);
1625 int GetElementFromGroupElement(int element)
1627 if (IS_GROUP_ELEMENT(element))
1629 struct ElementGroupInfo *group = element_info[element].group;
1630 int last_anim_random_frame = gfx.anim_random_frame;
1633 if (group->choice_mode == ANIM_RANDOM)
1634 gfx.anim_random_frame = RND(group->num_elements_resolved);
1636 element_pos = getAnimationFrame(group->num_elements_resolved, 1,
1637 group->choice_mode, 0,
1640 if (group->choice_mode == ANIM_RANDOM)
1641 gfx.anim_random_frame = last_anim_random_frame;
1643 group->choice_pos++;
1645 element = group->element_resolved[element_pos];
1651 static void InitPlayerField(int x, int y, int element, boolean init_game)
1653 if (element == EL_SP_MURPHY)
1657 if (stored_player[0].present)
1659 Feld[x][y] = EL_SP_MURPHY_CLONE;
1665 stored_player[0].initial_element = element;
1666 stored_player[0].use_murphy = TRUE;
1668 if (!level.use_artwork_element[0])
1669 stored_player[0].artwork_element = EL_SP_MURPHY;
1672 Feld[x][y] = EL_PLAYER_1;
1678 struct PlayerInfo *player = &stored_player[Feld[x][y] - EL_PLAYER_1];
1679 int jx = player->jx, jy = player->jy;
1681 player->present = TRUE;
1683 player->block_last_field = (element == EL_SP_MURPHY ?
1684 level.sp_block_last_field :
1685 level.block_last_field);
1687 /* ---------- initialize player's last field block delay --------------- */
1689 /* always start with reliable default value (no adjustment needed) */
1690 player->block_delay_adjustment = 0;
1692 /* special case 1: in Supaplex, Murphy blocks last field one more frame */
1693 if (player->block_last_field && element == EL_SP_MURPHY)
1694 player->block_delay_adjustment = 1;
1696 /* special case 2: in game engines before 3.1.1, blocking was different */
1697 if (game.use_block_last_field_bug)
1698 player->block_delay_adjustment = (player->block_last_field ? -1 : 1);
1700 if (!options.network || player->connected)
1702 player->active = TRUE;
1704 /* remove potentially duplicate players */
1705 if (StorePlayer[jx][jy] == Feld[x][y])
1706 StorePlayer[jx][jy] = 0;
1708 StorePlayer[x][y] = Feld[x][y];
1710 #if DEBUG_INIT_PLAYER
1713 printf("- player element %d activated", player->element_nr);
1714 printf(" (local player is %d and currently %s)\n",
1715 local_player->element_nr,
1716 local_player->active ? "active" : "not active");
1721 Feld[x][y] = EL_EMPTY;
1723 player->jx = player->last_jx = x;
1724 player->jy = player->last_jy = y;
1729 int player_nr = GET_PLAYER_NR(element);
1730 struct PlayerInfo *player = &stored_player[player_nr];
1732 if (player->active && player->killed)
1733 player->reanimated = TRUE; /* if player was just killed, reanimate him */
1737 static void InitField(int x, int y, boolean init_game)
1739 int element = Feld[x][y];
1748 InitPlayerField(x, y, element, init_game);
1751 case EL_SOKOBAN_FIELD_PLAYER:
1752 element = Feld[x][y] = EL_PLAYER_1;
1753 InitField(x, y, init_game);
1755 element = Feld[x][y] = EL_SOKOBAN_FIELD_EMPTY;
1756 InitField(x, y, init_game);
1759 case EL_SOKOBAN_FIELD_EMPTY:
1760 local_player->sokobanfields_still_needed++;
1764 if (x < lev_fieldx-1 && Feld[x+1][y] == EL_ACID)
1765 Feld[x][y] = EL_ACID_POOL_TOPLEFT;
1766 else if (x > 0 && Feld[x-1][y] == EL_ACID)
1767 Feld[x][y] = EL_ACID_POOL_TOPRIGHT;
1768 else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPLEFT)
1769 Feld[x][y] = EL_ACID_POOL_BOTTOMLEFT;
1770 else if (y > 0 && Feld[x][y-1] == EL_ACID)
1771 Feld[x][y] = EL_ACID_POOL_BOTTOM;
1772 else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPRIGHT)
1773 Feld[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
1782 case EL_SPACESHIP_RIGHT:
1783 case EL_SPACESHIP_UP:
1784 case EL_SPACESHIP_LEFT:
1785 case EL_SPACESHIP_DOWN:
1786 case EL_BD_BUTTERFLY:
1787 case EL_BD_BUTTERFLY_RIGHT:
1788 case EL_BD_BUTTERFLY_UP:
1789 case EL_BD_BUTTERFLY_LEFT:
1790 case EL_BD_BUTTERFLY_DOWN:
1792 case EL_BD_FIREFLY_RIGHT:
1793 case EL_BD_FIREFLY_UP:
1794 case EL_BD_FIREFLY_LEFT:
1795 case EL_BD_FIREFLY_DOWN:
1796 case EL_PACMAN_RIGHT:
1798 case EL_PACMAN_LEFT:
1799 case EL_PACMAN_DOWN:
1801 case EL_YAMYAM_LEFT:
1802 case EL_YAMYAM_RIGHT:
1804 case EL_YAMYAM_DOWN:
1805 case EL_DARK_YAMYAM:
1808 case EL_SP_SNIKSNAK:
1809 case EL_SP_ELECTRON:
1818 case EL_AMOEBA_FULL:
1823 case EL_AMOEBA_DROP:
1824 if (y == lev_fieldy - 1)
1826 Feld[x][y] = EL_AMOEBA_GROWING;
1827 Store[x][y] = EL_AMOEBA_WET;
1831 case EL_DYNAMITE_ACTIVE:
1832 case EL_SP_DISK_RED_ACTIVE:
1833 case EL_DYNABOMB_PLAYER_1_ACTIVE:
1834 case EL_DYNABOMB_PLAYER_2_ACTIVE:
1835 case EL_DYNABOMB_PLAYER_3_ACTIVE:
1836 case EL_DYNABOMB_PLAYER_4_ACTIVE:
1837 MovDelay[x][y] = 96;
1840 case EL_EM_DYNAMITE_ACTIVE:
1841 MovDelay[x][y] = 32;
1845 local_player->lights_still_needed++;
1849 local_player->friends_still_needed++;
1854 GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
1857 case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
1858 case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
1859 case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
1860 case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
1861 case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
1862 case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
1863 case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
1864 case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
1865 case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
1866 case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
1867 case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
1868 case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
1871 int belt_nr = getBeltNrFromBeltSwitchElement(Feld[x][y]);
1872 int belt_dir = getBeltDirFromBeltSwitchElement(Feld[x][y]);
1873 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Feld[x][y]);
1875 if (game.belt_dir_nr[belt_nr] == 3) /* initial value */
1877 game.belt_dir[belt_nr] = belt_dir;
1878 game.belt_dir_nr[belt_nr] = belt_dir_nr;
1880 else /* more than one switch -- set it like the first switch */
1882 Feld[x][y] = Feld[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
1887 case EL_LIGHT_SWITCH_ACTIVE:
1889 game.light_time_left = level.time_light * FRAMES_PER_SECOND;
1892 case EL_INVISIBLE_STEELWALL:
1893 case EL_INVISIBLE_WALL:
1894 case EL_INVISIBLE_SAND:
1895 if (game.light_time_left > 0 ||
1896 game.lenses_time_left > 0)
1897 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
1900 case EL_EMC_MAGIC_BALL:
1901 if (game.ball_state)
1902 Feld[x][y] = EL_EMC_MAGIC_BALL_ACTIVE;
1905 case EL_EMC_MAGIC_BALL_SWITCH:
1906 if (game.ball_state)
1907 Feld[x][y] = EL_EMC_MAGIC_BALL_SWITCH_ACTIVE;
1910 case EL_TRIGGER_PLAYER:
1911 case EL_TRIGGER_ELEMENT:
1912 case EL_TRIGGER_CE_VALUE:
1913 case EL_TRIGGER_CE_SCORE:
1915 case EL_ANY_ELEMENT:
1916 case EL_CURRENT_CE_VALUE:
1917 case EL_CURRENT_CE_SCORE:
1934 /* reference elements should not be used on the playfield */
1935 Feld[x][y] = EL_EMPTY;
1939 if (IS_CUSTOM_ELEMENT(element))
1941 if (CAN_MOVE(element))
1944 if (!element_info[element].use_last_ce_value || init_game)
1945 CustomValue[x][y] = GET_NEW_CE_VALUE(Feld[x][y]);
1947 else if (IS_GROUP_ELEMENT(element))
1949 Feld[x][y] = GetElementFromGroupElement(element);
1951 InitField(x, y, init_game);
1958 CheckTriggeredElementChange(x, y, element, CE_CREATION_OF_X);
1961 inline static void InitField_WithBug1(int x, int y, boolean init_game)
1963 InitField(x, y, init_game);
1965 /* not needed to call InitMovDir() -- already done by InitField()! */
1966 if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1967 CAN_MOVE(Feld[x][y]))
1971 inline static void InitField_WithBug2(int x, int y, boolean init_game)
1973 int old_element = Feld[x][y];
1975 InitField(x, y, init_game);
1977 /* not needed to call InitMovDir() -- already done by InitField()! */
1978 if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1979 CAN_MOVE(old_element) &&
1980 (old_element < EL_MOLE_LEFT || old_element > EL_MOLE_DOWN))
1983 /* this case is in fact a combination of not less than three bugs:
1984 first, it calls InitMovDir() for elements that can move, although this is
1985 already done by InitField(); then, it checks the element that was at this
1986 field _before_ the call to InitField() (which can change it); lastly, it
1987 was not called for "mole with direction" elements, which were treated as
1988 "cannot move" due to (fixed) wrong element initialization in "src/init.c"
1992 static int get_key_element_from_nr(int key_nr)
1994 int key_base_element = (key_nr >= STD_NUM_KEYS ? EL_EMC_KEY_5 - STD_NUM_KEYS :
1995 level.game_engine_type == GAME_ENGINE_TYPE_EM ?
1996 EL_EM_KEY_1 : EL_KEY_1);
1998 return key_base_element + key_nr;
2001 static int get_next_dropped_element(struct PlayerInfo *player)
2003 return (player->inventory_size > 0 ?
2004 player->inventory_element[player->inventory_size - 1] :
2005 player->inventory_infinite_element != EL_UNDEFINED ?
2006 player->inventory_infinite_element :
2007 player->dynabombs_left > 0 ?
2008 EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
2012 static int get_inventory_element_from_pos(struct PlayerInfo *player, int pos)
2014 /* pos >= 0: get element from bottom of the stack;
2015 pos < 0: get element from top of the stack */
2019 int min_inventory_size = -pos;
2020 int inventory_pos = player->inventory_size - min_inventory_size;
2021 int min_dynabombs_left = min_inventory_size - player->inventory_size;
2023 return (player->inventory_size >= min_inventory_size ?
2024 player->inventory_element[inventory_pos] :
2025 player->inventory_infinite_element != EL_UNDEFINED ?
2026 player->inventory_infinite_element :
2027 player->dynabombs_left >= min_dynabombs_left ?
2028 EL_DYNABOMB_PLAYER_1 + player->index_nr :
2033 int min_dynabombs_left = pos + 1;
2034 int min_inventory_size = pos + 1 - player->dynabombs_left;
2035 int inventory_pos = pos - player->dynabombs_left;
2037 return (player->inventory_infinite_element != EL_UNDEFINED ?
2038 player->inventory_infinite_element :
2039 player->dynabombs_left >= min_dynabombs_left ?
2040 EL_DYNABOMB_PLAYER_1 + player->index_nr :
2041 player->inventory_size >= min_inventory_size ?
2042 player->inventory_element[inventory_pos] :
2047 static int compareGamePanelOrderInfo(const void *object1, const void *object2)
2049 const struct GamePanelOrderInfo *gpo1 = (struct GamePanelOrderInfo *)object1;
2050 const struct GamePanelOrderInfo *gpo2 = (struct GamePanelOrderInfo *)object2;
2053 if (gpo1->sort_priority != gpo2->sort_priority)
2054 compare_result = gpo1->sort_priority - gpo2->sort_priority;
2056 compare_result = gpo1->nr - gpo2->nr;
2058 return compare_result;
2061 int getPlayerInventorySize(int player_nr)
2063 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2064 return level.native_em_level->ply[player_nr]->dynamite;
2065 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
2066 return level.native_sp_level->game_sp->red_disk_count;
2068 return stored_player[player_nr].inventory_size;
2071 void InitGameControlValues()
2075 for (i = 0; game_panel_controls[i].nr != -1; i++)
2077 struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2078 struct GamePanelOrderInfo *gpo = &game_panel_order[i];
2079 struct TextPosInfo *pos = gpc->pos;
2081 int type = gpc->type;
2085 Error(ERR_INFO, "'game_panel_controls' structure corrupted at %d", i);
2086 Error(ERR_EXIT, "this should not happen -- please debug");
2089 /* force update of game controls after initialization */
2090 gpc->value = gpc->last_value = -1;
2091 gpc->frame = gpc->last_frame = -1;
2092 gpc->gfx_frame = -1;
2094 /* determine panel value width for later calculation of alignment */
2095 if (type == TYPE_INTEGER || type == TYPE_STRING)
2097 pos->width = pos->size * getFontWidth(pos->font);
2098 pos->height = getFontHeight(pos->font);
2100 else if (type == TYPE_ELEMENT)
2102 pos->width = pos->size;
2103 pos->height = pos->size;
2106 /* fill structure for game panel draw order */
2108 gpo->sort_priority = pos->sort_priority;
2111 /* sort game panel controls according to sort_priority and control number */
2112 qsort(game_panel_order, NUM_GAME_PANEL_CONTROLS,
2113 sizeof(struct GamePanelOrderInfo), compareGamePanelOrderInfo);
2116 void UpdatePlayfieldElementCount()
2118 boolean use_element_count = FALSE;
2121 /* first check if it is needed at all to calculate playfield element count */
2122 for (i = GAME_PANEL_ELEMENT_COUNT_1; i <= GAME_PANEL_ELEMENT_COUNT_8; i++)
2123 if (!PANEL_DEACTIVATED(game_panel_controls[i].pos))
2124 use_element_count = TRUE;
2126 if (!use_element_count)
2129 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2130 element_info[i].element_count = 0;
2132 SCAN_PLAYFIELD(x, y)
2134 element_info[Feld[x][y]].element_count++;
2137 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
2138 for (j = 0; j < MAX_NUM_ELEMENTS; j++)
2139 if (IS_IN_GROUP(j, i))
2140 element_info[EL_GROUP_START + i].element_count +=
2141 element_info[j].element_count;
2144 void UpdateGameControlValues()
2147 int time = (local_player->LevelSolved ?
2148 local_player->LevelSolved_CountingTime :
2149 level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2150 level.native_em_level->lev->time :
2151 level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2152 level.native_sp_level->game_sp->time_played :
2153 game.no_time_limit ? TimePlayed : TimeLeft);
2154 int score = (local_player->LevelSolved ?
2155 local_player->LevelSolved_CountingScore :
2156 level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2157 level.native_em_level->lev->score :
2158 level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2159 level.native_sp_level->game_sp->score :
2160 local_player->score);
2161 int gems = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2162 level.native_em_level->lev->required :
2163 level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2164 level.native_sp_level->game_sp->infotrons_still_needed :
2165 local_player->gems_still_needed);
2166 int exit_closed = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2167 level.native_em_level->lev->required > 0 :
2168 level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2169 level.native_sp_level->game_sp->infotrons_still_needed > 0 :
2170 local_player->gems_still_needed > 0 ||
2171 local_player->sokobanfields_still_needed > 0 ||
2172 local_player->lights_still_needed > 0);
2174 UpdatePlayfieldElementCount();
2176 /* update game panel control values */
2178 game_panel_controls[GAME_PANEL_LEVEL_NUMBER].value = level_nr;
2179 game_panel_controls[GAME_PANEL_GEMS].value = gems;
2181 game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value = 0;
2182 for (i = 0; i < MAX_NUM_KEYS; i++)
2183 game_panel_controls[GAME_PANEL_KEY_1 + i].value = EL_EMPTY;
2184 game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_EMPTY;
2185 game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value = 0;
2187 if (game.centered_player_nr == -1)
2189 for (i = 0; i < MAX_PLAYERS; i++)
2191 /* only one player in Supaplex game engine */
2192 if (level.game_engine_type == GAME_ENGINE_TYPE_SP && i > 0)
2195 for (k = 0; k < MAX_NUM_KEYS; k++)
2197 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2199 if (level.native_em_level->ply[i]->keys & (1 << k))
2200 game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2201 get_key_element_from_nr(k);
2203 else if (stored_player[i].key[k])
2204 game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2205 get_key_element_from_nr(k);
2208 game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2209 getPlayerInventorySize(i);
2211 if (stored_player[i].num_white_keys > 0)
2212 game_panel_controls[GAME_PANEL_KEY_WHITE].value =
2215 game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2216 stored_player[i].num_white_keys;
2221 int player_nr = game.centered_player_nr;
2223 for (k = 0; k < MAX_NUM_KEYS; k++)
2225 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2227 if (level.native_em_level->ply[player_nr]->keys & (1 << k))
2228 game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2229 get_key_element_from_nr(k);
2231 else if (stored_player[player_nr].key[k])
2232 game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2233 get_key_element_from_nr(k);
2236 game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2237 getPlayerInventorySize(player_nr);
2239 if (stored_player[player_nr].num_white_keys > 0)
2240 game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_DC_KEY_WHITE;
2242 game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2243 stored_player[player_nr].num_white_keys;
2246 for (i = 0; i < NUM_PANEL_INVENTORY; i++)
2248 game_panel_controls[GAME_PANEL_INVENTORY_FIRST_1 + i].value =
2249 get_inventory_element_from_pos(local_player, i);
2250 game_panel_controls[GAME_PANEL_INVENTORY_LAST_1 + i].value =
2251 get_inventory_element_from_pos(local_player, -i - 1);
2254 game_panel_controls[GAME_PANEL_SCORE].value = score;
2255 game_panel_controls[GAME_PANEL_HIGHSCORE].value = highscore[0].Score;
2257 game_panel_controls[GAME_PANEL_TIME].value = time;
2259 game_panel_controls[GAME_PANEL_TIME_HH].value = time / 3600;
2260 game_panel_controls[GAME_PANEL_TIME_MM].value = (time / 60) % 60;
2261 game_panel_controls[GAME_PANEL_TIME_SS].value = time % 60;
2263 game_panel_controls[GAME_PANEL_FRAME].value = FrameCounter;
2265 game_panel_controls[GAME_PANEL_SHIELD_NORMAL].value =
2266 (local_player->shield_normal_time_left > 0 ? EL_SHIELD_NORMAL_ACTIVE :
2268 game_panel_controls[GAME_PANEL_SHIELD_NORMAL_TIME].value =
2269 local_player->shield_normal_time_left;
2270 game_panel_controls[GAME_PANEL_SHIELD_DEADLY].value =
2271 (local_player->shield_deadly_time_left > 0 ? EL_SHIELD_DEADLY_ACTIVE :
2273 game_panel_controls[GAME_PANEL_SHIELD_DEADLY_TIME].value =
2274 local_player->shield_deadly_time_left;
2276 game_panel_controls[GAME_PANEL_EXIT].value =
2277 (exit_closed ? EL_EXIT_CLOSED : EL_EXIT_OPEN);
2279 game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL].value =
2280 (game.ball_state ? EL_EMC_MAGIC_BALL_ACTIVE : EL_EMC_MAGIC_BALL);
2281 game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL_SWITCH].value =
2282 (game.ball_state ? EL_EMC_MAGIC_BALL_SWITCH_ACTIVE :
2283 EL_EMC_MAGIC_BALL_SWITCH);
2285 game_panel_controls[GAME_PANEL_LIGHT_SWITCH].value =
2286 (game.light_time_left > 0 ? EL_LIGHT_SWITCH_ACTIVE : EL_LIGHT_SWITCH);
2287 game_panel_controls[GAME_PANEL_LIGHT_SWITCH_TIME].value =
2288 game.light_time_left;
2290 game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH].value =
2291 (game.timegate_time_left > 0 ? EL_TIMEGATE_OPEN : EL_TIMEGATE_CLOSED);
2292 game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH_TIME].value =
2293 game.timegate_time_left;
2295 game_panel_controls[GAME_PANEL_SWITCHGATE_SWITCH].value =
2296 EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
2298 game_panel_controls[GAME_PANEL_EMC_LENSES].value =
2299 (game.lenses_time_left > 0 ? EL_EMC_LENSES : EL_EMPTY);
2300 game_panel_controls[GAME_PANEL_EMC_LENSES_TIME].value =
2301 game.lenses_time_left;
2303 game_panel_controls[GAME_PANEL_EMC_MAGNIFIER].value =
2304 (game.magnify_time_left > 0 ? EL_EMC_MAGNIFIER : EL_EMPTY);
2305 game_panel_controls[GAME_PANEL_EMC_MAGNIFIER_TIME].value =
2306 game.magnify_time_left;
2308 game_panel_controls[GAME_PANEL_BALLOON_SWITCH].value =
2309 (game.wind_direction == MV_LEFT ? EL_BALLOON_SWITCH_LEFT :
2310 game.wind_direction == MV_RIGHT ? EL_BALLOON_SWITCH_RIGHT :
2311 game.wind_direction == MV_UP ? EL_BALLOON_SWITCH_UP :
2312 game.wind_direction == MV_DOWN ? EL_BALLOON_SWITCH_DOWN :
2313 EL_BALLOON_SWITCH_NONE);
2315 game_panel_controls[GAME_PANEL_DYNABOMB_NUMBER].value =
2316 local_player->dynabomb_count;
2317 game_panel_controls[GAME_PANEL_DYNABOMB_SIZE].value =
2318 local_player->dynabomb_size;
2319 game_panel_controls[GAME_PANEL_DYNABOMB_POWER].value =
2320 (local_player->dynabomb_xl ? EL_DYNABOMB_INCREASE_POWER : EL_EMPTY);
2322 game_panel_controls[GAME_PANEL_PENGUINS].value =
2323 local_player->friends_still_needed;
2325 game_panel_controls[GAME_PANEL_SOKOBAN_OBJECTS].value =
2326 local_player->sokobanfields_still_needed;
2327 game_panel_controls[GAME_PANEL_SOKOBAN_FIELDS].value =
2328 local_player->sokobanfields_still_needed;
2330 game_panel_controls[GAME_PANEL_ROBOT_WHEEL].value =
2331 (game.robot_wheel_active ? EL_ROBOT_WHEEL_ACTIVE : EL_ROBOT_WHEEL);
2333 for (i = 0; i < NUM_BELTS; i++)
2335 game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1 + i].value =
2336 (game.belt_dir[i] != MV_NONE ? EL_CONVEYOR_BELT_1_MIDDLE_ACTIVE :
2337 EL_CONVEYOR_BELT_1_MIDDLE) + i;
2338 game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1_SWITCH + i].value =
2339 getBeltSwitchElementFromBeltNrAndBeltDir(i, game.belt_dir[i]);
2342 game_panel_controls[GAME_PANEL_MAGIC_WALL].value =
2343 (game.magic_wall_active ? EL_MAGIC_WALL_ACTIVE : EL_MAGIC_WALL);
2344 game_panel_controls[GAME_PANEL_MAGIC_WALL_TIME].value =
2345 game.magic_wall_time_left;
2347 game_panel_controls[GAME_PANEL_GRAVITY_STATE].value =
2348 local_player->gravity;
2350 for (i = 0; i < NUM_PANEL_GRAPHICS; i++)
2351 game_panel_controls[GAME_PANEL_GRAPHIC_1 + i].value = EL_GRAPHIC_1 + i;
2353 for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2354 game_panel_controls[GAME_PANEL_ELEMENT_1 + i].value =
2355 (IS_DRAWABLE_ELEMENT(game.panel.element[i].id) ?
2356 game.panel.element[i].id : EL_UNDEFINED);
2358 for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2359 game_panel_controls[GAME_PANEL_ELEMENT_COUNT_1 + i].value =
2360 (IS_VALID_ELEMENT(game.panel.element_count[i].id) ?
2361 element_info[game.panel.element_count[i].id].element_count : 0);
2363 for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2364 game_panel_controls[GAME_PANEL_CE_SCORE_1 + i].value =
2365 (IS_CUSTOM_ELEMENT(game.panel.ce_score[i].id) ?
2366 element_info[game.panel.ce_score[i].id].collect_score : 0);
2368 for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2369 game_panel_controls[GAME_PANEL_CE_SCORE_1_ELEMENT + i].value =
2370 (IS_CUSTOM_ELEMENT(game.panel.ce_score_element[i].id) ?
2371 element_info[game.panel.ce_score_element[i].id].collect_score :
2374 game_panel_controls[GAME_PANEL_PLAYER_NAME].value = 0;
2375 game_panel_controls[GAME_PANEL_LEVEL_NAME].value = 0;
2376 game_panel_controls[GAME_PANEL_LEVEL_AUTHOR].value = 0;
2378 /* update game panel control frames */
2380 for (i = 0; game_panel_controls[i].nr != -1; i++)
2382 struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2384 if (gpc->type == TYPE_ELEMENT)
2386 if (gpc->value != EL_UNDEFINED && gpc->value != EL_EMPTY)
2388 int last_anim_random_frame = gfx.anim_random_frame;
2389 int element = gpc->value;
2390 int graphic = el2panelimg(element);
2392 if (gpc->value != gpc->last_value)
2395 gpc->gfx_random = INIT_GFX_RANDOM();
2401 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2402 IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2403 gpc->gfx_random = INIT_GFX_RANDOM();
2406 if (ANIM_MODE(graphic) == ANIM_RANDOM)
2407 gfx.anim_random_frame = gpc->gfx_random;
2409 if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
2410 gpc->gfx_frame = element_info[element].collect_score;
2412 gpc->frame = getGraphicAnimationFrame(el2panelimg(gpc->value),
2415 if (ANIM_MODE(graphic) == ANIM_RANDOM)
2416 gfx.anim_random_frame = last_anim_random_frame;
2422 void DisplayGameControlValues()
2424 boolean redraw_panel = FALSE;
2427 for (i = 0; game_panel_controls[i].nr != -1; i++)
2429 struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2431 if (PANEL_DEACTIVATED(gpc->pos))
2434 if (gpc->value == gpc->last_value &&
2435 gpc->frame == gpc->last_frame)
2438 redraw_panel = TRUE;
2444 /* copy default game door content to main double buffer */
2446 /* !!! CHECK AGAIN !!! */
2447 SetPanelBackground();
2448 // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
2449 DrawBackground(DX, DY, DXSIZE, DYSIZE);
2451 /* redraw game control buttons */
2452 RedrawGameButtons();
2454 SetGameStatus(GAME_MODE_PSEUDO_PANEL);
2456 for (i = 0; i < NUM_GAME_PANEL_CONTROLS; i++)
2458 int nr = game_panel_order[i].nr;
2459 struct GamePanelControlInfo *gpc = &game_panel_controls[nr];
2460 struct TextPosInfo *pos = gpc->pos;
2461 int type = gpc->type;
2462 int value = gpc->value;
2463 int frame = gpc->frame;
2464 int size = pos->size;
2465 int font = pos->font;
2466 boolean draw_masked = pos->draw_masked;
2467 int mask_mode = (draw_masked ? BLIT_MASKED : BLIT_OPAQUE);
2469 if (PANEL_DEACTIVATED(pos))
2472 gpc->last_value = value;
2473 gpc->last_frame = frame;
2475 if (type == TYPE_INTEGER)
2477 if (nr == GAME_PANEL_LEVEL_NUMBER ||
2478 nr == GAME_PANEL_TIME)
2480 boolean use_dynamic_size = (size == -1 ? TRUE : FALSE);
2482 if (use_dynamic_size) /* use dynamic number of digits */
2484 int value_change = (nr == GAME_PANEL_LEVEL_NUMBER ? 100 : 1000);
2485 int size1 = (nr == GAME_PANEL_LEVEL_NUMBER ? 2 : 3);
2486 int size2 = size1 + 1;
2487 int font1 = pos->font;
2488 int font2 = pos->font_alt;
2490 size = (value < value_change ? size1 : size2);
2491 font = (value < value_change ? font1 : font2);
2495 /* correct text size if "digits" is zero or less */
2497 size = strlen(int2str(value, size));
2499 /* dynamically correct text alignment */
2500 pos->width = size * getFontWidth(font);
2502 DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2503 int2str(value, size), font, mask_mode);
2505 else if (type == TYPE_ELEMENT)
2507 int element, graphic;
2511 int dst_x = PANEL_XPOS(pos);
2512 int dst_y = PANEL_YPOS(pos);
2514 if (value != EL_UNDEFINED && value != EL_EMPTY)
2517 graphic = el2panelimg(value);
2519 // printf("::: %d, '%s' [%d]\n", element, EL_NAME(element), size);
2521 if (element >= EL_GRAPHIC_1 && element <= EL_GRAPHIC_8 && size == 0)
2524 getSizedGraphicSource(graphic, frame, size, &src_bitmap,
2527 width = graphic_info[graphic].width * size / TILESIZE;
2528 height = graphic_info[graphic].height * size / TILESIZE;
2531 BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2534 BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2538 else if (type == TYPE_STRING)
2540 boolean active = (value != 0);
2541 char *state_normal = "off";
2542 char *state_active = "on";
2543 char *state = (active ? state_active : state_normal);
2544 char *s = (nr == GAME_PANEL_GRAVITY_STATE ? state :
2545 nr == GAME_PANEL_PLAYER_NAME ? setup.player_name :
2546 nr == GAME_PANEL_LEVEL_NAME ? level.name :
2547 nr == GAME_PANEL_LEVEL_AUTHOR ? level.author : NULL);
2549 if (nr == GAME_PANEL_GRAVITY_STATE)
2551 int font1 = pos->font; /* (used for normal state) */
2552 int font2 = pos->font_alt; /* (used for active state) */
2554 font = (active ? font2 : font1);
2563 /* don't truncate output if "chars" is zero or less */
2566 /* dynamically correct text alignment */
2567 pos->width = size * getFontWidth(font);
2570 s_cut = getStringCopyN(s, size);
2572 DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2573 s_cut, font, mask_mode);
2579 redraw_mask |= REDRAW_DOOR_1;
2582 SetGameStatus(GAME_MODE_PLAYING);
2585 void UpdateAndDisplayGameControlValues()
2587 if (tape.deactivate_display)
2590 UpdateGameControlValues();
2591 DisplayGameControlValues();
2594 void UpdateGameDoorValues()
2596 UpdateGameControlValues();
2599 void DrawGameDoorValues()
2601 DisplayGameControlValues();
2606 =============================================================================
2608 -----------------------------------------------------------------------------
2609 initialize game engine due to level / tape version number
2610 =============================================================================
2613 static void InitGameEngine()
2615 int i, j, k, l, x, y;
2617 /* set game engine from tape file when re-playing, else from level file */
2618 game.engine_version = (tape.playing ? tape.engine_version :
2619 level.game_version);
2621 /* set single or multi-player game mode (needed for re-playing tapes) */
2622 game.team_mode = setup.team_mode;
2626 int num_players = 0;
2628 for (i = 0; i < MAX_PLAYERS; i++)
2629 if (tape.player_participates[i])
2632 /* multi-player tapes contain input data for more than one player */
2633 game.team_mode = (num_players > 1);
2636 /* ---------------------------------------------------------------------- */
2637 /* set flags for bugs and changes according to active game engine version */
2638 /* ---------------------------------------------------------------------- */
2641 Summary of bugfix/change:
2642 Fixed handling for custom elements that change when pushed by the player.
2644 Fixed/changed in version:
2648 Before 3.1.0, custom elements that "change when pushing" changed directly
2649 after the player started pushing them (until then handled in "DigField()").
2650 Since 3.1.0, these custom elements are not changed until the "pushing"
2651 move of the element is finished (now handled in "ContinueMoving()").
2653 Affected levels/tapes:
2654 The first condition is generally needed for all levels/tapes before version
2655 3.1.0, which might use the old behaviour before it was changed; known tapes
2656 that are affected are some tapes from the level set "Walpurgis Gardens" by
2658 The second condition is an exception from the above case and is needed for
2659 the special case of tapes recorded with game (not engine!) version 3.1.0 or
2660 above (including some development versions of 3.1.0), but before it was
2661 known that this change would break tapes like the above and was fixed in
2662 3.1.1, so that the changed behaviour was active although the engine version
2663 while recording maybe was before 3.1.0. There is at least one tape that is
2664 affected by this exception, which is the tape for the one-level set "Bug
2665 Machine" by Juergen Bonhagen.
2668 game.use_change_when_pushing_bug =
2669 (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2671 tape.game_version >= VERSION_IDENT(3,1,0,0) &&
2672 tape.game_version < VERSION_IDENT(3,1,1,0)));
2675 Summary of bugfix/change:
2676 Fixed handling for blocking the field the player leaves when moving.
2678 Fixed/changed in version:
2682 Before 3.1.1, when "block last field when moving" was enabled, the field
2683 the player is leaving when moving was blocked for the time of the move,
2684 and was directly unblocked afterwards. This resulted in the last field
2685 being blocked for exactly one less than the number of frames of one player
2686 move. Additionally, even when blocking was disabled, the last field was
2687 blocked for exactly one frame.
2688 Since 3.1.1, due to changes in player movement handling, the last field
2689 is not blocked at all when blocking is disabled. When blocking is enabled,
2690 the last field is blocked for exactly the number of frames of one player
2691 move. Additionally, if the player is Murphy, the hero of Supaplex, the
2692 last field is blocked for exactly one more than the number of frames of
2695 Affected levels/tapes:
2696 (!!! yet to be determined -- probably many !!!)
2699 game.use_block_last_field_bug =
2700 (game.engine_version < VERSION_IDENT(3,1,1,0));
2702 game_em.use_single_button =
2703 (game.engine_version > VERSION_IDENT(4,0,0,2));
2705 game_em.use_snap_key_bug =
2706 (game.engine_version < VERSION_IDENT(4,0,1,0));
2708 /* ---------------------------------------------------------------------- */
2710 /* set maximal allowed number of custom element changes per game frame */
2711 game.max_num_changes_per_frame = 1;
2713 /* default scan direction: scan playfield from top/left to bottom/right */
2714 InitPlayfieldScanMode(CA_ARG_SCAN_MODE_NORMAL);
2716 /* dynamically adjust element properties according to game engine version */
2717 InitElementPropertiesEngine(game.engine_version);
2720 printf("level %d: level version == %06d\n", level_nr, level.game_version);
2721 printf(" tape version == %06d [%s] [file: %06d]\n",
2722 tape.engine_version, (tape.playing ? "PLAYING" : "RECORDING"),
2724 printf(" => game.engine_version == %06d\n", game.engine_version);
2727 /* ---------- initialize player's initial move delay --------------------- */
2729 /* dynamically adjust player properties according to level information */
2730 for (i = 0; i < MAX_PLAYERS; i++)
2731 game.initial_move_delay_value[i] =
2732 get_move_delay_from_stepsize(level.initial_player_stepsize[i]);
2734 /* dynamically adjust player properties according to game engine version */
2735 for (i = 0; i < MAX_PLAYERS; i++)
2736 game.initial_move_delay[i] =
2737 (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
2738 game.initial_move_delay_value[i] : 0);
2740 /* ---------- initialize player's initial push delay --------------------- */
2742 /* dynamically adjust player properties according to game engine version */
2743 game.initial_push_delay_value =
2744 (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
2746 /* ---------- initialize changing elements ------------------------------- */
2748 /* initialize changing elements information */
2749 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2751 struct ElementInfo *ei = &element_info[i];
2753 /* this pointer might have been changed in the level editor */
2754 ei->change = &ei->change_page[0];
2756 if (!IS_CUSTOM_ELEMENT(i))
2758 ei->change->target_element = EL_EMPTY_SPACE;
2759 ei->change->delay_fixed = 0;
2760 ei->change->delay_random = 0;
2761 ei->change->delay_frames = 1;
2764 for (j = 0; j < NUM_CHANGE_EVENTS; j++)
2766 ei->has_change_event[j] = FALSE;
2768 ei->event_page_nr[j] = 0;
2769 ei->event_page[j] = &ei->change_page[0];
2773 /* add changing elements from pre-defined list */
2774 for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
2776 struct ChangingElementInfo *ch_delay = &change_delay_list[i];
2777 struct ElementInfo *ei = &element_info[ch_delay->element];
2779 ei->change->target_element = ch_delay->target_element;
2780 ei->change->delay_fixed = ch_delay->change_delay;
2782 ei->change->pre_change_function = ch_delay->pre_change_function;
2783 ei->change->change_function = ch_delay->change_function;
2784 ei->change->post_change_function = ch_delay->post_change_function;
2786 ei->change->can_change = TRUE;
2787 ei->change->can_change_or_has_action = TRUE;
2789 ei->has_change_event[CE_DELAY] = TRUE;
2791 SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
2792 SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE_OR_HAS_ACTION, TRUE);
2795 /* ---------- initialize internal run-time variables --------------------- */
2797 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2799 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
2801 for (j = 0; j < ei->num_change_pages; j++)
2803 ei->change_page[j].can_change_or_has_action =
2804 (ei->change_page[j].can_change |
2805 ei->change_page[j].has_action);
2809 /* add change events from custom element configuration */
2810 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2812 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
2814 for (j = 0; j < ei->num_change_pages; j++)
2816 if (!ei->change_page[j].can_change_or_has_action)
2819 for (k = 0; k < NUM_CHANGE_EVENTS; k++)
2821 /* only add event page for the first page found with this event */
2822 if (ei->change_page[j].has_event[k] && !(ei->has_change_event[k]))
2824 ei->has_change_event[k] = TRUE;
2826 ei->event_page_nr[k] = j;
2827 ei->event_page[k] = &ei->change_page[j];
2833 /* ---------- initialize reference elements in change conditions --------- */
2835 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2837 int element = EL_CUSTOM_START + i;
2838 struct ElementInfo *ei = &element_info[element];
2840 for (j = 0; j < ei->num_change_pages; j++)
2842 int trigger_element = ei->change_page[j].initial_trigger_element;
2844 if (trigger_element >= EL_PREV_CE_8 &&
2845 trigger_element <= EL_NEXT_CE_8)
2846 trigger_element = RESOLVED_REFERENCE_ELEMENT(element, trigger_element);
2848 ei->change_page[j].trigger_element = trigger_element;
2852 /* ---------- initialize run-time trigger player and element ------------- */
2854 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2856 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
2858 for (j = 0; j < ei->num_change_pages; j++)
2860 ei->change_page[j].actual_trigger_element = EL_EMPTY;
2861 ei->change_page[j].actual_trigger_player = EL_EMPTY;
2862 ei->change_page[j].actual_trigger_player_bits = CH_PLAYER_NONE;
2863 ei->change_page[j].actual_trigger_side = CH_SIDE_NONE;
2864 ei->change_page[j].actual_trigger_ce_value = 0;
2865 ei->change_page[j].actual_trigger_ce_score = 0;
2869 /* ---------- initialize trigger events ---------------------------------- */
2871 /* initialize trigger events information */
2872 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2873 for (j = 0; j < NUM_CHANGE_EVENTS; j++)
2874 trigger_events[i][j] = FALSE;
2876 /* add trigger events from element change event properties */
2877 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2879 struct ElementInfo *ei = &element_info[i];
2881 for (j = 0; j < ei->num_change_pages; j++)
2883 if (!ei->change_page[j].can_change_or_has_action)
2886 if (ei->change_page[j].has_event[CE_BY_OTHER_ACTION])
2888 int trigger_element = ei->change_page[j].trigger_element;
2890 for (k = 0; k < NUM_CHANGE_EVENTS; k++)
2892 if (ei->change_page[j].has_event[k])
2894 if (IS_GROUP_ELEMENT(trigger_element))
2896 struct ElementGroupInfo *group =
2897 element_info[trigger_element].group;
2899 for (l = 0; l < group->num_elements_resolved; l++)
2900 trigger_events[group->element_resolved[l]][k] = TRUE;
2902 else if (trigger_element == EL_ANY_ELEMENT)
2903 for (l = 0; l < MAX_NUM_ELEMENTS; l++)
2904 trigger_events[l][k] = TRUE;
2906 trigger_events[trigger_element][k] = TRUE;
2913 /* ---------- initialize push delay -------------------------------------- */
2915 /* initialize push delay values to default */
2916 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2918 if (!IS_CUSTOM_ELEMENT(i))
2920 /* set default push delay values (corrected since version 3.0.7-1) */
2921 if (game.engine_version < VERSION_IDENT(3,0,7,1))
2923 element_info[i].push_delay_fixed = 2;
2924 element_info[i].push_delay_random = 8;
2928 element_info[i].push_delay_fixed = 8;
2929 element_info[i].push_delay_random = 8;
2934 /* set push delay value for certain elements from pre-defined list */
2935 for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
2937 int e = push_delay_list[i].element;
2939 element_info[e].push_delay_fixed = push_delay_list[i].push_delay_fixed;
2940 element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
2943 /* set push delay value for Supaplex elements for newer engine versions */
2944 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
2946 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2948 if (IS_SP_ELEMENT(i))
2950 /* set SP push delay to just enough to push under a falling zonk */
2951 int delay = (game.engine_version >= VERSION_IDENT(3,1,1,0) ? 8 : 6);
2953 element_info[i].push_delay_fixed = delay;
2954 element_info[i].push_delay_random = 0;
2959 /* ---------- initialize move stepsize ----------------------------------- */
2961 /* initialize move stepsize values to default */
2962 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2963 if (!IS_CUSTOM_ELEMENT(i))
2964 element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
2966 /* set move stepsize value for certain elements from pre-defined list */
2967 for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
2969 int e = move_stepsize_list[i].element;
2971 element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
2974 /* ---------- initialize collect score ----------------------------------- */
2976 /* initialize collect score values for custom elements from initial value */
2977 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2978 if (IS_CUSTOM_ELEMENT(i))
2979 element_info[i].collect_score = element_info[i].collect_score_initial;
2981 /* ---------- initialize collect count ----------------------------------- */
2983 /* initialize collect count values for non-custom elements */
2984 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2985 if (!IS_CUSTOM_ELEMENT(i))
2986 element_info[i].collect_count_initial = 0;
2988 /* add collect count values for all elements from pre-defined list */
2989 for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
2990 element_info[collect_count_list[i].element].collect_count_initial =
2991 collect_count_list[i].count;
2993 /* ---------- initialize access direction -------------------------------- */
2995 /* initialize access direction values to default (access from every side) */
2996 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2997 if (!IS_CUSTOM_ELEMENT(i))
2998 element_info[i].access_direction = MV_ALL_DIRECTIONS;
3000 /* set access direction value for certain elements from pre-defined list */
3001 for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
3002 element_info[access_direction_list[i].element].access_direction =
3003 access_direction_list[i].direction;
3005 /* ---------- initialize explosion content ------------------------------- */
3006 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3008 if (IS_CUSTOM_ELEMENT(i))
3011 for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
3013 /* (content for EL_YAMYAM set at run-time with game.yamyam_content_nr) */
3015 element_info[i].content.e[x][y] =
3016 (i == EL_PLAYER_1 ? EL_EMERALD_YELLOW :
3017 i == EL_PLAYER_2 ? EL_EMERALD_RED :
3018 i == EL_PLAYER_3 ? EL_EMERALD :
3019 i == EL_PLAYER_4 ? EL_EMERALD_PURPLE :
3020 i == EL_MOLE ? EL_EMERALD_RED :
3021 i == EL_PENGUIN ? EL_EMERALD_PURPLE :
3022 i == EL_BUG ? (x == 1 && y == 1 ? EL_DIAMOND : EL_EMERALD) :
3023 i == EL_BD_BUTTERFLY ? EL_BD_DIAMOND :
3024 i == EL_SP_ELECTRON ? EL_SP_INFOTRON :
3025 i == EL_AMOEBA_TO_DIAMOND ? level.amoeba_content :
3026 i == EL_WALL_EMERALD ? EL_EMERALD :
3027 i == EL_WALL_DIAMOND ? EL_DIAMOND :
3028 i == EL_WALL_BD_DIAMOND ? EL_BD_DIAMOND :
3029 i == EL_WALL_EMERALD_YELLOW ? EL_EMERALD_YELLOW :
3030 i == EL_WALL_EMERALD_RED ? EL_EMERALD_RED :
3031 i == EL_WALL_EMERALD_PURPLE ? EL_EMERALD_PURPLE :
3032 i == EL_WALL_PEARL ? EL_PEARL :
3033 i == EL_WALL_CRYSTAL ? EL_CRYSTAL :
3038 /* ---------- initialize recursion detection ------------------------------ */
3039 recursion_loop_depth = 0;
3040 recursion_loop_detected = FALSE;
3041 recursion_loop_element = EL_UNDEFINED;
3043 /* ---------- initialize graphics engine ---------------------------------- */
3044 game.scroll_delay_value =
3045 (game.forced_scroll_delay_value != -1 ? game.forced_scroll_delay_value :
3046 setup.scroll_delay ? setup.scroll_delay_value : 0);
3047 game.scroll_delay_value =
3048 MIN(MAX(MIN_SCROLL_DELAY, game.scroll_delay_value), MAX_SCROLL_DELAY);
3050 /* ---------- initialize game engine snapshots ---------------------------- */
3051 for (i = 0; i < MAX_PLAYERS; i++)
3052 game.snapshot.last_action[i] = 0;
3053 game.snapshot.changed_action = FALSE;
3054 game.snapshot.collected_item = FALSE;
3055 game.snapshot.mode =
3056 (strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_STEP) ?
3057 SNAPSHOT_MODE_EVERY_STEP :
3058 strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_MOVE) ?
3059 SNAPSHOT_MODE_EVERY_MOVE :
3060 strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_COLLECT) ?
3061 SNAPSHOT_MODE_EVERY_COLLECT : SNAPSHOT_MODE_OFF);
3062 game.snapshot.save_snapshot = FALSE;
3064 /* ---------- initialize level time for Supaplex engine ------------------- */
3065 /* Supaplex levels with time limit currently unsupported -- should be added */
3066 if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
3070 int get_num_special_action(int element, int action_first, int action_last)
3072 int num_special_action = 0;
3075 for (i = action_first; i <= action_last; i++)
3077 boolean found = FALSE;
3079 for (j = 0; j < NUM_DIRECTIONS; j++)
3080 if (el_act_dir2img(element, i, j) !=
3081 el_act_dir2img(element, ACTION_DEFAULT, j))
3085 num_special_action++;
3090 return num_special_action;
3095 =============================================================================
3097 -----------------------------------------------------------------------------
3098 initialize and start new game
3099 =============================================================================
3104 int full_lev_fieldx = lev_fieldx + (BorderElement != EL_EMPTY ? 2 : 0);
3105 int full_lev_fieldy = lev_fieldy + (BorderElement != EL_EMPTY ? 2 : 0);
3106 int fade_mask = REDRAW_FIELD;
3108 boolean emulate_bd = TRUE; /* unless non-BOULDERDASH elements found */
3109 boolean emulate_sb = TRUE; /* unless non-SOKOBAN elements found */
3110 boolean emulate_sp = TRUE; /* unless non-SUPAPLEX elements found */
3111 int initial_move_dir = MV_DOWN;
3114 // required here to update video display before fading (FIX THIS)
3115 DrawMaskedBorder(REDRAW_DOOR_2);
3117 if (!game.restart_level)
3118 CloseDoor(DOOR_CLOSE_1);
3120 SetGameStatus(GAME_MODE_PLAYING);
3122 if (level_editor_test_game)
3123 FadeSkipNextFadeIn();
3125 FadeSetEnterScreen();
3127 if (CheckIfGlobalBorderHasChanged())
3128 fade_mask = REDRAW_ALL;
3130 FadeLevelSoundsAndMusic();
3132 ExpireSoundLoops(TRUE);
3136 /* needed if different viewport properties defined for playing */
3137 ChangeViewportPropertiesIfNeeded();
3141 OpenDoor(GetDoorState() | DOOR_NO_DELAY | DOOR_FORCE_REDRAW);
3143 DrawCompleteVideoDisplay();
3146 InitGameControlValues();
3148 /* don't play tapes over network */
3149 network_playing = (options.network && !tape.playing);
3151 for (i = 0; i < MAX_PLAYERS; i++)
3153 struct PlayerInfo *player = &stored_player[i];
3155 player->index_nr = i;
3156 player->index_bit = (1 << i);
3157 player->element_nr = EL_PLAYER_1 + i;
3159 player->present = FALSE;
3160 player->active = FALSE;
3161 player->mapped = FALSE;
3163 player->killed = FALSE;
3164 player->reanimated = FALSE;
3167 player->effective_action = 0;
3168 player->programmed_action = 0;
3171 player->score_final = 0;
3173 player->gems_still_needed = level.gems_needed;
3174 player->sokobanfields_still_needed = 0;
3175 player->lights_still_needed = 0;
3176 player->friends_still_needed = 0;
3178 for (j = 0; j < MAX_NUM_KEYS; j++)
3179 player->key[j] = FALSE;
3181 player->num_white_keys = 0;
3183 player->dynabomb_count = 0;
3184 player->dynabomb_size = 1;
3185 player->dynabombs_left = 0;
3186 player->dynabomb_xl = FALSE;
3188 player->MovDir = initial_move_dir;
3191 player->GfxDir = initial_move_dir;
3192 player->GfxAction = ACTION_DEFAULT;
3194 player->StepFrame = 0;
3196 player->initial_element = player->element_nr;
3197 player->artwork_element =
3198 (level.use_artwork_element[i] ? level.artwork_element[i] :
3199 player->element_nr);
3200 player->use_murphy = FALSE;
3202 player->block_last_field = FALSE; /* initialized in InitPlayerField() */
3203 player->block_delay_adjustment = 0; /* initialized in InitPlayerField() */
3205 player->gravity = level.initial_player_gravity[i];
3207 player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
3209 player->actual_frame_counter = 0;
3211 player->step_counter = 0;
3213 player->last_move_dir = initial_move_dir;
3215 player->is_active = FALSE;
3217 player->is_waiting = FALSE;
3218 player->is_moving = FALSE;
3219 player->is_auto_moving = FALSE;
3220 player->is_digging = FALSE;
3221 player->is_snapping = FALSE;
3222 player->is_collecting = FALSE;
3223 player->is_pushing = FALSE;
3224 player->is_switching = FALSE;
3225 player->is_dropping = FALSE;
3226 player->is_dropping_pressed = FALSE;
3228 player->is_bored = FALSE;
3229 player->is_sleeping = FALSE;
3231 player->was_waiting = TRUE;
3232 player->was_moving = FALSE;
3233 player->was_snapping = FALSE;
3234 player->was_dropping = FALSE;
3236 player->force_dropping = FALSE;
3238 player->frame_counter_bored = -1;
3239 player->frame_counter_sleeping = -1;
3241 player->anim_delay_counter = 0;
3242 player->post_delay_counter = 0;
3244 player->dir_waiting = initial_move_dir;
3245 player->action_waiting = ACTION_DEFAULT;
3246 player->last_action_waiting = ACTION_DEFAULT;
3247 player->special_action_bored = ACTION_DEFAULT;
3248 player->special_action_sleeping = ACTION_DEFAULT;
3250 player->switch_x = -1;
3251 player->switch_y = -1;
3253 player->drop_x = -1;
3254 player->drop_y = -1;
3256 player->show_envelope = 0;
3258 SetPlayerMoveSpeed(player, level.initial_player_stepsize[i], TRUE);
3260 player->push_delay = -1; /* initialized when pushing starts */
3261 player->push_delay_value = game.initial_push_delay_value;
3263 player->drop_delay = 0;
3264 player->drop_pressed_delay = 0;
3266 player->last_jx = -1;
3267 player->last_jy = -1;
3271 player->shield_normal_time_left = 0;
3272 player->shield_deadly_time_left = 0;
3274 player->inventory_infinite_element = EL_UNDEFINED;
3275 player->inventory_size = 0;
3277 if (level.use_initial_inventory[i])
3279 for (j = 0; j < level.initial_inventory_size[i]; j++)
3281 int element = level.initial_inventory_content[i][j];
3282 int collect_count = element_info[element].collect_count_initial;
3285 if (!IS_CUSTOM_ELEMENT(element))
3288 if (collect_count == 0)
3289 player->inventory_infinite_element = element;
3291 for (k = 0; k < collect_count; k++)
3292 if (player->inventory_size < MAX_INVENTORY_SIZE)
3293 player->inventory_element[player->inventory_size++] = element;
3297 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
3298 SnapField(player, 0, 0);
3300 player->LevelSolved = FALSE;
3301 player->GameOver = FALSE;
3303 player->LevelSolved_GameWon = FALSE;
3304 player->LevelSolved_GameEnd = FALSE;
3305 player->LevelSolved_PanelOff = FALSE;
3306 player->LevelSolved_SaveTape = FALSE;
3307 player->LevelSolved_SaveScore = FALSE;
3308 player->LevelSolved_CountingTime = 0;
3309 player->LevelSolved_CountingScore = 0;
3311 map_player_action[i] = i;
3314 network_player_action_received = FALSE;
3316 #if defined(NETWORK_AVALIABLE)
3317 /* initial null action */
3318 if (network_playing)
3319 SendToServer_MovePlayer(MV_NONE);
3328 TimeLeft = level.time;
3331 ScreenMovDir = MV_NONE;
3335 ScrollStepSize = 0; /* will be correctly initialized by ScrollScreen() */
3337 AllPlayersGone = FALSE;
3339 game.no_time_limit = (level.time == 0);
3341 game.yamyam_content_nr = 0;
3342 game.robot_wheel_active = FALSE;
3343 game.magic_wall_active = FALSE;
3344 game.magic_wall_time_left = 0;
3345 game.light_time_left = 0;
3346 game.timegate_time_left = 0;
3347 game.switchgate_pos = 0;
3348 game.wind_direction = level.wind_direction_initial;
3350 game.lenses_time_left = 0;
3351 game.magnify_time_left = 0;
3353 game.ball_state = level.ball_state_initial;
3354 game.ball_content_nr = 0;
3356 game.envelope_active = FALSE;
3358 /* set focus to local player for network games, else to all players */
3359 game.centered_player_nr = (network_playing ? local_player->index_nr : -1);
3360 game.centered_player_nr_next = game.centered_player_nr;
3361 game.set_centered_player = FALSE;
3363 if (network_playing && tape.recording)
3365 /* store client dependent player focus when recording network games */
3366 tape.centered_player_nr_next = game.centered_player_nr_next;
3367 tape.set_centered_player = TRUE;
3370 for (i = 0; i < NUM_BELTS; i++)
3372 game.belt_dir[i] = MV_NONE;
3373 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
3376 for (i = 0; i < MAX_NUM_AMOEBA; i++)
3377 AmoebaCnt[i] = AmoebaCnt2[i] = 0;
3379 #if DEBUG_INIT_PLAYER
3382 printf("Player status at level initialization:\n");
3386 SCAN_PLAYFIELD(x, y)
3388 Feld[x][y] = level.field[x][y];
3389 MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
3390 ChangeDelay[x][y] = 0;
3391 ChangePage[x][y] = -1;
3392 CustomValue[x][y] = 0; /* initialized in InitField() */
3393 Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
3395 WasJustMoving[x][y] = 0;
3396 WasJustFalling[x][y] = 0;
3397 CheckCollision[x][y] = 0;
3398 CheckImpact[x][y] = 0;
3400 Pushed[x][y] = FALSE;
3402 ChangeCount[x][y] = 0;
3403 ChangeEvent[x][y] = -1;
3405 ExplodePhase[x][y] = 0;
3406 ExplodeDelay[x][y] = 0;
3407 ExplodeField[x][y] = EX_TYPE_NONE;
3409 RunnerVisit[x][y] = 0;
3410 PlayerVisit[x][y] = 0;
3413 GfxRandom[x][y] = INIT_GFX_RANDOM();
3414 GfxElement[x][y] = EL_UNDEFINED;
3415 GfxAction[x][y] = ACTION_DEFAULT;
3416 GfxDir[x][y] = MV_NONE;
3417 GfxRedraw[x][y] = GFX_REDRAW_NONE;
3420 SCAN_PLAYFIELD(x, y)
3422 if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
3424 if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
3426 if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
3429 InitField(x, y, TRUE);
3431 ResetGfxAnimation(x, y);
3436 for (i = 0; i < MAX_PLAYERS; i++)
3438 struct PlayerInfo *player = &stored_player[i];
3440 /* set number of special actions for bored and sleeping animation */
3441 player->num_special_action_bored =
3442 get_num_special_action(player->artwork_element,
3443 ACTION_BORING_1, ACTION_BORING_LAST);
3444 player->num_special_action_sleeping =
3445 get_num_special_action(player->artwork_element,
3446 ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
3449 game.emulation = (emulate_bd ? EMU_BOULDERDASH :
3450 emulate_sb ? EMU_SOKOBAN :
3451 emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
3453 /* initialize type of slippery elements */
3454 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3456 if (!IS_CUSTOM_ELEMENT(i))
3458 /* default: elements slip down either to the left or right randomly */
3459 element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
3461 /* SP style elements prefer to slip down on the left side */
3462 if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
3463 element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3465 /* BD style elements prefer to slip down on the left side */
3466 if (game.emulation == EMU_BOULDERDASH)
3467 element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3471 /* initialize explosion and ignition delay */
3472 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3474 if (!IS_CUSTOM_ELEMENT(i))
3477 int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
3478 game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
3479 game.emulation == EMU_SUPAPLEX ? 3 : 2);
3480 int last_phase = (num_phase + 1) * delay;
3481 int half_phase = (num_phase / 2) * delay;
3483 element_info[i].explosion_delay = last_phase - 1;
3484 element_info[i].ignition_delay = half_phase;
3486 if (i == EL_BLACK_ORB)
3487 element_info[i].ignition_delay = 1;
3491 /* correct non-moving belts to start moving left */
3492 for (i = 0; i < NUM_BELTS; i++)
3493 if (game.belt_dir[i] == MV_NONE)
3494 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
3496 #if USE_NEW_PLAYER_ASSIGNMENTS
3497 /* !!! SAME AS init.c:InitPlayerInfo() -- FIX THIS !!! */
3498 /* choose default local player */
3499 local_player = &stored_player[0];
3501 for (i = 0; i < MAX_PLAYERS; i++)
3502 stored_player[i].connected = FALSE;
3504 local_player->connected = TRUE;
3505 /* !!! SAME AS init.c:InitPlayerInfo() -- FIX THIS !!! */
3509 for (i = 0; i < MAX_PLAYERS; i++)
3510 stored_player[i].connected = tape.player_participates[i];
3512 else if (game.team_mode && !options.network)
3514 /* try to guess locally connected team mode players (needed for correct
3515 assignment of player figures from level to locally playing players) */
3517 for (i = 0; i < MAX_PLAYERS; i++)
3518 if (setup.input[i].use_joystick ||
3519 setup.input[i].key.left != KSYM_UNDEFINED)
3520 stored_player[i].connected = TRUE;
3523 #if DEBUG_INIT_PLAYER
3526 printf("Player status after level initialization:\n");
3528 for (i = 0; i < MAX_PLAYERS; i++)
3530 struct PlayerInfo *player = &stored_player[i];
3532 printf("- player %d: present == %d, connected == %d, active == %d",
3538 if (local_player == player)
3539 printf(" (local player)");
3546 #if DEBUG_INIT_PLAYER
3548 printf("Reassigning players ...\n");
3551 /* check if any connected player was not found in playfield */
3552 for (i = 0; i < MAX_PLAYERS; i++)
3554 struct PlayerInfo *player = &stored_player[i];
3556 if (player->connected && !player->present)
3558 struct PlayerInfo *field_player = NULL;
3560 #if DEBUG_INIT_PLAYER
3562 printf("- looking for field player for player %d ...\n", i + 1);
3565 /* assign first free player found that is present in the playfield */
3567 /* first try: look for unmapped playfield player that is not connected */
3568 for (j = 0; j < MAX_PLAYERS; j++)
3569 if (field_player == NULL &&
3570 stored_player[j].present &&
3571 !stored_player[j].mapped &&
3572 !stored_player[j].connected)
3573 field_player = &stored_player[j];
3575 /* second try: look for *any* unmapped playfield player */
3576 for (j = 0; j < MAX_PLAYERS; j++)
3577 if (field_player == NULL &&
3578 stored_player[j].present &&
3579 !stored_player[j].mapped)
3580 field_player = &stored_player[j];
3582 if (field_player != NULL)
3584 int jx = field_player->jx, jy = field_player->jy;
3586 #if DEBUG_INIT_PLAYER
3588 printf("- found player %d\n", field_player->index_nr + 1);
3591 player->present = FALSE;
3592 player->active = FALSE;
3594 field_player->present = TRUE;
3595 field_player->active = TRUE;
3598 player->initial_element = field_player->initial_element;
3599 player->artwork_element = field_player->artwork_element;
3601 player->block_last_field = field_player->block_last_field;
3602 player->block_delay_adjustment = field_player->block_delay_adjustment;
3605 StorePlayer[jx][jy] = field_player->element_nr;
3607 field_player->jx = field_player->last_jx = jx;
3608 field_player->jy = field_player->last_jy = jy;
3610 if (local_player == player)
3611 local_player = field_player;
3613 map_player_action[field_player->index_nr] = i;
3615 field_player->mapped = TRUE;
3617 #if DEBUG_INIT_PLAYER
3619 printf("- map_player_action[%d] == %d\n",
3620 field_player->index_nr + 1, i + 1);
3625 if (player->connected && player->present)
3626 player->mapped = TRUE;
3629 #if DEBUG_INIT_PLAYER
3632 printf("Player status after player assignment (first stage):\n");
3634 for (i = 0; i < MAX_PLAYERS; i++)
3636 struct PlayerInfo *player = &stored_player[i];
3638 printf("- player %d: present == %d, connected == %d, active == %d",
3644 if (local_player == player)
3645 printf(" (local player)");
3654 /* check if any connected player was not found in playfield */
3655 for (i = 0; i < MAX_PLAYERS; i++)
3657 struct PlayerInfo *player = &stored_player[i];
3659 if (player->connected && !player->present)
3661 for (j = 0; j < MAX_PLAYERS; j++)
3663 struct PlayerInfo *field_player = &stored_player[j];
3664 int jx = field_player->jx, jy = field_player->jy;
3666 /* assign first free player found that is present in the playfield */
3667 if (field_player->present && !field_player->connected)
3669 player->present = TRUE;
3670 player->active = TRUE;
3672 field_player->present = FALSE;
3673 field_player->active = FALSE;
3675 player->initial_element = field_player->initial_element;
3676 player->artwork_element = field_player->artwork_element;
3678 player->block_last_field = field_player->block_last_field;
3679 player->block_delay_adjustment = field_player->block_delay_adjustment;
3681 StorePlayer[jx][jy] = player->element_nr;
3683 player->jx = player->last_jx = jx;
3684 player->jy = player->last_jy = jy;
3694 printf("::: local_player->present == %d\n", local_player->present);
3699 /* when playing a tape, eliminate all players who do not participate */
3701 #if USE_NEW_PLAYER_ASSIGNMENTS
3703 if (!game.team_mode)
3705 for (i = 0; i < MAX_PLAYERS; i++)
3707 if (stored_player[i].active &&
3708 !tape.player_participates[map_player_action[i]])
3710 struct PlayerInfo *player = &stored_player[i];
3711 int jx = player->jx, jy = player->jy;
3713 #if DEBUG_INIT_PLAYER
3715 printf("Removing player %d at (%d, %d)\n", i + 1, jx, jy);
3718 player->active = FALSE;
3719 StorePlayer[jx][jy] = 0;
3720 Feld[jx][jy] = EL_EMPTY;
3727 for (i = 0; i < MAX_PLAYERS; i++)
3729 if (stored_player[i].active &&
3730 !tape.player_participates[i])
3732 struct PlayerInfo *player = &stored_player[i];
3733 int jx = player->jx, jy = player->jy;
3735 player->active = FALSE;
3736 StorePlayer[jx][jy] = 0;
3737 Feld[jx][jy] = EL_EMPTY;
3742 else if (!options.network && !game.team_mode) /* && !tape.playing */
3744 /* when in single player mode, eliminate all but the first active player */
3746 for (i = 0; i < MAX_PLAYERS; i++)
3748 if (stored_player[i].active)
3750 for (j = i + 1; j < MAX_PLAYERS; j++)
3752 if (stored_player[j].active)
3754 struct PlayerInfo *player = &stored_player[j];
3755 int jx = player->jx, jy = player->jy;
3757 player->active = FALSE;
3758 player->present = FALSE;
3760 StorePlayer[jx][jy] = 0;
3761 Feld[jx][jy] = EL_EMPTY;
3768 /* when recording the game, store which players take part in the game */
3771 #if USE_NEW_PLAYER_ASSIGNMENTS
3772 for (i = 0; i < MAX_PLAYERS; i++)
3773 if (stored_player[i].connected)
3774 tape.player_participates[i] = TRUE;
3776 for (i = 0; i < MAX_PLAYERS; i++)
3777 if (stored_player[i].active)
3778 tape.player_participates[i] = TRUE;
3782 #if DEBUG_INIT_PLAYER
3785 printf("Player status after player assignment (final stage):\n");
3787 for (i = 0; i < MAX_PLAYERS; i++)
3789 struct PlayerInfo *player = &stored_player[i];
3791 printf("- player %d: present == %d, connected == %d, active == %d",
3797 if (local_player == player)
3798 printf(" (local player)");
3805 if (BorderElement == EL_EMPTY)
3808 SBX_Right = lev_fieldx - SCR_FIELDX;
3810 SBY_Lower = lev_fieldy - SCR_FIELDY;
3815 SBX_Right = lev_fieldx - SCR_FIELDX + 1;
3817 SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
3820 if (full_lev_fieldx <= SCR_FIELDX)
3821 SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
3822 if (full_lev_fieldy <= SCR_FIELDY)
3823 SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
3825 if (EVEN(SCR_FIELDX) && full_lev_fieldx > SCR_FIELDX)
3827 if (EVEN(SCR_FIELDY) && full_lev_fieldy > SCR_FIELDY)
3830 /* if local player not found, look for custom element that might create
3831 the player (make some assumptions about the right custom element) */
3832 if (!local_player->present)
3834 int start_x = 0, start_y = 0;
3835 int found_rating = 0;
3836 int found_element = EL_UNDEFINED;
3837 int player_nr = local_player->index_nr;
3839 SCAN_PLAYFIELD(x, y)
3841 int element = Feld[x][y];
3846 if (level.use_start_element[player_nr] &&
3847 level.start_element[player_nr] == element &&
3854 found_element = element;
3857 if (!IS_CUSTOM_ELEMENT(element))
3860 if (CAN_CHANGE(element))
3862 for (i = 0; i < element_info[element].num_change_pages; i++)
3864 /* check for player created from custom element as single target */
3865 content = element_info[element].change_page[i].target_element;
3866 is_player = ELEM_IS_PLAYER(content);
3868 if (is_player && (found_rating < 3 ||
3869 (found_rating == 3 && element < found_element)))
3875 found_element = element;
3880 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
3882 /* check for player created from custom element as explosion content */
3883 content = element_info[element].content.e[xx][yy];
3884 is_player = ELEM_IS_PLAYER(content);
3886 if (is_player && (found_rating < 2 ||
3887 (found_rating == 2 && element < found_element)))
3889 start_x = x + xx - 1;
3890 start_y = y + yy - 1;
3893 found_element = element;
3896 if (!CAN_CHANGE(element))
3899 for (i = 0; i < element_info[element].num_change_pages; i++)
3901 /* check for player created from custom element as extended target */
3903 element_info[element].change_page[i].target_content.e[xx][yy];
3905 is_player = ELEM_IS_PLAYER(content);
3907 if (is_player && (found_rating < 1 ||
3908 (found_rating == 1 && element < found_element)))
3910 start_x = x + xx - 1;
3911 start_y = y + yy - 1;
3914 found_element = element;
3920 scroll_x = SCROLL_POSITION_X(start_x);
3921 scroll_y = SCROLL_POSITION_Y(start_y);
3925 scroll_x = SCROLL_POSITION_X(local_player->jx);
3926 scroll_y = SCROLL_POSITION_Y(local_player->jy);
3929 /* !!! FIX THIS (START) !!! */
3930 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
3932 InitGameEngine_EM();
3934 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
3936 InitGameEngine_SP();
3940 DrawLevel(REDRAW_FIELD);
3943 /* after drawing the level, correct some elements */
3944 if (game.timegate_time_left == 0)
3945 CloseAllOpenTimegates();
3948 /* blit playfield from scroll buffer to normal back buffer for fading in */
3949 BlitScreenToBitmap(backbuffer);
3950 /* !!! FIX THIS (END) !!! */
3952 DrawMaskedBorder(fade_mask);
3957 // full screen redraw is required at this point in the following cases:
3958 // - special editor door undrawn when game was started from level editor
3959 // - drawing area (playfield) was changed and has to be removed completely
3960 redraw_mask = REDRAW_ALL;
3964 if (!game.restart_level)
3966 /* copy default game door content to main double buffer */
3968 /* !!! CHECK AGAIN !!! */
3969 SetPanelBackground();
3970 // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
3971 DrawBackground(DX, DY, DXSIZE, DYSIZE);
3974 SetPanelBackground();
3975 SetDrawBackgroundMask(REDRAW_DOOR_1);
3977 UpdateAndDisplayGameControlValues();
3979 if (!game.restart_level)
3985 CreateGameButtons();
3987 game_gadget[SOUND_CTRL_ID_MUSIC]->checked = setup.sound_music;
3988 game_gadget[SOUND_CTRL_ID_LOOPS]->checked = setup.sound_loops;
3989 game_gadget[SOUND_CTRL_ID_SIMPLE]->checked = setup.sound_simple;
3994 /* copy actual game door content to door double buffer for OpenDoor() */
3995 BlitBitmap(drawto, bitmap_db_door_1, DX, DY, DXSIZE, DYSIZE, 0, 0);
3997 OpenDoor(DOOR_OPEN_ALL);
3999 PlaySound(SND_GAME_STARTING);
4001 if (setup.sound_music)
4004 KeyboardAutoRepeatOffUnlessAutoplay();
4006 #if DEBUG_INIT_PLAYER
4009 printf("Player status (final):\n");
4011 for (i = 0; i < MAX_PLAYERS; i++)
4013 struct PlayerInfo *player = &stored_player[i];
4015 printf("- player %d: present == %d, connected == %d, active == %d",
4021 if (local_player == player)
4022 printf(" (local player)");
4035 if (!game.restart_level && !tape.playing)
4037 LevelStats_incPlayed(level_nr);
4039 SaveLevelSetup_SeriesInfo();
4042 game.restart_level = FALSE;
4044 SaveEngineSnapshotToListInitial();
4047 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y,
4048 int actual_player_x, int actual_player_y)
4050 /* this is used for non-R'n'D game engines to update certain engine values */
4052 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
4054 actual_player_x = correctLevelPosX_EM(actual_player_x);
4055 actual_player_y = correctLevelPosY_EM(actual_player_y);
4058 /* needed to determine if sounds are played within the visible screen area */
4059 scroll_x = actual_scroll_x;
4060 scroll_y = actual_scroll_y;
4062 /* needed to get player position for "follow finger" playing input method */
4063 local_player->jx = actual_player_x;
4064 local_player->jy = actual_player_y;
4067 void InitMovDir(int x, int y)
4069 int i, element = Feld[x][y];
4070 static int xy[4][2] =
4077 static int direction[3][4] =
4079 { MV_RIGHT, MV_UP, MV_LEFT, MV_DOWN },
4080 { MV_LEFT, MV_DOWN, MV_RIGHT, MV_UP },
4081 { MV_LEFT, MV_RIGHT, MV_UP, MV_DOWN }
4090 Feld[x][y] = EL_BUG;
4091 MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
4094 case EL_SPACESHIP_RIGHT:
4095 case EL_SPACESHIP_UP:
4096 case EL_SPACESHIP_LEFT:
4097 case EL_SPACESHIP_DOWN:
4098 Feld[x][y] = EL_SPACESHIP;
4099 MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
4102 case EL_BD_BUTTERFLY_RIGHT:
4103 case EL_BD_BUTTERFLY_UP:
4104 case EL_BD_BUTTERFLY_LEFT:
4105 case EL_BD_BUTTERFLY_DOWN:
4106 Feld[x][y] = EL_BD_BUTTERFLY;
4107 MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
4110 case EL_BD_FIREFLY_RIGHT:
4111 case EL_BD_FIREFLY_UP:
4112 case EL_BD_FIREFLY_LEFT:
4113 case EL_BD_FIREFLY_DOWN:
4114 Feld[x][y] = EL_BD_FIREFLY;
4115 MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
4118 case EL_PACMAN_RIGHT:
4120 case EL_PACMAN_LEFT:
4121 case EL_PACMAN_DOWN:
4122 Feld[x][y] = EL_PACMAN;
4123 MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
4126 case EL_YAMYAM_LEFT:
4127 case EL_YAMYAM_RIGHT:
4129 case EL_YAMYAM_DOWN:
4130 Feld[x][y] = EL_YAMYAM;
4131 MovDir[x][y] = direction[2][element - EL_YAMYAM_LEFT];
4134 case EL_SP_SNIKSNAK:
4135 MovDir[x][y] = MV_UP;
4138 case EL_SP_ELECTRON:
4139 MovDir[x][y] = MV_LEFT;
4146 Feld[x][y] = EL_MOLE;
4147 MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
4151 if (IS_CUSTOM_ELEMENT(element))
4153 struct ElementInfo *ei = &element_info[element];
4154 int move_direction_initial = ei->move_direction_initial;
4155 int move_pattern = ei->move_pattern;
4157 if (move_direction_initial == MV_START_PREVIOUS)
4159 if (MovDir[x][y] != MV_NONE)
4162 move_direction_initial = MV_START_AUTOMATIC;
4165 if (move_direction_initial == MV_START_RANDOM)
4166 MovDir[x][y] = 1 << RND(4);
4167 else if (move_direction_initial & MV_ANY_DIRECTION)
4168 MovDir[x][y] = move_direction_initial;
4169 else if (move_pattern == MV_ALL_DIRECTIONS ||
4170 move_pattern == MV_TURNING_LEFT ||
4171 move_pattern == MV_TURNING_RIGHT ||
4172 move_pattern == MV_TURNING_LEFT_RIGHT ||
4173 move_pattern == MV_TURNING_RIGHT_LEFT ||
4174 move_pattern == MV_TURNING_RANDOM)
4175 MovDir[x][y] = 1 << RND(4);
4176 else if (move_pattern == MV_HORIZONTAL)
4177 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
4178 else if (move_pattern == MV_VERTICAL)
4179 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
4180 else if (move_pattern & MV_ANY_DIRECTION)
4181 MovDir[x][y] = element_info[element].move_pattern;
4182 else if (move_pattern == MV_ALONG_LEFT_SIDE ||
4183 move_pattern == MV_ALONG_RIGHT_SIDE)
4185 /* use random direction as default start direction */
4186 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
4187 MovDir[x][y] = 1 << RND(4);
4189 for (i = 0; i < NUM_DIRECTIONS; i++)
4191 int x1 = x + xy[i][0];
4192 int y1 = y + xy[i][1];
4194 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4196 if (move_pattern == MV_ALONG_RIGHT_SIDE)
4197 MovDir[x][y] = direction[0][i];
4199 MovDir[x][y] = direction[1][i];
4208 MovDir[x][y] = 1 << RND(4);
4210 if (element != EL_BUG &&
4211 element != EL_SPACESHIP &&
4212 element != EL_BD_BUTTERFLY &&
4213 element != EL_BD_FIREFLY)
4216 for (i = 0; i < NUM_DIRECTIONS; i++)
4218 int x1 = x + xy[i][0];
4219 int y1 = y + xy[i][1];
4221 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4223 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
4225 MovDir[x][y] = direction[0][i];
4228 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
4229 element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4231 MovDir[x][y] = direction[1][i];
4240 GfxDir[x][y] = MovDir[x][y];
4243 void InitAmoebaNr(int x, int y)
4246 int group_nr = AmoebeNachbarNr(x, y);
4250 for (i = 1; i < MAX_NUM_AMOEBA; i++)
4252 if (AmoebaCnt[i] == 0)
4260 AmoebaNr[x][y] = group_nr;
4261 AmoebaCnt[group_nr]++;
4262 AmoebaCnt2[group_nr]++;
4265 static void PlayerWins(struct PlayerInfo *player)
4267 player->LevelSolved = TRUE;
4268 player->GameOver = TRUE;
4270 player->score_final = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
4271 level.native_em_level->lev->score : player->score);
4273 player->LevelSolved_CountingTime = (game.no_time_limit ? TimePlayed :
4275 player->LevelSolved_CountingScore = player->score_final;
4280 static int time, time_final;
4281 static int score, score_final;
4282 static int game_over_delay_1 = 0;
4283 static int game_over_delay_2 = 0;
4284 int game_over_delay_value_1 = 50;
4285 int game_over_delay_value_2 = 50;
4287 if (!local_player->LevelSolved_GameWon)
4291 /* do not start end game actions before the player stops moving (to exit) */
4292 if (local_player->MovPos)
4295 local_player->LevelSolved_GameWon = TRUE;
4296 local_player->LevelSolved_SaveTape = tape.recording;
4297 local_player->LevelSolved_SaveScore = !tape.playing;
4301 LevelStats_incSolved(level_nr);
4303 SaveLevelSetup_SeriesInfo();
4306 if (tape.auto_play) /* tape might already be stopped here */
4307 tape.auto_play_level_solved = TRUE;
4311 game_over_delay_1 = game_over_delay_value_1;
4312 game_over_delay_2 = game_over_delay_value_2;
4314 time = time_final = (game.no_time_limit ? TimePlayed : TimeLeft);
4315 score = score_final = local_player->score_final;
4320 score_final += TimeLeft * level.score[SC_TIME_BONUS];
4322 else if (game.no_time_limit && TimePlayed < 999)
4325 score_final += (999 - TimePlayed) * level.score[SC_TIME_BONUS];
4328 local_player->score_final = score_final;
4330 if (level_editor_test_game)
4333 score = score_final;
4335 local_player->LevelSolved_CountingTime = time;
4336 local_player->LevelSolved_CountingScore = score;
4338 game_panel_controls[GAME_PANEL_TIME].value = time;
4339 game_panel_controls[GAME_PANEL_SCORE].value = score;
4341 DisplayGameControlValues();
4344 if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
4346 if (ExitX >= 0 && ExitY >= 0) /* local player has left the level */
4348 /* close exit door after last player */
4349 if ((AllPlayersGone &&
4350 (Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
4351 Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN ||
4352 Feld[ExitX][ExitY] == EL_STEEL_EXIT_OPEN)) ||
4353 Feld[ExitX][ExitY] == EL_EM_EXIT_OPEN ||
4354 Feld[ExitX][ExitY] == EL_EM_STEEL_EXIT_OPEN)
4356 int element = Feld[ExitX][ExitY];
4358 Feld[ExitX][ExitY] =
4359 (element == EL_EXIT_OPEN ? EL_EXIT_CLOSING :
4360 element == EL_EM_EXIT_OPEN ? EL_EM_EXIT_CLOSING :
4361 element == EL_SP_EXIT_OPEN ? EL_SP_EXIT_CLOSING:
4362 element == EL_STEEL_EXIT_OPEN ? EL_STEEL_EXIT_CLOSING:
4363 EL_EM_STEEL_EXIT_CLOSING);
4365 PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING);
4368 /* player disappears */
4369 DrawLevelField(ExitX, ExitY);
4372 for (i = 0; i < MAX_PLAYERS; i++)
4374 struct PlayerInfo *player = &stored_player[i];
4376 if (player->present)
4378 RemovePlayer(player);
4380 /* player disappears */
4381 DrawLevelField(player->jx, player->jy);
4386 PlaySound(SND_GAME_WINNING);
4389 if (game_over_delay_1 > 0)
4391 game_over_delay_1--;
4396 if (time != time_final)
4398 int time_to_go = ABS(time_final - time);
4399 int time_count_dir = (time < time_final ? +1 : -1);
4400 int time_count_steps = (time_to_go > 100 && time_to_go % 10 == 0 ? 10 : 1);
4402 time += time_count_steps * time_count_dir;
4403 score += time_count_steps * level.score[SC_TIME_BONUS];
4405 local_player->LevelSolved_CountingTime = time;
4406 local_player->LevelSolved_CountingScore = score;
4408 game_panel_controls[GAME_PANEL_TIME].value = time;
4409 game_panel_controls[GAME_PANEL_SCORE].value = score;
4411 DisplayGameControlValues();
4413 if (time == time_final)
4414 StopSound(SND_GAME_LEVELTIME_BONUS);
4415 else if (setup.sound_loops)
4416 PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4418 PlaySound(SND_GAME_LEVELTIME_BONUS);
4423 local_player->LevelSolved_PanelOff = TRUE;
4425 if (game_over_delay_2 > 0)
4427 game_over_delay_2--;
4438 boolean raise_level = FALSE;
4440 local_player->LevelSolved_GameEnd = TRUE;
4442 if (!global.use_envelope_request)
4443 CloseDoor(DOOR_CLOSE_1);
4445 if (local_player->LevelSolved_SaveTape)
4447 SaveTapeChecked(tape.level_nr); /* ask to save tape */
4450 CloseDoor(DOOR_CLOSE_ALL);
4452 if (level_editor_test_game)
4454 SetGameStatus(GAME_MODE_MAIN);
4461 if (!local_player->LevelSolved_SaveScore)
4463 SetGameStatus(GAME_MODE_MAIN);
4470 if (level_nr == leveldir_current->handicap_level)
4472 leveldir_current->handicap_level++;
4474 SaveLevelSetup_SeriesInfo();
4477 if (setup.increment_levels &&
4478 level_nr < leveldir_current->last_level)
4479 raise_level = TRUE; /* advance to next level */
4481 if ((hi_pos = NewHiScore()) >= 0)
4483 SetGameStatus(GAME_MODE_SCORES);
4485 DrawHallOfFame(hi_pos);
4495 SetGameStatus(GAME_MODE_MAIN);
4511 boolean one_score_entry_per_name = !program.many_scores_per_name;
4513 LoadScore(level_nr);
4515 if (strEqual(setup.player_name, EMPTY_PLAYER_NAME) ||
4516 local_player->score_final < highscore[MAX_SCORE_ENTRIES - 1].Score)
4519 for (k = 0; k < MAX_SCORE_ENTRIES; k++)
4521 if (local_player->score_final > highscore[k].Score)
4523 /* player has made it to the hall of fame */
4525 if (k < MAX_SCORE_ENTRIES - 1)
4527 int m = MAX_SCORE_ENTRIES - 1;
4529 if (one_score_entry_per_name)
4531 for (l = k; l < MAX_SCORE_ENTRIES; l++)
4532 if (strEqual(setup.player_name, highscore[l].Name))
4535 if (m == k) /* player's new highscore overwrites his old one */
4539 for (l = m; l > k; l--)
4541 strcpy(highscore[l].Name, highscore[l - 1].Name);
4542 highscore[l].Score = highscore[l - 1].Score;
4548 strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
4549 highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
4550 highscore[k].Score = local_player->score_final;
4555 else if (one_score_entry_per_name &&
4556 !strncmp(setup.player_name, highscore[k].Name,
4557 MAX_PLAYER_NAME_LEN))
4558 break; /* player already there with a higher score */
4562 SaveScore(level_nr);
4567 inline static int getElementMoveStepsizeExt(int x, int y, int direction)
4569 int element = Feld[x][y];
4570 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
4571 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
4572 int horiz_move = (dx != 0);
4573 int sign = (horiz_move ? dx : dy);
4574 int step = sign * element_info[element].move_stepsize;
4576 /* special values for move stepsize for spring and things on conveyor belt */
4579 if (CAN_FALL(element) &&
4580 y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
4581 step = sign * MOVE_STEPSIZE_NORMAL / 2;
4582 else if (element == EL_SPRING)
4583 step = sign * MOVE_STEPSIZE_NORMAL * 2;
4589 inline static int getElementMoveStepsize(int x, int y)
4591 return getElementMoveStepsizeExt(x, y, MovDir[x][y]);
4594 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
4596 if (player->GfxAction != action || player->GfxDir != dir)
4598 player->GfxAction = action;
4599 player->GfxDir = dir;
4601 player->StepFrame = 0;
4605 static void ResetGfxFrame(int x, int y)
4607 // profiling showed that "autotest" spends 10~20% of its time in this function
4608 if (DrawingDeactivatedField())
4611 int element = Feld[x][y];
4612 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
4614 if (graphic_info[graphic].anim_global_sync)
4615 GfxFrame[x][y] = FrameCounter;
4616 else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
4617 GfxFrame[x][y] = CustomValue[x][y];
4618 else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
4619 GfxFrame[x][y] = element_info[element].collect_score;
4620 else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
4621 GfxFrame[x][y] = ChangeDelay[x][y];
4624 static void ResetGfxAnimation(int x, int y)
4626 GfxAction[x][y] = ACTION_DEFAULT;
4627 GfxDir[x][y] = MovDir[x][y];
4630 ResetGfxFrame(x, y);
4633 static void ResetRandomAnimationValue(int x, int y)
4635 GfxRandom[x][y] = INIT_GFX_RANDOM();
4638 void InitMovingField(int x, int y, int direction)
4640 int element = Feld[x][y];
4641 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
4642 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
4645 boolean is_moving_before, is_moving_after;
4647 /* check if element was/is moving or being moved before/after mode change */
4648 is_moving_before = (WasJustMoving[x][y] != 0);
4649 is_moving_after = (getElementMoveStepsizeExt(x, y, direction) != 0);
4651 /* reset animation only for moving elements which change direction of moving
4652 or which just started or stopped moving
4653 (else CEs with property "can move" / "not moving" are reset each frame) */
4654 if (is_moving_before != is_moving_after ||
4655 direction != MovDir[x][y])
4656 ResetGfxAnimation(x, y);
4658 MovDir[x][y] = direction;
4659 GfxDir[x][y] = direction;
4661 GfxAction[x][y] = (!is_moving_after ? ACTION_WAITING :
4662 direction == MV_DOWN && CAN_FALL(element) ?
4663 ACTION_FALLING : ACTION_MOVING);
4665 /* this is needed for CEs with property "can move" / "not moving" */
4667 if (is_moving_after)
4669 if (Feld[newx][newy] == EL_EMPTY)
4670 Feld[newx][newy] = EL_BLOCKED;
4672 MovDir[newx][newy] = MovDir[x][y];
4674 CustomValue[newx][newy] = CustomValue[x][y];
4676 GfxFrame[newx][newy] = GfxFrame[x][y];
4677 GfxRandom[newx][newy] = GfxRandom[x][y];
4678 GfxAction[newx][newy] = GfxAction[x][y];
4679 GfxDir[newx][newy] = GfxDir[x][y];
4683 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
4685 int direction = MovDir[x][y];
4686 int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0);
4687 int newy = y + (direction & MV_UP ? -1 : direction & MV_DOWN ? +1 : 0);
4693 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
4695 int oldx = x, oldy = y;
4696 int direction = MovDir[x][y];
4698 if (direction == MV_LEFT)
4700 else if (direction == MV_RIGHT)
4702 else if (direction == MV_UP)
4704 else if (direction == MV_DOWN)
4707 *comes_from_x = oldx;
4708 *comes_from_y = oldy;
4711 int MovingOrBlocked2Element(int x, int y)
4713 int element = Feld[x][y];
4715 if (element == EL_BLOCKED)
4719 Blocked2Moving(x, y, &oldx, &oldy);
4720 return Feld[oldx][oldy];
4726 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
4728 /* like MovingOrBlocked2Element(), but if element is moving
4729 and (x,y) is the field the moving element is just leaving,
4730 return EL_BLOCKED instead of the element value */
4731 int element = Feld[x][y];
4733 if (IS_MOVING(x, y))
4735 if (element == EL_BLOCKED)
4739 Blocked2Moving(x, y, &oldx, &oldy);
4740 return Feld[oldx][oldy];
4749 static void RemoveField(int x, int y)
4751 Feld[x][y] = EL_EMPTY;
4757 CustomValue[x][y] = 0;
4760 ChangeDelay[x][y] = 0;
4761 ChangePage[x][y] = -1;
4762 Pushed[x][y] = FALSE;
4764 GfxElement[x][y] = EL_UNDEFINED;
4765 GfxAction[x][y] = ACTION_DEFAULT;
4766 GfxDir[x][y] = MV_NONE;
4769 void RemoveMovingField(int x, int y)
4771 int oldx = x, oldy = y, newx = x, newy = y;
4772 int element = Feld[x][y];
4773 int next_element = EL_UNDEFINED;
4775 if (element != EL_BLOCKED && !IS_MOVING(x, y))
4778 if (IS_MOVING(x, y))
4780 Moving2Blocked(x, y, &newx, &newy);
4782 if (Feld[newx][newy] != EL_BLOCKED)
4784 /* element is moving, but target field is not free (blocked), but
4785 already occupied by something different (example: acid pool);
4786 in this case, only remove the moving field, but not the target */
4788 RemoveField(oldx, oldy);
4790 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
4792 TEST_DrawLevelField(oldx, oldy);
4797 else if (element == EL_BLOCKED)
4799 Blocked2Moving(x, y, &oldx, &oldy);
4800 if (!IS_MOVING(oldx, oldy))
4804 if (element == EL_BLOCKED &&
4805 (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
4806 Feld[oldx][oldy] == EL_QUICKSAND_FAST_EMPTYING ||
4807 Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
4808 Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
4809 Feld[oldx][oldy] == EL_DC_MAGIC_WALL_EMPTYING ||
4810 Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
4811 next_element = get_next_element(Feld[oldx][oldy]);
4813 RemoveField(oldx, oldy);
4814 RemoveField(newx, newy);
4816 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
4818 if (next_element != EL_UNDEFINED)
4819 Feld[oldx][oldy] = next_element;
4821 TEST_DrawLevelField(oldx, oldy);
4822 TEST_DrawLevelField(newx, newy);
4825 void DrawDynamite(int x, int y)
4827 int sx = SCREENX(x), sy = SCREENY(y);
4828 int graphic = el2img(Feld[x][y]);
4831 if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
4834 if (IS_WALKABLE_INSIDE(Back[x][y]))
4838 DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
4839 else if (Store[x][y])
4840 DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
4842 frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
4844 if (Back[x][y] || Store[x][y])
4845 DrawGraphicThruMask(sx, sy, graphic, frame);
4847 DrawGraphic(sx, sy, graphic, frame);
4850 void CheckDynamite(int x, int y)
4852 if (MovDelay[x][y] != 0) /* dynamite is still waiting to explode */
4856 if (MovDelay[x][y] != 0)
4859 PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
4865 StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
4870 static void setMinimalPlayerBoundaries(int *sx1, int *sy1, int *sx2, int *sy2)
4872 boolean num_checked_players = 0;
4875 for (i = 0; i < MAX_PLAYERS; i++)
4877 if (stored_player[i].active)
4879 int sx = stored_player[i].jx;
4880 int sy = stored_player[i].jy;
4882 if (num_checked_players == 0)
4889 *sx1 = MIN(*sx1, sx);
4890 *sy1 = MIN(*sy1, sy);
4891 *sx2 = MAX(*sx2, sx);
4892 *sy2 = MAX(*sy2, sy);
4895 num_checked_players++;
4900 static boolean checkIfAllPlayersFitToScreen_RND()
4902 int sx1 = 0, sy1 = 0, sx2 = 0, sy2 = 0;
4904 setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
4906 return (sx2 - sx1 < SCR_FIELDX &&
4907 sy2 - sy1 < SCR_FIELDY);
4910 static void setScreenCenteredToAllPlayers(int *sx, int *sy)
4912 int sx1 = scroll_x, sy1 = scroll_y, sx2 = scroll_x, sy2 = scroll_y;
4914 setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
4916 *sx = (sx1 + sx2) / 2;
4917 *sy = (sy1 + sy2) / 2;
4920 void DrawRelocateScreen(int old_x, int old_y, int x, int y, int move_dir,
4921 boolean center_screen, boolean quick_relocation)
4923 unsigned int frame_delay_value_old = GetVideoFrameDelay();
4924 boolean ffwd_delay = (tape.playing && tape.fast_forward);
4925 boolean no_delay = (tape.warp_forward);
4926 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
4927 int wait_delay_value = (no_delay ? 0 : frame_delay_value);
4928 int new_scroll_x, new_scroll_y;
4930 if (level.lazy_relocation && IN_VIS_FIELD(SCREENX(x), SCREENY(y)))
4932 /* case 1: quick relocation inside visible screen (without scrolling) */
4939 if (!level.shifted_relocation || center_screen)
4941 /* relocation _with_ centering of screen */
4943 new_scroll_x = SCROLL_POSITION_X(x);
4944 new_scroll_y = SCROLL_POSITION_Y(y);
4948 /* relocation _without_ centering of screen */
4950 int center_scroll_x = SCROLL_POSITION_X(old_x);
4951 int center_scroll_y = SCROLL_POSITION_Y(old_y);
4952 int offset_x = x + (scroll_x - center_scroll_x);
4953 int offset_y = y + (scroll_y - center_scroll_y);
4955 /* for new screen position, apply previous offset to center position */
4956 new_scroll_x = SCROLL_POSITION_X(offset_x);
4957 new_scroll_y = SCROLL_POSITION_Y(offset_y);
4960 if (quick_relocation)
4962 /* case 2: quick relocation (redraw without visible scrolling) */
4964 scroll_x = new_scroll_x;
4965 scroll_y = new_scroll_y;
4972 /* case 3: visible relocation (with scrolling to new position) */
4974 ScrollScreen(NULL, SCROLL_GO_ON); /* scroll last frame to full tile */
4976 SetVideoFrameDelay(wait_delay_value);
4978 while (scroll_x != new_scroll_x || scroll_y != new_scroll_y)
4981 int fx = FX, fy = FY;
4983 dx = (new_scroll_x < scroll_x ? +1 : new_scroll_x > scroll_x ? -1 : 0);
4984 dy = (new_scroll_y < scroll_y ? +1 : new_scroll_y > scroll_y ? -1 : 0);
4986 if (dx == 0 && dy == 0) /* no scrolling needed at all */
4992 fx += dx * TILEX / 2;
4993 fy += dy * TILEY / 2;
4995 ScrollLevel(dx, dy);
4998 /* scroll in two steps of half tile size to make things smoother */
4999 BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
5001 /* scroll second step to align at full tile size */
5002 BlitScreenToBitmap(window);
5008 SetVideoFrameDelay(frame_delay_value_old);
5011 void RelocatePlayer(int jx, int jy, int el_player_raw)
5013 int el_player = GET_PLAYER_ELEMENT(el_player_raw);
5014 int player_nr = GET_PLAYER_NR(el_player);
5015 struct PlayerInfo *player = &stored_player[player_nr];
5016 boolean ffwd_delay = (tape.playing && tape.fast_forward);
5017 boolean no_delay = (tape.warp_forward);
5018 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5019 int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5020 int old_jx = player->jx;
5021 int old_jy = player->jy;
5022 int old_element = Feld[old_jx][old_jy];
5023 int element = Feld[jx][jy];
5024 boolean player_relocated = (old_jx != jx || old_jy != jy);
5026 int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
5027 int move_dir_vert = (jy < old_jy ? MV_UP : jy > old_jy ? MV_DOWN : 0);
5028 int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
5029 int enter_side_vert = MV_DIR_OPPOSITE(move_dir_vert);
5030 int leave_side_horiz = move_dir_horiz;
5031 int leave_side_vert = move_dir_vert;
5032 int enter_side = enter_side_horiz | enter_side_vert;
5033 int leave_side = leave_side_horiz | leave_side_vert;
5035 if (player->GameOver) /* do not reanimate dead player */
5038 if (!player_relocated) /* no need to relocate the player */
5041 if (IS_PLAYER(jx, jy)) /* player already placed at new position */
5043 RemoveField(jx, jy); /* temporarily remove newly placed player */
5044 DrawLevelField(jx, jy);
5047 if (player->present)
5049 while (player->MovPos)
5051 ScrollPlayer(player, SCROLL_GO_ON);
5052 ScrollScreen(NULL, SCROLL_GO_ON);
5054 AdvanceFrameAndPlayerCounters(player->index_nr);
5058 BackToFront_WithFrameDelay(wait_delay_value);
5061 DrawPlayer(player); /* needed here only to cleanup last field */
5062 DrawLevelField(player->jx, player->jy); /* remove player graphic */
5064 player->is_moving = FALSE;
5067 if (IS_CUSTOM_ELEMENT(old_element))
5068 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
5070 player->index_bit, leave_side);
5072 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
5074 player->index_bit, leave_side);
5076 Feld[jx][jy] = el_player;
5077 InitPlayerField(jx, jy, el_player, TRUE);
5079 /* "InitPlayerField()" above sets Feld[jx][jy] to EL_EMPTY, but it may be
5080 possible that the relocation target field did not contain a player element,
5081 but a walkable element, to which the new player was relocated -- in this
5082 case, restore that (already initialized!) element on the player field */
5083 if (!ELEM_IS_PLAYER(element)) /* player may be set on walkable element */
5085 Feld[jx][jy] = element; /* restore previously existing element */
5088 /* only visually relocate centered player */
5089 DrawRelocateScreen(old_jx, old_jy, player->jx, player->jy, player->MovDir,
5090 FALSE, level.instant_relocation);
5092 TestIfPlayerTouchesBadThing(jx, jy);
5093 TestIfPlayerTouchesCustomElement(jx, jy);
5095 if (IS_CUSTOM_ELEMENT(element))
5096 CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
5097 player->index_bit, enter_side);
5099 CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
5100 player->index_bit, enter_side);
5102 if (player->is_switching)
5104 /* ensure that relocation while still switching an element does not cause
5105 a new element to be treated as also switched directly after relocation
5106 (this is important for teleporter switches that teleport the player to
5107 a place where another teleporter switch is in the same direction, which
5108 would then incorrectly be treated as immediately switched before the
5109 direction key that caused the switch was released) */
5111 player->switch_x += jx - old_jx;
5112 player->switch_y += jy - old_jy;
5116 void Explode(int ex, int ey, int phase, int mode)
5122 /* !!! eliminate this variable !!! */
5123 int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
5125 if (game.explosions_delayed)
5127 ExplodeField[ex][ey] = mode;
5131 if (phase == EX_PHASE_START) /* initialize 'Store[][]' field */
5133 int center_element = Feld[ex][ey];
5134 int artwork_element, explosion_element; /* set these values later */
5136 /* remove things displayed in background while burning dynamite */
5137 if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
5140 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
5142 /* put moving element to center field (and let it explode there) */
5143 center_element = MovingOrBlocked2Element(ex, ey);
5144 RemoveMovingField(ex, ey);
5145 Feld[ex][ey] = center_element;
5148 /* now "center_element" is finally determined -- set related values now */
5149 artwork_element = center_element; /* for custom player artwork */
5150 explosion_element = center_element; /* for custom player artwork */
5152 if (IS_PLAYER(ex, ey))
5154 int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
5156 artwork_element = stored_player[player_nr].artwork_element;
5158 if (level.use_explosion_element[player_nr])
5160 explosion_element = level.explosion_element[player_nr];
5161 artwork_element = explosion_element;
5165 if (mode == EX_TYPE_NORMAL ||
5166 mode == EX_TYPE_CENTER ||
5167 mode == EX_TYPE_CROSS)
5168 PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
5170 last_phase = element_info[explosion_element].explosion_delay + 1;
5172 for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
5174 int xx = x - ex + 1;
5175 int yy = y - ey + 1;
5178 if (!IN_LEV_FIELD(x, y) ||
5179 (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
5180 (mode == EX_TYPE_CROSS && (x != ex && y != ey)))
5183 element = Feld[x][y];
5185 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
5187 element = MovingOrBlocked2Element(x, y);
5189 if (!IS_EXPLOSION_PROOF(element))
5190 RemoveMovingField(x, y);
5193 /* indestructible elements can only explode in center (but not flames) */
5194 if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
5195 mode == EX_TYPE_BORDER)) ||
5196 element == EL_FLAMES)
5199 /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
5200 behaviour, for example when touching a yamyam that explodes to rocks
5201 with active deadly shield, a rock is created under the player !!! */
5202 /* (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8) */
5204 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
5205 (game.engine_version < VERSION_IDENT(3,1,0,0) ||
5206 (x == ex && y == ey && mode != EX_TYPE_BORDER)))
5208 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
5211 if (IS_ACTIVE_BOMB(element))
5213 /* re-activate things under the bomb like gate or penguin */
5214 Feld[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
5221 /* save walkable background elements while explosion on same tile */
5222 if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
5223 (x != ex || y != ey || mode == EX_TYPE_BORDER))
5224 Back[x][y] = element;
5226 /* ignite explodable elements reached by other explosion */
5227 if (element == EL_EXPLOSION)
5228 element = Store2[x][y];
5230 if (AmoebaNr[x][y] &&
5231 (element == EL_AMOEBA_FULL ||
5232 element == EL_BD_AMOEBA ||
5233 element == EL_AMOEBA_GROWING))
5235 AmoebaCnt[AmoebaNr[x][y]]--;
5236 AmoebaCnt2[AmoebaNr[x][y]]--;
5241 if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
5243 int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
5245 Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
5247 if (PLAYERINFO(ex, ey)->use_murphy)
5248 Store[x][y] = EL_EMPTY;
5251 /* !!! check this case -- currently needed for rnd_rado_negundo_v,
5252 !!! levels 015 018 019 020 021 022 023 026 027 028 !!! */
5253 else if (ELEM_IS_PLAYER(center_element))
5254 Store[x][y] = EL_EMPTY;
5255 else if (center_element == EL_YAMYAM)
5256 Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
5257 else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
5258 Store[x][y] = element_info[center_element].content.e[xx][yy];
5260 /* needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
5261 (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
5262 otherwise) -- FIX THIS !!! */
5263 else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
5264 Store[x][y] = element_info[element].content.e[1][1];
5266 else if (!CAN_EXPLODE(element))
5267 Store[x][y] = element_info[element].content.e[1][1];
5270 Store[x][y] = EL_EMPTY;
5272 if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
5273 center_element == EL_AMOEBA_TO_DIAMOND)
5274 Store2[x][y] = element;
5276 Feld[x][y] = EL_EXPLOSION;
5277 GfxElement[x][y] = artwork_element;
5279 ExplodePhase[x][y] = 1;
5280 ExplodeDelay[x][y] = last_phase;
5285 if (center_element == EL_YAMYAM)
5286 game.yamyam_content_nr =
5287 (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
5299 GfxFrame[x][y] = 0; /* restart explosion animation */
5301 last_phase = ExplodeDelay[x][y];
5303 ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
5305 /* this can happen if the player leaves an explosion just in time */
5306 if (GfxElement[x][y] == EL_UNDEFINED)
5307 GfxElement[x][y] = EL_EMPTY;
5309 border_element = Store2[x][y];
5310 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5311 border_element = StorePlayer[x][y];
5313 if (phase == element_info[border_element].ignition_delay ||
5314 phase == last_phase)
5316 boolean border_explosion = FALSE;
5318 if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
5319 !PLAYER_EXPLOSION_PROTECTED(x, y))
5321 KillPlayerUnlessExplosionProtected(x, y);
5322 border_explosion = TRUE;
5324 else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
5326 Feld[x][y] = Store2[x][y];
5329 border_explosion = TRUE;
5331 else if (border_element == EL_AMOEBA_TO_DIAMOND)
5333 AmoebeUmwandeln(x, y);
5335 border_explosion = TRUE;
5338 /* if an element just explodes due to another explosion (chain-reaction),
5339 do not immediately end the new explosion when it was the last frame of
5340 the explosion (as it would be done in the following "if"-statement!) */
5341 if (border_explosion && phase == last_phase)
5345 if (phase == last_phase)
5349 element = Feld[x][y] = Store[x][y];
5350 Store[x][y] = Store2[x][y] = 0;
5351 GfxElement[x][y] = EL_UNDEFINED;
5353 /* player can escape from explosions and might therefore be still alive */
5354 if (element >= EL_PLAYER_IS_EXPLODING_1 &&
5355 element <= EL_PLAYER_IS_EXPLODING_4)
5357 int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
5358 int explosion_element = EL_PLAYER_1 + player_nr;
5359 int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
5360 int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
5362 if (level.use_explosion_element[player_nr])
5363 explosion_element = level.explosion_element[player_nr];
5365 Feld[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
5366 element_info[explosion_element].content.e[xx][yy]);
5369 /* restore probably existing indestructible background element */
5370 if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
5371 element = Feld[x][y] = Back[x][y];
5374 MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
5375 GfxDir[x][y] = MV_NONE;
5376 ChangeDelay[x][y] = 0;
5377 ChangePage[x][y] = -1;
5379 CustomValue[x][y] = 0;
5381 InitField_WithBug2(x, y, FALSE);
5383 TEST_DrawLevelField(x, y);
5385 TestIfElementTouchesCustomElement(x, y);
5387 if (GFX_CRUMBLED(element))
5388 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
5390 if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
5391 StorePlayer[x][y] = 0;
5393 if (ELEM_IS_PLAYER(element))
5394 RelocatePlayer(x, y, element);
5396 else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
5398 int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
5399 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5402 TEST_DrawLevelFieldCrumbled(x, y);
5404 if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
5406 DrawLevelElement(x, y, Back[x][y]);
5407 DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
5409 else if (IS_WALKABLE_UNDER(Back[x][y]))
5411 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5412 DrawLevelElementThruMask(x, y, Back[x][y]);
5414 else if (!IS_WALKABLE_INSIDE(Back[x][y]))
5415 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5419 void DynaExplode(int ex, int ey)
5422 int dynabomb_element = Feld[ex][ey];
5423 int dynabomb_size = 1;
5424 boolean dynabomb_xl = FALSE;
5425 struct PlayerInfo *player;
5426 static int xy[4][2] =
5434 if (IS_ACTIVE_BOMB(dynabomb_element))
5436 player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
5437 dynabomb_size = player->dynabomb_size;
5438 dynabomb_xl = player->dynabomb_xl;
5439 player->dynabombs_left++;
5442 Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
5444 for (i = 0; i < NUM_DIRECTIONS; i++)
5446 for (j = 1; j <= dynabomb_size; j++)
5448 int x = ex + j * xy[i][0];
5449 int y = ey + j * xy[i][1];
5452 if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
5455 element = Feld[x][y];
5457 /* do not restart explosions of fields with active bombs */
5458 if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
5461 Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
5463 if (element != EL_EMPTY && element != EL_EXPLOSION &&
5464 !IS_DIGGABLE(element) && !dynabomb_xl)
5470 void Bang(int x, int y)
5472 int element = MovingOrBlocked2Element(x, y);
5473 int explosion_type = EX_TYPE_NORMAL;
5475 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5477 struct PlayerInfo *player = PLAYERINFO(x, y);
5479 element = Feld[x][y] = player->initial_element;
5481 if (level.use_explosion_element[player->index_nr])
5483 int explosion_element = level.explosion_element[player->index_nr];
5485 if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
5486 explosion_type = EX_TYPE_CROSS;
5487 else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
5488 explosion_type = EX_TYPE_CENTER;
5496 case EL_BD_BUTTERFLY:
5499 case EL_DARK_YAMYAM:
5503 RaiseScoreElement(element);
5506 case EL_DYNABOMB_PLAYER_1_ACTIVE:
5507 case EL_DYNABOMB_PLAYER_2_ACTIVE:
5508 case EL_DYNABOMB_PLAYER_3_ACTIVE:
5509 case EL_DYNABOMB_PLAYER_4_ACTIVE:
5510 case EL_DYNABOMB_INCREASE_NUMBER:
5511 case EL_DYNABOMB_INCREASE_SIZE:
5512 case EL_DYNABOMB_INCREASE_POWER:
5513 explosion_type = EX_TYPE_DYNA;
5516 case EL_DC_LANDMINE:
5517 explosion_type = EX_TYPE_CENTER;
5522 case EL_LAMP_ACTIVE:
5523 case EL_AMOEBA_TO_DIAMOND:
5524 if (!IS_PLAYER(x, y)) /* penguin and player may be at same field */
5525 explosion_type = EX_TYPE_CENTER;
5529 if (element_info[element].explosion_type == EXPLODES_CROSS)
5530 explosion_type = EX_TYPE_CROSS;
5531 else if (element_info[element].explosion_type == EXPLODES_1X1)
5532 explosion_type = EX_TYPE_CENTER;
5536 if (explosion_type == EX_TYPE_DYNA)
5539 Explode(x, y, EX_PHASE_START, explosion_type);
5541 CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
5544 void SplashAcid(int x, int y)
5546 if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
5547 (!IN_LEV_FIELD(x - 1, y - 2) ||
5548 !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
5549 Feld[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
5551 if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
5552 (!IN_LEV_FIELD(x + 1, y - 2) ||
5553 !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
5554 Feld[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
5556 PlayLevelSound(x, y, SND_ACID_SPLASHING);
5559 static void InitBeltMovement()
5561 static int belt_base_element[4] =
5563 EL_CONVEYOR_BELT_1_LEFT,
5564 EL_CONVEYOR_BELT_2_LEFT,
5565 EL_CONVEYOR_BELT_3_LEFT,
5566 EL_CONVEYOR_BELT_4_LEFT
5568 static int belt_base_active_element[4] =
5570 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
5571 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
5572 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
5573 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
5578 /* set frame order for belt animation graphic according to belt direction */
5579 for (i = 0; i < NUM_BELTS; i++)
5583 for (j = 0; j < NUM_BELT_PARTS; j++)
5585 int element = belt_base_active_element[belt_nr] + j;
5586 int graphic_1 = el2img(element);
5587 int graphic_2 = el2panelimg(element);
5589 if (game.belt_dir[i] == MV_LEFT)
5591 graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
5592 graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
5596 graphic_info[graphic_1].anim_mode |= ANIM_REVERSE;
5597 graphic_info[graphic_2].anim_mode |= ANIM_REVERSE;
5602 SCAN_PLAYFIELD(x, y)
5604 int element = Feld[x][y];
5606 for (i = 0; i < NUM_BELTS; i++)
5608 if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
5610 int e_belt_nr = getBeltNrFromBeltElement(element);
5613 if (e_belt_nr == belt_nr)
5615 int belt_part = Feld[x][y] - belt_base_element[belt_nr];
5617 Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
5624 static void ToggleBeltSwitch(int x, int y)
5626 static int belt_base_element[4] =
5628 EL_CONVEYOR_BELT_1_LEFT,
5629 EL_CONVEYOR_BELT_2_LEFT,
5630 EL_CONVEYOR_BELT_3_LEFT,
5631 EL_CONVEYOR_BELT_4_LEFT
5633 static int belt_base_active_element[4] =
5635 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
5636 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
5637 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
5638 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
5640 static int belt_base_switch_element[4] =
5642 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
5643 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
5644 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
5645 EL_CONVEYOR_BELT_4_SWITCH_LEFT
5647 static int belt_move_dir[4] =
5655 int element = Feld[x][y];
5656 int belt_nr = getBeltNrFromBeltSwitchElement(element);
5657 int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
5658 int belt_dir = belt_move_dir[belt_dir_nr];
5661 if (!IS_BELT_SWITCH(element))
5664 game.belt_dir_nr[belt_nr] = belt_dir_nr;
5665 game.belt_dir[belt_nr] = belt_dir;
5667 if (belt_dir_nr == 3)
5670 /* set frame order for belt animation graphic according to belt direction */
5671 for (i = 0; i < NUM_BELT_PARTS; i++)
5673 int element = belt_base_active_element[belt_nr] + i;
5674 int graphic_1 = el2img(element);
5675 int graphic_2 = el2panelimg(element);
5677 if (belt_dir == MV_LEFT)
5679 graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
5680 graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
5684 graphic_info[graphic_1].anim_mode |= ANIM_REVERSE;
5685 graphic_info[graphic_2].anim_mode |= ANIM_REVERSE;
5689 SCAN_PLAYFIELD(xx, yy)
5691 int element = Feld[xx][yy];
5693 if (IS_BELT_SWITCH(element))
5695 int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
5697 if (e_belt_nr == belt_nr)
5699 Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
5700 TEST_DrawLevelField(xx, yy);
5703 else if (IS_BELT(element) && belt_dir != MV_NONE)
5705 int e_belt_nr = getBeltNrFromBeltElement(element);
5707 if (e_belt_nr == belt_nr)
5709 int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
5711 Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
5712 TEST_DrawLevelField(xx, yy);
5715 else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
5717 int e_belt_nr = getBeltNrFromBeltActiveElement(element);
5719 if (e_belt_nr == belt_nr)
5721 int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
5723 Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
5724 TEST_DrawLevelField(xx, yy);
5730 static void ToggleSwitchgateSwitch(int x, int y)
5734 game.switchgate_pos = !game.switchgate_pos;
5736 SCAN_PLAYFIELD(xx, yy)
5738 int element = Feld[xx][yy];
5740 if (element == EL_SWITCHGATE_SWITCH_UP)
5742 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_DOWN;
5743 TEST_DrawLevelField(xx, yy);
5745 else if (element == EL_SWITCHGATE_SWITCH_DOWN)
5747 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP;
5748 TEST_DrawLevelField(xx, yy);
5750 else if (element == EL_DC_SWITCHGATE_SWITCH_UP)
5752 Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_DOWN;
5753 TEST_DrawLevelField(xx, yy);
5755 else if (element == EL_DC_SWITCHGATE_SWITCH_DOWN)
5757 Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP;
5758 TEST_DrawLevelField(xx, yy);
5760 else if (element == EL_SWITCHGATE_OPEN ||
5761 element == EL_SWITCHGATE_OPENING)
5763 Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
5765 PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
5767 else if (element == EL_SWITCHGATE_CLOSED ||
5768 element == EL_SWITCHGATE_CLOSING)
5770 Feld[xx][yy] = EL_SWITCHGATE_OPENING;
5772 PlayLevelSoundAction(xx, yy, ACTION_OPENING);
5777 static int getInvisibleActiveFromInvisibleElement(int element)
5779 return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
5780 element == EL_INVISIBLE_WALL ? EL_INVISIBLE_WALL_ACTIVE :
5781 element == EL_INVISIBLE_SAND ? EL_INVISIBLE_SAND_ACTIVE :
5785 static int getInvisibleFromInvisibleActiveElement(int element)
5787 return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
5788 element == EL_INVISIBLE_WALL_ACTIVE ? EL_INVISIBLE_WALL :
5789 element == EL_INVISIBLE_SAND_ACTIVE ? EL_INVISIBLE_SAND :
5793 static void RedrawAllLightSwitchesAndInvisibleElements()
5797 SCAN_PLAYFIELD(x, y)
5799 int element = Feld[x][y];
5801 if (element == EL_LIGHT_SWITCH &&
5802 game.light_time_left > 0)
5804 Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
5805 TEST_DrawLevelField(x, y);
5807 else if (element == EL_LIGHT_SWITCH_ACTIVE &&
5808 game.light_time_left == 0)
5810 Feld[x][y] = EL_LIGHT_SWITCH;
5811 TEST_DrawLevelField(x, y);
5813 else if (element == EL_EMC_DRIPPER &&
5814 game.light_time_left > 0)
5816 Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
5817 TEST_DrawLevelField(x, y);
5819 else if (element == EL_EMC_DRIPPER_ACTIVE &&
5820 game.light_time_left == 0)
5822 Feld[x][y] = EL_EMC_DRIPPER;
5823 TEST_DrawLevelField(x, y);
5825 else if (element == EL_INVISIBLE_STEELWALL ||
5826 element == EL_INVISIBLE_WALL ||
5827 element == EL_INVISIBLE_SAND)
5829 if (game.light_time_left > 0)
5830 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
5832 TEST_DrawLevelField(x, y);
5834 /* uncrumble neighbour fields, if needed */
5835 if (element == EL_INVISIBLE_SAND)
5836 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
5838 else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
5839 element == EL_INVISIBLE_WALL_ACTIVE ||
5840 element == EL_INVISIBLE_SAND_ACTIVE)
5842 if (game.light_time_left == 0)
5843 Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
5845 TEST_DrawLevelField(x, y);
5847 /* re-crumble neighbour fields, if needed */
5848 if (element == EL_INVISIBLE_SAND)
5849 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
5854 static void RedrawAllInvisibleElementsForLenses()
5858 SCAN_PLAYFIELD(x, y)
5860 int element = Feld[x][y];
5862 if (element == EL_EMC_DRIPPER &&
5863 game.lenses_time_left > 0)
5865 Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
5866 TEST_DrawLevelField(x, y);
5868 else if (element == EL_EMC_DRIPPER_ACTIVE &&
5869 game.lenses_time_left == 0)
5871 Feld[x][y] = EL_EMC_DRIPPER;
5872 TEST_DrawLevelField(x, y);
5874 else if (element == EL_INVISIBLE_STEELWALL ||
5875 element == EL_INVISIBLE_WALL ||
5876 element == EL_INVISIBLE_SAND)
5878 if (game.lenses_time_left > 0)
5879 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
5881 TEST_DrawLevelField(x, y);
5883 /* uncrumble neighbour fields, if needed */
5884 if (element == EL_INVISIBLE_SAND)
5885 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
5887 else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
5888 element == EL_INVISIBLE_WALL_ACTIVE ||
5889 element == EL_INVISIBLE_SAND_ACTIVE)
5891 if (game.lenses_time_left == 0)
5892 Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
5894 TEST_DrawLevelField(x, y);
5896 /* re-crumble neighbour fields, if needed */
5897 if (element == EL_INVISIBLE_SAND)
5898 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
5903 static void RedrawAllInvisibleElementsForMagnifier()
5907 SCAN_PLAYFIELD(x, y)
5909 int element = Feld[x][y];
5911 if (element == EL_EMC_FAKE_GRASS &&
5912 game.magnify_time_left > 0)
5914 Feld[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
5915 TEST_DrawLevelField(x, y);
5917 else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
5918 game.magnify_time_left == 0)
5920 Feld[x][y] = EL_EMC_FAKE_GRASS;
5921 TEST_DrawLevelField(x, y);
5923 else if (IS_GATE_GRAY(element) &&
5924 game.magnify_time_left > 0)
5926 Feld[x][y] = (IS_RND_GATE_GRAY(element) ?
5927 element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
5928 IS_EM_GATE_GRAY(element) ?
5929 element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
5930 IS_EMC_GATE_GRAY(element) ?
5931 element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
5932 IS_DC_GATE_GRAY(element) ?
5933 EL_DC_GATE_WHITE_GRAY_ACTIVE :
5935 TEST_DrawLevelField(x, y);
5937 else if (IS_GATE_GRAY_ACTIVE(element) &&
5938 game.magnify_time_left == 0)
5940 Feld[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
5941 element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
5942 IS_EM_GATE_GRAY_ACTIVE(element) ?
5943 element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
5944 IS_EMC_GATE_GRAY_ACTIVE(element) ?
5945 element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
5946 IS_DC_GATE_GRAY_ACTIVE(element) ?
5947 EL_DC_GATE_WHITE_GRAY :
5949 TEST_DrawLevelField(x, y);
5954 static void ToggleLightSwitch(int x, int y)
5956 int element = Feld[x][y];
5958 game.light_time_left =
5959 (element == EL_LIGHT_SWITCH ?
5960 level.time_light * FRAMES_PER_SECOND : 0);
5962 RedrawAllLightSwitchesAndInvisibleElements();
5965 static void ActivateTimegateSwitch(int x, int y)
5969 game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
5971 SCAN_PLAYFIELD(xx, yy)
5973 int element = Feld[xx][yy];
5975 if (element == EL_TIMEGATE_CLOSED ||
5976 element == EL_TIMEGATE_CLOSING)
5978 Feld[xx][yy] = EL_TIMEGATE_OPENING;
5979 PlayLevelSound(xx, yy, SND_CLASS_TIMEGATE_OPENING);
5983 else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
5985 Feld[xx][yy] = EL_TIMEGATE_SWITCH;
5986 TEST_DrawLevelField(xx, yy);
5992 Feld[x][y] = (Feld[x][y] == EL_TIMEGATE_SWITCH ? EL_TIMEGATE_SWITCH_ACTIVE :
5993 EL_DC_TIMEGATE_SWITCH_ACTIVE);
5996 void Impact(int x, int y)
5998 boolean last_line = (y == lev_fieldy - 1);
5999 boolean object_hit = FALSE;
6000 boolean impact = (last_line || object_hit);
6001 int element = Feld[x][y];
6002 int smashed = EL_STEELWALL;
6004 if (!last_line) /* check if element below was hit */
6006 if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
6009 object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
6010 MovDir[x][y + 1] != MV_DOWN ||
6011 MovPos[x][y + 1] <= TILEY / 2));
6013 /* do not smash moving elements that left the smashed field in time */
6014 if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
6015 ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
6018 #if USE_QUICKSAND_IMPACT_BUGFIX
6019 if (Feld[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
6021 RemoveMovingField(x, y + 1);
6022 Feld[x][y + 1] = EL_QUICKSAND_EMPTY;
6023 Feld[x][y + 2] = EL_ROCK;
6024 TEST_DrawLevelField(x, y + 2);
6029 if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTYING && object_hit == FALSE)
6031 RemoveMovingField(x, y + 1);
6032 Feld[x][y + 1] = EL_QUICKSAND_FAST_EMPTY;
6033 Feld[x][y + 2] = EL_ROCK;
6034 TEST_DrawLevelField(x, y + 2);
6041 smashed = MovingOrBlocked2Element(x, y + 1);
6043 impact = (last_line || object_hit);
6046 if (!last_line && smashed == EL_ACID) /* element falls into acid */
6048 SplashAcid(x, y + 1);
6052 /* !!! not sufficient for all cases -- see EL_PEARL below !!! */
6053 /* only reset graphic animation if graphic really changes after impact */
6055 el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
6057 ResetGfxAnimation(x, y);
6058 TEST_DrawLevelField(x, y);
6061 if (impact && CAN_EXPLODE_IMPACT(element))
6066 else if (impact && element == EL_PEARL &&
6067 smashed != EL_DC_MAGIC_WALL && smashed != EL_DC_MAGIC_WALL_ACTIVE)
6069 ResetGfxAnimation(x, y);
6071 Feld[x][y] = EL_PEARL_BREAKING;
6072 PlayLevelSound(x, y, SND_PEARL_BREAKING);
6075 else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
6077 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6082 if (impact && element == EL_AMOEBA_DROP)
6084 if (object_hit && IS_PLAYER(x, y + 1))
6085 KillPlayerUnlessEnemyProtected(x, y + 1);
6086 else if (object_hit && smashed == EL_PENGUIN)
6090 Feld[x][y] = EL_AMOEBA_GROWING;
6091 Store[x][y] = EL_AMOEBA_WET;
6093 ResetRandomAnimationValue(x, y);
6098 if (object_hit) /* check which object was hit */
6100 if ((CAN_PASS_MAGIC_WALL(element) &&
6101 (smashed == EL_MAGIC_WALL ||
6102 smashed == EL_BD_MAGIC_WALL)) ||
6103 (CAN_PASS_DC_MAGIC_WALL(element) &&
6104 smashed == EL_DC_MAGIC_WALL))
6107 int activated_magic_wall =
6108 (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
6109 smashed == EL_BD_MAGIC_WALL ? EL_BD_MAGIC_WALL_ACTIVE :
6110 EL_DC_MAGIC_WALL_ACTIVE);
6112 /* activate magic wall / mill */
6113 SCAN_PLAYFIELD(xx, yy)
6115 if (Feld[xx][yy] == smashed)
6116 Feld[xx][yy] = activated_magic_wall;
6119 game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
6120 game.magic_wall_active = TRUE;
6122 PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
6123 SND_MAGIC_WALL_ACTIVATING :
6124 smashed == EL_BD_MAGIC_WALL ?
6125 SND_BD_MAGIC_WALL_ACTIVATING :
6126 SND_DC_MAGIC_WALL_ACTIVATING));
6129 if (IS_PLAYER(x, y + 1))
6131 if (CAN_SMASH_PLAYER(element))
6133 KillPlayerUnlessEnemyProtected(x, y + 1);
6137 else if (smashed == EL_PENGUIN)
6139 if (CAN_SMASH_PLAYER(element))
6145 else if (element == EL_BD_DIAMOND)
6147 if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
6153 else if (((element == EL_SP_INFOTRON ||
6154 element == EL_SP_ZONK) &&
6155 (smashed == EL_SP_SNIKSNAK ||
6156 smashed == EL_SP_ELECTRON ||
6157 smashed == EL_SP_DISK_ORANGE)) ||
6158 (element == EL_SP_INFOTRON &&
6159 smashed == EL_SP_DISK_YELLOW))
6164 else if (CAN_SMASH_EVERYTHING(element))
6166 if (IS_CLASSIC_ENEMY(smashed) ||
6167 CAN_EXPLODE_SMASHED(smashed))
6172 else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
6174 if (smashed == EL_LAMP ||
6175 smashed == EL_LAMP_ACTIVE)
6180 else if (smashed == EL_NUT)
6182 Feld[x][y + 1] = EL_NUT_BREAKING;
6183 PlayLevelSound(x, y, SND_NUT_BREAKING);
6184 RaiseScoreElement(EL_NUT);
6187 else if (smashed == EL_PEARL)
6189 ResetGfxAnimation(x, y);
6191 Feld[x][y + 1] = EL_PEARL_BREAKING;
6192 PlayLevelSound(x, y, SND_PEARL_BREAKING);
6195 else if (smashed == EL_DIAMOND)
6197 Feld[x][y + 1] = EL_DIAMOND_BREAKING;
6198 PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
6201 else if (IS_BELT_SWITCH(smashed))
6203 ToggleBeltSwitch(x, y + 1);
6205 else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
6206 smashed == EL_SWITCHGATE_SWITCH_DOWN ||
6207 smashed == EL_DC_SWITCHGATE_SWITCH_UP ||
6208 smashed == EL_DC_SWITCHGATE_SWITCH_DOWN)
6210 ToggleSwitchgateSwitch(x, y + 1);
6212 else if (smashed == EL_LIGHT_SWITCH ||
6213 smashed == EL_LIGHT_SWITCH_ACTIVE)
6215 ToggleLightSwitch(x, y + 1);
6219 CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6221 CheckElementChangeBySide(x, y + 1, smashed, element,
6222 CE_SWITCHED, CH_SIDE_TOP);
6223 CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
6229 CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6234 /* play sound of magic wall / mill */
6236 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
6237 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ||
6238 Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE))
6240 if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
6241 PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
6242 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
6243 PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
6244 else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
6245 PlayLevelSound(x, y, SND_DC_MAGIC_WALL_FILLING);
6250 /* play sound of object that hits the ground */
6251 if (last_line || object_hit)
6252 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6255 inline static void TurnRoundExt(int x, int y)
6267 { 0, 0 }, { 0, 0 }, { 0, 0 },
6272 int left, right, back;
6276 { MV_DOWN, MV_UP, MV_RIGHT },
6277 { MV_UP, MV_DOWN, MV_LEFT },
6279 { MV_LEFT, MV_RIGHT, MV_DOWN },
6283 { MV_RIGHT, MV_LEFT, MV_UP }
6286 int element = Feld[x][y];
6287 int move_pattern = element_info[element].move_pattern;
6289 int old_move_dir = MovDir[x][y];
6290 int left_dir = turn[old_move_dir].left;
6291 int right_dir = turn[old_move_dir].right;
6292 int back_dir = turn[old_move_dir].back;
6294 int left_dx = move_xy[left_dir].dx, left_dy = move_xy[left_dir].dy;
6295 int right_dx = move_xy[right_dir].dx, right_dy = move_xy[right_dir].dy;
6296 int move_dx = move_xy[old_move_dir].dx, move_dy = move_xy[old_move_dir].dy;
6297 int back_dx = move_xy[back_dir].dx, back_dy = move_xy[back_dir].dy;
6299 int left_x = x + left_dx, left_y = y + left_dy;
6300 int right_x = x + right_dx, right_y = y + right_dy;
6301 int move_x = x + move_dx, move_y = y + move_dy;
6305 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
6307 TestIfBadThingTouchesOtherBadThing(x, y);
6309 if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
6310 MovDir[x][y] = right_dir;
6311 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6312 MovDir[x][y] = left_dir;
6314 if (element == EL_BUG && MovDir[x][y] != old_move_dir)
6316 else if (element == EL_BD_BUTTERFLY) /* && MovDir[x][y] == left_dir) */
6319 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
6321 TestIfBadThingTouchesOtherBadThing(x, y);
6323 if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
6324 MovDir[x][y] = left_dir;
6325 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6326 MovDir[x][y] = right_dir;
6328 if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
6330 else if (element == EL_BD_FIREFLY) /* && MovDir[x][y] == right_dir) */
6333 else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
6335 TestIfBadThingTouchesOtherBadThing(x, y);
6337 if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
6338 MovDir[x][y] = left_dir;
6339 else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
6340 MovDir[x][y] = right_dir;
6342 if (MovDir[x][y] != old_move_dir)
6345 else if (element == EL_YAMYAM)
6347 boolean can_turn_left = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
6348 boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
6350 if (can_turn_left && can_turn_right)
6351 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6352 else if (can_turn_left)
6353 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6354 else if (can_turn_right)
6355 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6357 MovDir[x][y] = back_dir;
6359 MovDelay[x][y] = 16 + 16 * RND(3);
6361 else if (element == EL_DARK_YAMYAM)
6363 boolean can_turn_left = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6365 boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6368 if (can_turn_left && can_turn_right)
6369 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6370 else if (can_turn_left)
6371 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6372 else if (can_turn_right)
6373 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6375 MovDir[x][y] = back_dir;
6377 MovDelay[x][y] = 16 + 16 * RND(3);
6379 else if (element == EL_PACMAN)
6381 boolean can_turn_left = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
6382 boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
6384 if (can_turn_left && can_turn_right)
6385 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6386 else if (can_turn_left)
6387 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6388 else if (can_turn_right)
6389 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6391 MovDir[x][y] = back_dir;
6393 MovDelay[x][y] = 6 + RND(40);
6395 else if (element == EL_PIG)
6397 boolean can_turn_left = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
6398 boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
6399 boolean can_move_on = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
6400 boolean should_turn_left, should_turn_right, should_move_on;
6402 int rnd = RND(rnd_value);
6404 should_turn_left = (can_turn_left &&
6406 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
6407 y + back_dy + left_dy)));
6408 should_turn_right = (can_turn_right &&
6410 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
6411 y + back_dy + right_dy)));
6412 should_move_on = (can_move_on &&
6415 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
6416 y + move_dy + left_dy) ||
6417 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
6418 y + move_dy + right_dy)));
6420 if (should_turn_left || should_turn_right || should_move_on)
6422 if (should_turn_left && should_turn_right && should_move_on)
6423 MovDir[x][y] = (rnd < rnd_value / 3 ? left_dir :
6424 rnd < 2 * rnd_value / 3 ? right_dir :
6426 else if (should_turn_left && should_turn_right)
6427 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6428 else if (should_turn_left && should_move_on)
6429 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
6430 else if (should_turn_right && should_move_on)
6431 MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
6432 else if (should_turn_left)
6433 MovDir[x][y] = left_dir;
6434 else if (should_turn_right)
6435 MovDir[x][y] = right_dir;
6436 else if (should_move_on)
6437 MovDir[x][y] = old_move_dir;
6439 else if (can_move_on && rnd > rnd_value / 8)
6440 MovDir[x][y] = old_move_dir;
6441 else if (can_turn_left && can_turn_right)
6442 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6443 else if (can_turn_left && rnd > rnd_value / 8)
6444 MovDir[x][y] = left_dir;
6445 else if (can_turn_right && rnd > rnd_value/8)
6446 MovDir[x][y] = right_dir;
6448 MovDir[x][y] = back_dir;
6450 xx = x + move_xy[MovDir[x][y]].dx;
6451 yy = y + move_xy[MovDir[x][y]].dy;
6453 if (!IN_LEV_FIELD(xx, yy) ||
6454 (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy])))
6455 MovDir[x][y] = old_move_dir;
6459 else if (element == EL_DRAGON)
6461 boolean can_turn_left = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
6462 boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
6463 boolean can_move_on = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
6465 int rnd = RND(rnd_value);
6467 if (can_move_on && rnd > rnd_value / 8)
6468 MovDir[x][y] = old_move_dir;
6469 else if (can_turn_left && can_turn_right)
6470 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6471 else if (can_turn_left && rnd > rnd_value / 8)
6472 MovDir[x][y] = left_dir;
6473 else if (can_turn_right && rnd > rnd_value / 8)
6474 MovDir[x][y] = right_dir;
6476 MovDir[x][y] = back_dir;
6478 xx = x + move_xy[MovDir[x][y]].dx;
6479 yy = y + move_xy[MovDir[x][y]].dy;
6481 if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
6482 MovDir[x][y] = old_move_dir;
6486 else if (element == EL_MOLE)
6488 boolean can_move_on =
6489 (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
6490 IS_AMOEBOID(Feld[move_x][move_y]) ||
6491 Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
6494 boolean can_turn_left =
6495 (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
6496 IS_AMOEBOID(Feld[left_x][left_y])));
6498 boolean can_turn_right =
6499 (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
6500 IS_AMOEBOID(Feld[right_x][right_y])));
6502 if (can_turn_left && can_turn_right)
6503 MovDir[x][y] = (RND(2) ? left_dir : right_dir);
6504 else if (can_turn_left)
6505 MovDir[x][y] = left_dir;
6507 MovDir[x][y] = right_dir;
6510 if (MovDir[x][y] != old_move_dir)
6513 else if (element == EL_BALLOON)
6515 MovDir[x][y] = game.wind_direction;
6518 else if (element == EL_SPRING)
6520 if (MovDir[x][y] & MV_HORIZONTAL)
6522 if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
6523 !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
6525 Feld[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
6526 ResetGfxAnimation(move_x, move_y);
6527 TEST_DrawLevelField(move_x, move_y);
6529 MovDir[x][y] = back_dir;
6531 else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
6532 SPRING_CAN_ENTER_FIELD(element, x, y + 1))
6533 MovDir[x][y] = MV_NONE;
6538 else if (element == EL_ROBOT ||
6539 element == EL_SATELLITE ||
6540 element == EL_PENGUIN ||
6541 element == EL_EMC_ANDROID)
6543 int attr_x = -1, attr_y = -1;
6554 for (i = 0; i < MAX_PLAYERS; i++)
6556 struct PlayerInfo *player = &stored_player[i];
6557 int jx = player->jx, jy = player->jy;
6559 if (!player->active)
6563 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
6571 if (element == EL_ROBOT && ZX >= 0 && ZY >= 0 &&
6572 (Feld[ZX][ZY] == EL_ROBOT_WHEEL_ACTIVE ||
6573 game.engine_version < VERSION_IDENT(3,1,0,0)))
6579 if (element == EL_PENGUIN)
6582 static int xy[4][2] =
6590 for (i = 0; i < NUM_DIRECTIONS; i++)
6592 int ex = x + xy[i][0];
6593 int ey = y + xy[i][1];
6595 if (IN_LEV_FIELD(ex, ey) && (Feld[ex][ey] == EL_EXIT_OPEN ||
6596 Feld[ex][ey] == EL_EM_EXIT_OPEN ||
6597 Feld[ex][ey] == EL_STEEL_EXIT_OPEN ||
6598 Feld[ex][ey] == EL_EM_STEEL_EXIT_OPEN))
6607 MovDir[x][y] = MV_NONE;
6609 MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
6610 else if (attr_x > x)
6611 MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
6613 MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
6614 else if (attr_y > y)
6615 MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
6617 if (element == EL_ROBOT)
6621 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6622 MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
6623 Moving2Blocked(x, y, &newx, &newy);
6625 if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
6626 MovDelay[x][y] = 8 + 8 * !RND(3);
6628 MovDelay[x][y] = 16;
6630 else if (element == EL_PENGUIN)
6636 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6638 boolean first_horiz = RND(2);
6639 int new_move_dir = MovDir[x][y];
6642 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6643 Moving2Blocked(x, y, &newx, &newy);
6645 if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
6649 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6650 Moving2Blocked(x, y, &newx, &newy);
6652 if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
6655 MovDir[x][y] = old_move_dir;
6659 else if (element == EL_SATELLITE)
6665 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6667 boolean first_horiz = RND(2);
6668 int new_move_dir = MovDir[x][y];
6671 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6672 Moving2Blocked(x, y, &newx, &newy);
6674 if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
6678 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6679 Moving2Blocked(x, y, &newx, &newy);
6681 if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
6684 MovDir[x][y] = old_move_dir;
6688 else if (element == EL_EMC_ANDROID)
6690 static int check_pos[16] =
6692 -1, /* 0 => (invalid) */
6693 7, /* 1 => MV_LEFT */
6694 3, /* 2 => MV_RIGHT */
6695 -1, /* 3 => (invalid) */
6697 0, /* 5 => MV_LEFT | MV_UP */
6698 2, /* 6 => MV_RIGHT | MV_UP */
6699 -1, /* 7 => (invalid) */
6700 5, /* 8 => MV_DOWN */
6701 6, /* 9 => MV_LEFT | MV_DOWN */
6702 4, /* 10 => MV_RIGHT | MV_DOWN */
6703 -1, /* 11 => (invalid) */
6704 -1, /* 12 => (invalid) */
6705 -1, /* 13 => (invalid) */
6706 -1, /* 14 => (invalid) */
6707 -1, /* 15 => (invalid) */
6715 { -1, -1, MV_LEFT | MV_UP },
6717 { +1, -1, MV_RIGHT | MV_UP },
6718 { +1, 0, MV_RIGHT },
6719 { +1, +1, MV_RIGHT | MV_DOWN },
6721 { -1, +1, MV_LEFT | MV_DOWN },
6724 int start_pos, check_order;
6725 boolean can_clone = FALSE;
6728 /* check if there is any free field around current position */
6729 for (i = 0; i < 8; i++)
6731 int newx = x + check_xy[i].dx;
6732 int newy = y + check_xy[i].dy;
6734 if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
6742 if (can_clone) /* randomly find an element to clone */
6746 start_pos = check_pos[RND(8)];
6747 check_order = (RND(2) ? -1 : +1);
6749 for (i = 0; i < 8; i++)
6751 int pos_raw = start_pos + i * check_order;
6752 int pos = (pos_raw + 8) % 8;
6753 int newx = x + check_xy[pos].dx;
6754 int newy = y + check_xy[pos].dy;
6756 if (ANDROID_CAN_CLONE_FIELD(newx, newy))
6758 element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
6759 element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
6761 Store[x][y] = Feld[newx][newy];
6770 if (can_clone) /* randomly find a direction to move */
6774 start_pos = check_pos[RND(8)];
6775 check_order = (RND(2) ? -1 : +1);
6777 for (i = 0; i < 8; i++)
6779 int pos_raw = start_pos + i * check_order;
6780 int pos = (pos_raw + 8) % 8;
6781 int newx = x + check_xy[pos].dx;
6782 int newy = y + check_xy[pos].dy;
6783 int new_move_dir = check_xy[pos].dir;
6785 if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
6787 MovDir[x][y] = new_move_dir;
6788 MovDelay[x][y] = level.android_clone_time * 8 + 1;
6797 if (can_clone) /* cloning and moving successful */
6800 /* cannot clone -- try to move towards player */
6802 start_pos = check_pos[MovDir[x][y] & 0x0f];
6803 check_order = (RND(2) ? -1 : +1);
6805 for (i = 0; i < 3; i++)
6807 /* first check start_pos, then previous/next or (next/previous) pos */
6808 int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
6809 int pos = (pos_raw + 8) % 8;
6810 int newx = x + check_xy[pos].dx;
6811 int newy = y + check_xy[pos].dy;
6812 int new_move_dir = check_xy[pos].dir;
6814 if (IS_PLAYER(newx, newy))
6817 if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
6819 MovDir[x][y] = new_move_dir;
6820 MovDelay[x][y] = level.android_move_time * 8 + 1;
6827 else if (move_pattern == MV_TURNING_LEFT ||
6828 move_pattern == MV_TURNING_RIGHT ||
6829 move_pattern == MV_TURNING_LEFT_RIGHT ||
6830 move_pattern == MV_TURNING_RIGHT_LEFT ||
6831 move_pattern == MV_TURNING_RANDOM ||
6832 move_pattern == MV_ALL_DIRECTIONS)
6834 boolean can_turn_left =
6835 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
6836 boolean can_turn_right =
6837 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
6839 if (element_info[element].move_stepsize == 0) /* "not moving" */
6842 if (move_pattern == MV_TURNING_LEFT)
6843 MovDir[x][y] = left_dir;
6844 else if (move_pattern == MV_TURNING_RIGHT)
6845 MovDir[x][y] = right_dir;
6846 else if (move_pattern == MV_TURNING_LEFT_RIGHT)
6847 MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
6848 else if (move_pattern == MV_TURNING_RIGHT_LEFT)
6849 MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
6850 else if (move_pattern == MV_TURNING_RANDOM)
6851 MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
6852 can_turn_right && !can_turn_left ? right_dir :
6853 RND(2) ? left_dir : right_dir);
6854 else if (can_turn_left && can_turn_right)
6855 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6856 else if (can_turn_left)
6857 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6858 else if (can_turn_right)
6859 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6861 MovDir[x][y] = back_dir;
6863 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6865 else if (move_pattern == MV_HORIZONTAL ||
6866 move_pattern == MV_VERTICAL)
6868 if (move_pattern & old_move_dir)
6869 MovDir[x][y] = back_dir;
6870 else if (move_pattern == MV_HORIZONTAL)
6871 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
6872 else if (move_pattern == MV_VERTICAL)
6873 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
6875 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6877 else if (move_pattern & MV_ANY_DIRECTION)
6879 MovDir[x][y] = move_pattern;
6880 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6882 else if (move_pattern & MV_WIND_DIRECTION)
6884 MovDir[x][y] = game.wind_direction;
6885 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6887 else if (move_pattern == MV_ALONG_LEFT_SIDE)
6889 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
6890 MovDir[x][y] = left_dir;
6891 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
6892 MovDir[x][y] = right_dir;
6894 if (MovDir[x][y] != old_move_dir)
6895 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6897 else if (move_pattern == MV_ALONG_RIGHT_SIDE)
6899 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
6900 MovDir[x][y] = right_dir;
6901 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
6902 MovDir[x][y] = left_dir;
6904 if (MovDir[x][y] != old_move_dir)
6905 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6907 else if (move_pattern == MV_TOWARDS_PLAYER ||
6908 move_pattern == MV_AWAY_FROM_PLAYER)
6910 int attr_x = -1, attr_y = -1;
6912 boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
6923 for (i = 0; i < MAX_PLAYERS; i++)
6925 struct PlayerInfo *player = &stored_player[i];
6926 int jx = player->jx, jy = player->jy;
6928 if (!player->active)
6932 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
6940 MovDir[x][y] = MV_NONE;
6942 MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
6943 else if (attr_x > x)
6944 MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
6946 MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
6947 else if (attr_y > y)
6948 MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
6950 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6952 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6954 boolean first_horiz = RND(2);
6955 int new_move_dir = MovDir[x][y];
6957 if (element_info[element].move_stepsize == 0) /* "not moving" */
6959 first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
6960 MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6966 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6967 Moving2Blocked(x, y, &newx, &newy);
6969 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
6973 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6974 Moving2Blocked(x, y, &newx, &newy);
6976 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
6979 MovDir[x][y] = old_move_dir;
6982 else if (move_pattern == MV_WHEN_PUSHED ||
6983 move_pattern == MV_WHEN_DROPPED)
6985 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
6986 MovDir[x][y] = MV_NONE;
6990 else if (move_pattern & MV_MAZE_RUNNER_STYLE)
6992 static int test_xy[7][2] =
7002 static int test_dir[7] =
7012 boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
7013 int move_preference = -1000000; /* start with very low preference */
7014 int new_move_dir = MV_NONE;
7015 int start_test = RND(4);
7018 for (i = 0; i < NUM_DIRECTIONS; i++)
7020 int move_dir = test_dir[start_test + i];
7021 int move_dir_preference;
7023 xx = x + test_xy[start_test + i][0];
7024 yy = y + test_xy[start_test + i][1];
7026 if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
7027 (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
7029 new_move_dir = move_dir;
7034 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
7037 move_dir_preference = -1 * RunnerVisit[xx][yy];
7038 if (hunter_mode && PlayerVisit[xx][yy] > 0)
7039 move_dir_preference = PlayerVisit[xx][yy];
7041 if (move_dir_preference > move_preference)
7043 /* prefer field that has not been visited for the longest time */
7044 move_preference = move_dir_preference;
7045 new_move_dir = move_dir;
7047 else if (move_dir_preference == move_preference &&
7048 move_dir == old_move_dir)
7050 /* prefer last direction when all directions are preferred equally */
7051 move_preference = move_dir_preference;
7052 new_move_dir = move_dir;
7056 MovDir[x][y] = new_move_dir;
7057 if (old_move_dir != new_move_dir)
7058 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7062 static void TurnRound(int x, int y)
7064 int direction = MovDir[x][y];
7068 GfxDir[x][y] = MovDir[x][y];
7070 if (direction != MovDir[x][y])
7074 GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
7076 ResetGfxFrame(x, y);
7079 static boolean JustBeingPushed(int x, int y)
7083 for (i = 0; i < MAX_PLAYERS; i++)
7085 struct PlayerInfo *player = &stored_player[i];
7087 if (player->active && player->is_pushing && player->MovPos)
7089 int next_jx = player->jx + (player->jx - player->last_jx);
7090 int next_jy = player->jy + (player->jy - player->last_jy);
7092 if (x == next_jx && y == next_jy)
7100 void StartMoving(int x, int y)
7102 boolean started_moving = FALSE; /* some elements can fall _and_ move */
7103 int element = Feld[x][y];
7108 if (MovDelay[x][y] == 0)
7109 GfxAction[x][y] = ACTION_DEFAULT;
7111 if (CAN_FALL(element) && y < lev_fieldy - 1)
7113 if ((x > 0 && IS_PLAYER(x - 1, y)) ||
7114 (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
7115 if (JustBeingPushed(x, y))
7118 if (element == EL_QUICKSAND_FULL)
7120 if (IS_FREE(x, y + 1))
7122 InitMovingField(x, y, MV_DOWN);
7123 started_moving = TRUE;
7125 Feld[x][y] = EL_QUICKSAND_EMPTYING;
7126 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7127 if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7128 Store[x][y] = EL_ROCK;
7130 Store[x][y] = EL_ROCK;
7133 PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7135 else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7137 if (!MovDelay[x][y])
7139 MovDelay[x][y] = TILEY + 1;
7141 ResetGfxAnimation(x, y);
7142 ResetGfxAnimation(x, y + 1);
7147 DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7148 DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7155 Feld[x][y] = EL_QUICKSAND_EMPTY;
7156 Feld[x][y + 1] = EL_QUICKSAND_FULL;
7157 Store[x][y + 1] = Store[x][y];
7160 PlayLevelSoundAction(x, y, ACTION_FILLING);
7162 else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7164 if (!MovDelay[x][y])
7166 MovDelay[x][y] = TILEY + 1;
7168 ResetGfxAnimation(x, y);
7169 ResetGfxAnimation(x, y + 1);
7174 DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7175 DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7182 Feld[x][y] = EL_QUICKSAND_EMPTY;
7183 Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7184 Store[x][y + 1] = Store[x][y];
7187 PlayLevelSoundAction(x, y, ACTION_FILLING);
7190 else if (element == EL_QUICKSAND_FAST_FULL)
7192 if (IS_FREE(x, y + 1))
7194 InitMovingField(x, y, MV_DOWN);
7195 started_moving = TRUE;
7197 Feld[x][y] = EL_QUICKSAND_FAST_EMPTYING;
7198 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7199 if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7200 Store[x][y] = EL_ROCK;
7202 Store[x][y] = EL_ROCK;
7205 PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7207 else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7209 if (!MovDelay[x][y])
7211 MovDelay[x][y] = TILEY + 1;
7213 ResetGfxAnimation(x, y);
7214 ResetGfxAnimation(x, y + 1);
7219 DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7220 DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7227 Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
7228 Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7229 Store[x][y + 1] = Store[x][y];
7232 PlayLevelSoundAction(x, y, ACTION_FILLING);
7234 else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7236 if (!MovDelay[x][y])
7238 MovDelay[x][y] = TILEY + 1;
7240 ResetGfxAnimation(x, y);
7241 ResetGfxAnimation(x, y + 1);
7246 DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7247 DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7254 Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
7255 Feld[x][y + 1] = EL_QUICKSAND_FULL;
7256 Store[x][y + 1] = Store[x][y];
7259 PlayLevelSoundAction(x, y, ACTION_FILLING);
7262 else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7263 Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7265 InitMovingField(x, y, MV_DOWN);
7266 started_moving = TRUE;
7268 Feld[x][y] = EL_QUICKSAND_FILLING;
7269 Store[x][y] = element;
7271 PlayLevelSoundAction(x, y, ACTION_FILLING);
7273 else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7274 Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7276 InitMovingField(x, y, MV_DOWN);
7277 started_moving = TRUE;
7279 Feld[x][y] = EL_QUICKSAND_FAST_FILLING;
7280 Store[x][y] = element;
7282 PlayLevelSoundAction(x, y, ACTION_FILLING);
7284 else if (element == EL_MAGIC_WALL_FULL)
7286 if (IS_FREE(x, y + 1))
7288 InitMovingField(x, y, MV_DOWN);
7289 started_moving = TRUE;
7291 Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
7292 Store[x][y] = EL_CHANGED(Store[x][y]);
7294 else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
7296 if (!MovDelay[x][y])
7297 MovDelay[x][y] = TILEY / 4 + 1;
7306 Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
7307 Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
7308 Store[x][y + 1] = EL_CHANGED(Store[x][y]);
7312 else if (element == EL_BD_MAGIC_WALL_FULL)
7314 if (IS_FREE(x, y + 1))
7316 InitMovingField(x, y, MV_DOWN);
7317 started_moving = TRUE;
7319 Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
7320 Store[x][y] = EL_CHANGED_BD(Store[x][y]);
7322 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
7324 if (!MovDelay[x][y])
7325 MovDelay[x][y] = TILEY / 4 + 1;
7334 Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
7335 Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
7336 Store[x][y + 1] = EL_CHANGED_BD(Store[x][y]);
7340 else if (element == EL_DC_MAGIC_WALL_FULL)
7342 if (IS_FREE(x, y + 1))
7344 InitMovingField(x, y, MV_DOWN);
7345 started_moving = TRUE;
7347 Feld[x][y] = EL_DC_MAGIC_WALL_EMPTYING;
7348 Store[x][y] = EL_CHANGED_DC(Store[x][y]);
7350 else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
7352 if (!MovDelay[x][y])
7353 MovDelay[x][y] = TILEY / 4 + 1;
7362 Feld[x][y] = EL_DC_MAGIC_WALL_ACTIVE;
7363 Feld[x][y + 1] = EL_DC_MAGIC_WALL_FULL;
7364 Store[x][y + 1] = EL_CHANGED_DC(Store[x][y]);
7368 else if ((CAN_PASS_MAGIC_WALL(element) &&
7369 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
7370 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)) ||
7371 (CAN_PASS_DC_MAGIC_WALL(element) &&
7372 (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)))
7375 InitMovingField(x, y, MV_DOWN);
7376 started_moving = TRUE;
7379 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
7380 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ? EL_BD_MAGIC_WALL_FILLING :
7381 EL_DC_MAGIC_WALL_FILLING);
7382 Store[x][y] = element;
7384 else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
7386 SplashAcid(x, y + 1);
7388 InitMovingField(x, y, MV_DOWN);
7389 started_moving = TRUE;
7391 Store[x][y] = EL_ACID;
7394 (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7395 CheckImpact[x][y] && !IS_FREE(x, y + 1)) ||
7396 (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
7397 CAN_FALL(element) && WasJustFalling[x][y] &&
7398 (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
7400 (game.engine_version < VERSION_IDENT(2,2,0,7) &&
7401 CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
7402 (Feld[x][y + 1] == EL_BLOCKED)))
7404 /* this is needed for a special case not covered by calling "Impact()"
7405 from "ContinueMoving()": if an element moves to a tile directly below
7406 another element which was just falling on that tile (which was empty
7407 in the previous frame), the falling element above would just stop
7408 instead of smashing the element below (in previous version, the above
7409 element was just checked for "moving" instead of "falling", resulting
7410 in incorrect smashes caused by horizontal movement of the above
7411 element; also, the case of the player being the element to smash was
7412 simply not covered here... :-/ ) */
7414 CheckCollision[x][y] = 0;
7415 CheckImpact[x][y] = 0;
7419 else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
7421 if (MovDir[x][y] == MV_NONE)
7423 InitMovingField(x, y, MV_DOWN);
7424 started_moving = TRUE;
7427 else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
7429 if (WasJustFalling[x][y]) /* prevent animation from being restarted */
7430 MovDir[x][y] = MV_DOWN;
7432 InitMovingField(x, y, MV_DOWN);
7433 started_moving = TRUE;
7435 else if (element == EL_AMOEBA_DROP)
7437 Feld[x][y] = EL_AMOEBA_GROWING;
7438 Store[x][y] = EL_AMOEBA_WET;
7440 else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
7441 (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
7442 !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
7443 element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
7445 boolean can_fall_left = (x > 0 && IS_FREE(x - 1, y) &&
7446 (IS_FREE(x - 1, y + 1) ||
7447 Feld[x - 1][y + 1] == EL_ACID));
7448 boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
7449 (IS_FREE(x + 1, y + 1) ||
7450 Feld[x + 1][y + 1] == EL_ACID));
7451 boolean can_fall_any = (can_fall_left || can_fall_right);
7452 boolean can_fall_both = (can_fall_left && can_fall_right);
7453 int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
7455 if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
7457 if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
7458 can_fall_right = FALSE;
7459 else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
7460 can_fall_left = FALSE;
7461 else if (slippery_type == SLIPPERY_ONLY_LEFT)
7462 can_fall_right = FALSE;
7463 else if (slippery_type == SLIPPERY_ONLY_RIGHT)
7464 can_fall_left = FALSE;
7466 can_fall_any = (can_fall_left || can_fall_right);
7467 can_fall_both = FALSE;
7472 if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
7473 can_fall_right = FALSE; /* slip down on left side */
7475 can_fall_left = !(can_fall_right = RND(2));
7477 can_fall_both = FALSE;
7482 /* if not determined otherwise, prefer left side for slipping down */
7483 InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
7484 started_moving = TRUE;
7487 else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
7489 boolean left_is_free = (x > 0 && IS_FREE(x - 1, y));
7490 boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
7491 int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
7492 int belt_dir = game.belt_dir[belt_nr];
7494 if ((belt_dir == MV_LEFT && left_is_free) ||
7495 (belt_dir == MV_RIGHT && right_is_free))
7497 int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
7499 InitMovingField(x, y, belt_dir);
7500 started_moving = TRUE;
7502 Pushed[x][y] = TRUE;
7503 Pushed[nextx][y] = TRUE;
7505 GfxAction[x][y] = ACTION_DEFAULT;
7509 MovDir[x][y] = 0; /* if element was moving, stop it */
7514 /* not "else if" because of elements that can fall and move (EL_SPRING) */
7515 if (CAN_MOVE(element) && !started_moving)
7517 int move_pattern = element_info[element].move_pattern;
7520 Moving2Blocked(x, y, &newx, &newy);
7522 if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
7525 if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7526 CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7528 WasJustMoving[x][y] = 0;
7529 CheckCollision[x][y] = 0;
7531 TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
7533 if (Feld[x][y] != element) /* element has changed */
7537 if (!MovDelay[x][y]) /* start new movement phase */
7539 /* all objects that can change their move direction after each step
7540 (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
7542 if (element != EL_YAMYAM &&
7543 element != EL_DARK_YAMYAM &&
7544 element != EL_PACMAN &&
7545 !(move_pattern & MV_ANY_DIRECTION) &&
7546 move_pattern != MV_TURNING_LEFT &&
7547 move_pattern != MV_TURNING_RIGHT &&
7548 move_pattern != MV_TURNING_LEFT_RIGHT &&
7549 move_pattern != MV_TURNING_RIGHT_LEFT &&
7550 move_pattern != MV_TURNING_RANDOM)
7554 if (MovDelay[x][y] && (element == EL_BUG ||
7555 element == EL_SPACESHIP ||
7556 element == EL_SP_SNIKSNAK ||
7557 element == EL_SP_ELECTRON ||
7558 element == EL_MOLE))
7559 TEST_DrawLevelField(x, y);
7563 if (MovDelay[x][y]) /* wait some time before next movement */
7567 if (element == EL_ROBOT ||
7568 element == EL_YAMYAM ||
7569 element == EL_DARK_YAMYAM)
7571 DrawLevelElementAnimationIfNeeded(x, y, element);
7572 PlayLevelSoundAction(x, y, ACTION_WAITING);
7574 else if (element == EL_SP_ELECTRON)
7575 DrawLevelElementAnimationIfNeeded(x, y, element);
7576 else if (element == EL_DRAGON)
7579 int dir = MovDir[x][y];
7580 int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
7581 int dy = (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
7582 int graphic = (dir == MV_LEFT ? IMG_FLAMES_1_LEFT :
7583 dir == MV_RIGHT ? IMG_FLAMES_1_RIGHT :
7584 dir == MV_UP ? IMG_FLAMES_1_UP :
7585 dir == MV_DOWN ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
7586 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
7588 GfxAction[x][y] = ACTION_ATTACKING;
7590 if (IS_PLAYER(x, y))
7591 DrawPlayerField(x, y);
7593 TEST_DrawLevelField(x, y);
7595 PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
7597 for (i = 1; i <= 3; i++)
7599 int xx = x + i * dx;
7600 int yy = y + i * dy;
7601 int sx = SCREENX(xx);
7602 int sy = SCREENY(yy);
7603 int flame_graphic = graphic + (i - 1);
7605 if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
7610 int flamed = MovingOrBlocked2Element(xx, yy);
7612 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
7615 RemoveMovingField(xx, yy);
7617 ChangeDelay[xx][yy] = 0;
7619 Feld[xx][yy] = EL_FLAMES;
7621 if (IN_SCR_FIELD(sx, sy))
7623 TEST_DrawLevelFieldCrumbled(xx, yy);
7624 DrawGraphic(sx, sy, flame_graphic, frame);
7629 if (Feld[xx][yy] == EL_FLAMES)
7630 Feld[xx][yy] = EL_EMPTY;
7631 TEST_DrawLevelField(xx, yy);
7636 if (MovDelay[x][y]) /* element still has to wait some time */
7638 PlayLevelSoundAction(x, y, ACTION_WAITING);
7644 /* now make next step */
7646 Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
7648 if (DONT_COLLIDE_WITH(element) &&
7649 IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
7650 !PLAYER_ENEMY_PROTECTED(newx, newy))
7652 TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
7657 else if (CAN_MOVE_INTO_ACID(element) &&
7658 IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID &&
7659 !IS_MV_DIAGONAL(MovDir[x][y]) &&
7660 (MovDir[x][y] == MV_DOWN ||
7661 game.engine_version >= VERSION_IDENT(3,1,0,0)))
7663 SplashAcid(newx, newy);
7664 Store[x][y] = EL_ACID;
7666 else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
7668 if (Feld[newx][newy] == EL_EXIT_OPEN ||
7669 Feld[newx][newy] == EL_EM_EXIT_OPEN ||
7670 Feld[newx][newy] == EL_STEEL_EXIT_OPEN ||
7671 Feld[newx][newy] == EL_EM_STEEL_EXIT_OPEN)
7674 TEST_DrawLevelField(x, y);
7676 PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
7677 if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
7678 DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
7680 local_player->friends_still_needed--;
7681 if (!local_player->friends_still_needed &&
7682 !local_player->GameOver && AllPlayersGone)
7683 PlayerWins(local_player);
7687 else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
7689 if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MP_MOVING)
7690 TEST_DrawLevelField(newx, newy);
7692 GfxDir[x][y] = MovDir[x][y] = MV_NONE;
7694 else if (!IS_FREE(newx, newy))
7696 GfxAction[x][y] = ACTION_WAITING;
7698 if (IS_PLAYER(x, y))
7699 DrawPlayerField(x, y);
7701 TEST_DrawLevelField(x, y);
7706 else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
7708 if (IS_FOOD_PIG(Feld[newx][newy]))
7710 if (IS_MOVING(newx, newy))
7711 RemoveMovingField(newx, newy);
7714 Feld[newx][newy] = EL_EMPTY;
7715 TEST_DrawLevelField(newx, newy);
7718 PlayLevelSound(x, y, SND_PIG_DIGGING);
7720 else if (!IS_FREE(newx, newy))
7722 if (IS_PLAYER(x, y))
7723 DrawPlayerField(x, y);
7725 TEST_DrawLevelField(x, y);
7730 else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
7732 if (Store[x][y] != EL_EMPTY)
7734 boolean can_clone = FALSE;
7737 /* check if element to clone is still there */
7738 for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
7740 if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == Store[x][y])
7748 /* cannot clone or target field not free anymore -- do not clone */
7749 if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7750 Store[x][y] = EL_EMPTY;
7753 if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7755 if (IS_MV_DIAGONAL(MovDir[x][y]))
7757 int diagonal_move_dir = MovDir[x][y];
7758 int stored = Store[x][y];
7759 int change_delay = 8;
7762 /* android is moving diagonally */
7764 CreateField(x, y, EL_DIAGONAL_SHRINKING);
7766 Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
7767 GfxElement[x][y] = EL_EMC_ANDROID;
7768 GfxAction[x][y] = ACTION_SHRINKING;
7769 GfxDir[x][y] = diagonal_move_dir;
7770 ChangeDelay[x][y] = change_delay;
7772 graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
7775 DrawLevelGraphicAnimation(x, y, graphic);
7776 PlayLevelSoundAction(x, y, ACTION_SHRINKING);
7778 if (Feld[newx][newy] == EL_ACID)
7780 SplashAcid(newx, newy);
7785 CreateField(newx, newy, EL_DIAGONAL_GROWING);
7787 Store[newx][newy] = EL_EMC_ANDROID;
7788 GfxElement[newx][newy] = EL_EMC_ANDROID;
7789 GfxAction[newx][newy] = ACTION_GROWING;
7790 GfxDir[newx][newy] = diagonal_move_dir;
7791 ChangeDelay[newx][newy] = change_delay;
7793 graphic = el_act_dir2img(GfxElement[newx][newy],
7794 GfxAction[newx][newy], GfxDir[newx][newy]);
7796 DrawLevelGraphicAnimation(newx, newy, graphic);
7797 PlayLevelSoundAction(newx, newy, ACTION_GROWING);
7803 Feld[newx][newy] = EL_EMPTY;
7804 TEST_DrawLevelField(newx, newy);
7806 PlayLevelSoundAction(x, y, ACTION_DIGGING);
7809 else if (!IS_FREE(newx, newy))
7814 else if (IS_CUSTOM_ELEMENT(element) &&
7815 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7817 if (!DigFieldByCE(newx, newy, element))
7820 if (move_pattern & MV_MAZE_RUNNER_STYLE)
7822 RunnerVisit[x][y] = FrameCounter;
7823 PlayerVisit[x][y] /= 8; /* expire player visit path */
7826 else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
7828 if (!IS_FREE(newx, newy))
7830 if (IS_PLAYER(x, y))
7831 DrawPlayerField(x, y);
7833 TEST_DrawLevelField(x, y);
7839 boolean wanna_flame = !RND(10);
7840 int dx = newx - x, dy = newy - y;
7841 int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
7842 int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
7843 int element1 = (IN_LEV_FIELD(newx1, newy1) ?
7844 MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
7845 int element2 = (IN_LEV_FIELD(newx2, newy2) ?
7846 MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
7849 IS_CLASSIC_ENEMY(element1) ||
7850 IS_CLASSIC_ENEMY(element2)) &&
7851 element1 != EL_DRAGON && element2 != EL_DRAGON &&
7852 element1 != EL_FLAMES && element2 != EL_FLAMES)
7854 ResetGfxAnimation(x, y);
7855 GfxAction[x][y] = ACTION_ATTACKING;
7857 if (IS_PLAYER(x, y))
7858 DrawPlayerField(x, y);
7860 TEST_DrawLevelField(x, y);
7862 PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
7864 MovDelay[x][y] = 50;
7866 Feld[newx][newy] = EL_FLAMES;
7867 if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
7868 Feld[newx1][newy1] = EL_FLAMES;
7869 if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
7870 Feld[newx2][newy2] = EL_FLAMES;
7876 else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
7877 Feld[newx][newy] == EL_DIAMOND)
7879 if (IS_MOVING(newx, newy))
7880 RemoveMovingField(newx, newy);
7883 Feld[newx][newy] = EL_EMPTY;
7884 TEST_DrawLevelField(newx, newy);
7887 PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
7889 else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
7890 IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
7892 if (AmoebaNr[newx][newy])
7894 AmoebaCnt2[AmoebaNr[newx][newy]]--;
7895 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
7896 Feld[newx][newy] == EL_BD_AMOEBA)
7897 AmoebaCnt[AmoebaNr[newx][newy]]--;
7900 if (IS_MOVING(newx, newy))
7902 RemoveMovingField(newx, newy);
7906 Feld[newx][newy] = EL_EMPTY;
7907 TEST_DrawLevelField(newx, newy);
7910 PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
7912 else if ((element == EL_PACMAN || element == EL_MOLE)
7913 && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
7915 if (AmoebaNr[newx][newy])
7917 AmoebaCnt2[AmoebaNr[newx][newy]]--;
7918 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
7919 Feld[newx][newy] == EL_BD_AMOEBA)
7920 AmoebaCnt[AmoebaNr[newx][newy]]--;
7923 if (element == EL_MOLE)
7925 Feld[newx][newy] = EL_AMOEBA_SHRINKING;
7926 PlayLevelSound(x, y, SND_MOLE_DIGGING);
7928 ResetGfxAnimation(x, y);
7929 GfxAction[x][y] = ACTION_DIGGING;
7930 TEST_DrawLevelField(x, y);
7932 MovDelay[newx][newy] = 0; /* start amoeba shrinking delay */
7934 return; /* wait for shrinking amoeba */
7936 else /* element == EL_PACMAN */
7938 Feld[newx][newy] = EL_EMPTY;
7939 TEST_DrawLevelField(newx, newy);
7940 PlayLevelSound(x, y, SND_PACMAN_DIGGING);
7943 else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
7944 (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
7945 (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
7947 /* wait for shrinking amoeba to completely disappear */
7950 else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
7952 /* object was running against a wall */
7956 if (GFX_ELEMENT(element) != EL_SAND) /* !!! FIX THIS (crumble) !!! */
7957 DrawLevelElementAnimation(x, y, element);
7959 if (DONT_TOUCH(element))
7960 TestIfBadThingTouchesPlayer(x, y);
7965 InitMovingField(x, y, MovDir[x][y]);
7967 PlayLevelSoundAction(x, y, ACTION_MOVING);
7971 ContinueMoving(x, y);
7974 void ContinueMoving(int x, int y)
7976 int element = Feld[x][y];
7977 struct ElementInfo *ei = &element_info[element];
7978 int direction = MovDir[x][y];
7979 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
7980 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
7981 int newx = x + dx, newy = y + dy;
7982 int stored = Store[x][y];
7983 int stored_new = Store[newx][newy];
7984 boolean pushed_by_player = (Pushed[x][y] && IS_PLAYER(x, y));
7985 boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
7986 boolean last_line = (newy == lev_fieldy - 1);
7988 MovPos[x][y] += getElementMoveStepsize(x, y);
7990 if (pushed_by_player) /* special case: moving object pushed by player */
7991 MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
7993 if (ABS(MovPos[x][y]) < TILEX)
7995 TEST_DrawLevelField(x, y);
7997 return; /* element is still moving */
8000 /* element reached destination field */
8002 Feld[x][y] = EL_EMPTY;
8003 Feld[newx][newy] = element;
8004 MovPos[x][y] = 0; /* force "not moving" for "crumbled sand" */
8006 if (Store[x][y] == EL_ACID) /* element is moving into acid pool */
8008 element = Feld[newx][newy] = EL_ACID;
8010 else if (element == EL_MOLE)
8012 Feld[x][y] = EL_SAND;
8014 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8016 else if (element == EL_QUICKSAND_FILLING)
8018 element = Feld[newx][newy] = get_next_element(element);
8019 Store[newx][newy] = Store[x][y];
8021 else if (element == EL_QUICKSAND_EMPTYING)
8023 Feld[x][y] = get_next_element(element);
8024 element = Feld[newx][newy] = Store[x][y];
8026 else if (element == EL_QUICKSAND_FAST_FILLING)
8028 element = Feld[newx][newy] = get_next_element(element);
8029 Store[newx][newy] = Store[x][y];
8031 else if (element == EL_QUICKSAND_FAST_EMPTYING)
8033 Feld[x][y] = get_next_element(element);
8034 element = Feld[newx][newy] = Store[x][y];
8036 else if (element == EL_MAGIC_WALL_FILLING)
8038 element = Feld[newx][newy] = get_next_element(element);
8039 if (!game.magic_wall_active)
8040 element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
8041 Store[newx][newy] = Store[x][y];
8043 else if (element == EL_MAGIC_WALL_EMPTYING)
8045 Feld[x][y] = get_next_element(element);
8046 if (!game.magic_wall_active)
8047 Feld[x][y] = EL_MAGIC_WALL_DEAD;
8048 element = Feld[newx][newy] = Store[x][y];
8050 InitField(newx, newy, FALSE);
8052 else if (element == EL_BD_MAGIC_WALL_FILLING)
8054 element = Feld[newx][newy] = get_next_element(element);
8055 if (!game.magic_wall_active)
8056 element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
8057 Store[newx][newy] = Store[x][y];
8059 else if (element == EL_BD_MAGIC_WALL_EMPTYING)
8061 Feld[x][y] = get_next_element(element);
8062 if (!game.magic_wall_active)
8063 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
8064 element = Feld[newx][newy] = Store[x][y];
8066 InitField(newx, newy, FALSE);
8068 else if (element == EL_DC_MAGIC_WALL_FILLING)
8070 element = Feld[newx][newy] = get_next_element(element);
8071 if (!game.magic_wall_active)
8072 element = Feld[newx][newy] = EL_DC_MAGIC_WALL_DEAD;
8073 Store[newx][newy] = Store[x][y];
8075 else if (element == EL_DC_MAGIC_WALL_EMPTYING)
8077 Feld[x][y] = get_next_element(element);
8078 if (!game.magic_wall_active)
8079 Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
8080 element = Feld[newx][newy] = Store[x][y];
8082 InitField(newx, newy, FALSE);
8084 else if (element == EL_AMOEBA_DROPPING)
8086 Feld[x][y] = get_next_element(element);
8087 element = Feld[newx][newy] = Store[x][y];
8089 else if (element == EL_SOKOBAN_OBJECT)
8092 Feld[x][y] = Back[x][y];
8094 if (Back[newx][newy])
8095 Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
8097 Back[x][y] = Back[newx][newy] = 0;
8100 Store[x][y] = EL_EMPTY;
8105 MovDelay[newx][newy] = 0;
8107 if (CAN_CHANGE_OR_HAS_ACTION(element))
8109 /* copy element change control values to new field */
8110 ChangeDelay[newx][newy] = ChangeDelay[x][y];
8111 ChangePage[newx][newy] = ChangePage[x][y];
8112 ChangeCount[newx][newy] = ChangeCount[x][y];
8113 ChangeEvent[newx][newy] = ChangeEvent[x][y];
8116 CustomValue[newx][newy] = CustomValue[x][y];
8118 ChangeDelay[x][y] = 0;
8119 ChangePage[x][y] = -1;
8120 ChangeCount[x][y] = 0;
8121 ChangeEvent[x][y] = -1;
8123 CustomValue[x][y] = 0;
8125 /* copy animation control values to new field */
8126 GfxFrame[newx][newy] = GfxFrame[x][y];
8127 GfxRandom[newx][newy] = GfxRandom[x][y]; /* keep same random value */
8128 GfxAction[newx][newy] = GfxAction[x][y]; /* keep action one frame */
8129 GfxDir[newx][newy] = GfxDir[x][y]; /* keep element direction */
8131 Pushed[x][y] = Pushed[newx][newy] = FALSE;
8133 /* some elements can leave other elements behind after moving */
8134 if (ei->move_leave_element != EL_EMPTY &&
8135 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
8136 (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
8138 int move_leave_element = ei->move_leave_element;
8140 /* this makes it possible to leave the removed element again */
8141 if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
8142 move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
8144 Feld[x][y] = move_leave_element;
8146 if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
8147 MovDir[x][y] = direction;
8149 InitField(x, y, FALSE);
8151 if (GFX_CRUMBLED(Feld[x][y]))
8152 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8154 if (ELEM_IS_PLAYER(move_leave_element))
8155 RelocatePlayer(x, y, move_leave_element);
8158 /* do this after checking for left-behind element */
8159 ResetGfxAnimation(x, y); /* reset animation values for old field */
8161 if (!CAN_MOVE(element) ||
8162 (CAN_FALL(element) && direction == MV_DOWN &&
8163 (element == EL_SPRING ||
8164 element_info[element].move_pattern == MV_WHEN_PUSHED ||
8165 element_info[element].move_pattern == MV_WHEN_DROPPED)))
8166 GfxDir[x][y] = MovDir[newx][newy] = 0;
8168 TEST_DrawLevelField(x, y);
8169 TEST_DrawLevelField(newx, newy);
8171 Stop[newx][newy] = TRUE; /* ignore this element until the next frame */
8173 /* prevent pushed element from moving on in pushed direction */
8174 if (pushed_by_player && CAN_MOVE(element) &&
8175 element_info[element].move_pattern & MV_ANY_DIRECTION &&
8176 !(element_info[element].move_pattern & direction))
8177 TurnRound(newx, newy);
8179 /* prevent elements on conveyor belt from moving on in last direction */
8180 if (pushed_by_conveyor && CAN_FALL(element) &&
8181 direction & MV_HORIZONTAL)
8182 MovDir[newx][newy] = 0;
8184 if (!pushed_by_player)
8186 int nextx = newx + dx, nexty = newy + dy;
8187 boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
8189 WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
8191 if (CAN_FALL(element) && direction == MV_DOWN)
8192 WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
8194 if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
8195 CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
8197 if (CAN_FALL(element) && direction == MV_DOWN && check_collision_again)
8198 CheckImpact[newx][newy] = CHECK_DELAY_IMPACT;
8201 if (DONT_TOUCH(element)) /* object may be nasty to player or others */
8203 TestIfBadThingTouchesPlayer(newx, newy);
8204 TestIfBadThingTouchesFriend(newx, newy);
8206 if (!IS_CUSTOM_ELEMENT(element))
8207 TestIfBadThingTouchesOtherBadThing(newx, newy);
8209 else if (element == EL_PENGUIN)
8210 TestIfFriendTouchesBadThing(newx, newy);
8212 if (DONT_GET_HIT_BY(element))
8214 TestIfGoodThingGetsHitByBadThing(newx, newy, direction);
8217 /* give the player one last chance (one more frame) to move away */
8218 if (CAN_FALL(element) && direction == MV_DOWN &&
8219 (last_line || (!IS_FREE(x, newy + 1) &&
8220 (!IS_PLAYER(x, newy + 1) ||
8221 game.engine_version < VERSION_IDENT(3,1,1,0)))))
8224 if (pushed_by_player && !game.use_change_when_pushing_bug)
8226 int push_side = MV_DIR_OPPOSITE(direction);
8227 struct PlayerInfo *player = PLAYERINFO(x, y);
8229 CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
8230 player->index_bit, push_side);
8231 CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
8232 player->index_bit, push_side);
8235 if (element == EL_EMC_ANDROID && pushed_by_player) /* make another move */
8236 MovDelay[newx][newy] = 1;
8238 CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
8240 TestIfElementTouchesCustomElement(x, y); /* empty or new element */
8241 TestIfElementHitsCustomElement(newx, newy, direction);
8242 TestIfPlayerTouchesCustomElement(newx, newy);
8243 TestIfElementTouchesCustomElement(newx, newy);
8245 if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
8246 IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
8247 CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
8248 MV_DIR_OPPOSITE(direction));
8251 int AmoebeNachbarNr(int ax, int ay)
8254 int element = Feld[ax][ay];
8256 static int xy[4][2] =
8264 for (i = 0; i < NUM_DIRECTIONS; i++)
8266 int x = ax + xy[i][0];
8267 int y = ay + xy[i][1];
8269 if (!IN_LEV_FIELD(x, y))
8272 if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
8273 group_nr = AmoebaNr[x][y];
8279 void AmoebenVereinigen(int ax, int ay)
8281 int i, x, y, xx, yy;
8282 int new_group_nr = AmoebaNr[ax][ay];
8283 static int xy[4][2] =
8291 if (new_group_nr == 0)
8294 for (i = 0; i < NUM_DIRECTIONS; i++)
8299 if (!IN_LEV_FIELD(x, y))
8302 if ((Feld[x][y] == EL_AMOEBA_FULL ||
8303 Feld[x][y] == EL_BD_AMOEBA ||
8304 Feld[x][y] == EL_AMOEBA_DEAD) &&
8305 AmoebaNr[x][y] != new_group_nr)
8307 int old_group_nr = AmoebaNr[x][y];
8309 if (old_group_nr == 0)
8312 AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
8313 AmoebaCnt[old_group_nr] = 0;
8314 AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
8315 AmoebaCnt2[old_group_nr] = 0;
8317 SCAN_PLAYFIELD(xx, yy)
8319 if (AmoebaNr[xx][yy] == old_group_nr)
8320 AmoebaNr[xx][yy] = new_group_nr;
8326 void AmoebeUmwandeln(int ax, int ay)
8330 if (Feld[ax][ay] == EL_AMOEBA_DEAD)
8332 int group_nr = AmoebaNr[ax][ay];
8337 printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
8338 printf("AmoebeUmwandeln(): This should never happen!\n");
8343 SCAN_PLAYFIELD(x, y)
8345 if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
8348 Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
8352 PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
8353 SND_AMOEBA_TURNING_TO_GEM :
8354 SND_AMOEBA_TURNING_TO_ROCK));
8359 static int xy[4][2] =
8367 for (i = 0; i < NUM_DIRECTIONS; i++)
8372 if (!IN_LEV_FIELD(x, y))
8375 if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
8377 PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
8378 SND_AMOEBA_TURNING_TO_GEM :
8379 SND_AMOEBA_TURNING_TO_ROCK));
8386 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
8389 int group_nr = AmoebaNr[ax][ay];
8390 boolean done = FALSE;
8395 printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
8396 printf("AmoebeUmwandelnBD(): This should never happen!\n");
8401 SCAN_PLAYFIELD(x, y)
8403 if (AmoebaNr[x][y] == group_nr &&
8404 (Feld[x][y] == EL_AMOEBA_DEAD ||
8405 Feld[x][y] == EL_BD_AMOEBA ||
8406 Feld[x][y] == EL_AMOEBA_GROWING))
8409 Feld[x][y] = new_element;
8410 InitField(x, y, FALSE);
8411 TEST_DrawLevelField(x, y);
8417 PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
8418 SND_BD_AMOEBA_TURNING_TO_ROCK :
8419 SND_BD_AMOEBA_TURNING_TO_GEM));
8422 void AmoebeWaechst(int x, int y)
8424 static unsigned int sound_delay = 0;
8425 static unsigned int sound_delay_value = 0;
8427 if (!MovDelay[x][y]) /* start new growing cycle */
8431 if (DelayReached(&sound_delay, sound_delay_value))
8433 PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
8434 sound_delay_value = 30;
8438 if (MovDelay[x][y]) /* wait some time before growing bigger */
8441 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8443 int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
8444 6 - MovDelay[x][y]);
8446 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
8449 if (!MovDelay[x][y])
8451 Feld[x][y] = Store[x][y];
8453 TEST_DrawLevelField(x, y);
8458 void AmoebaDisappearing(int x, int y)
8460 static unsigned int sound_delay = 0;
8461 static unsigned int sound_delay_value = 0;
8463 if (!MovDelay[x][y]) /* start new shrinking cycle */
8467 if (DelayReached(&sound_delay, sound_delay_value))
8468 sound_delay_value = 30;
8471 if (MovDelay[x][y]) /* wait some time before shrinking */
8474 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8476 int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
8477 6 - MovDelay[x][y]);
8479 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
8482 if (!MovDelay[x][y])
8484 Feld[x][y] = EL_EMPTY;
8485 TEST_DrawLevelField(x, y);
8487 /* don't let mole enter this field in this cycle;
8488 (give priority to objects falling to this field from above) */
8494 void AmoebeAbleger(int ax, int ay)
8497 int element = Feld[ax][ay];
8498 int graphic = el2img(element);
8499 int newax = ax, neway = ay;
8500 boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
8501 static int xy[4][2] =
8509 if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
8511 Feld[ax][ay] = EL_AMOEBA_DEAD;
8512 TEST_DrawLevelField(ax, ay);
8516 if (IS_ANIMATED(graphic))
8517 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
8519 if (!MovDelay[ax][ay]) /* start making new amoeba field */
8520 MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
8522 if (MovDelay[ax][ay]) /* wait some time before making new amoeba */
8525 if (MovDelay[ax][ay])
8529 if (can_drop) /* EL_AMOEBA_WET or EL_EMC_DRIPPER */
8532 int x = ax + xy[start][0];
8533 int y = ay + xy[start][1];
8535 if (!IN_LEV_FIELD(x, y))
8538 if (IS_FREE(x, y) ||
8539 CAN_GROW_INTO(Feld[x][y]) ||
8540 Feld[x][y] == EL_QUICKSAND_EMPTY ||
8541 Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
8547 if (newax == ax && neway == ay)
8550 else /* normal or "filled" (BD style) amoeba */
8553 boolean waiting_for_player = FALSE;
8555 for (i = 0; i < NUM_DIRECTIONS; i++)
8557 int j = (start + i) % 4;
8558 int x = ax + xy[j][0];
8559 int y = ay + xy[j][1];
8561 if (!IN_LEV_FIELD(x, y))
8564 if (IS_FREE(x, y) ||
8565 CAN_GROW_INTO(Feld[x][y]) ||
8566 Feld[x][y] == EL_QUICKSAND_EMPTY ||
8567 Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
8573 else if (IS_PLAYER(x, y))
8574 waiting_for_player = TRUE;
8577 if (newax == ax && neway == ay) /* amoeba cannot grow */
8579 if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
8581 Feld[ax][ay] = EL_AMOEBA_DEAD;
8582 TEST_DrawLevelField(ax, ay);
8583 AmoebaCnt[AmoebaNr[ax][ay]]--;
8585 if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0) /* amoeba is completely dead */
8587 if (element == EL_AMOEBA_FULL)
8588 AmoebeUmwandeln(ax, ay);
8589 else if (element == EL_BD_AMOEBA)
8590 AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
8595 else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
8597 /* amoeba gets larger by growing in some direction */
8599 int new_group_nr = AmoebaNr[ax][ay];
8602 if (new_group_nr == 0)
8604 printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
8605 printf("AmoebeAbleger(): This should never happen!\n");
8610 AmoebaNr[newax][neway] = new_group_nr;
8611 AmoebaCnt[new_group_nr]++;
8612 AmoebaCnt2[new_group_nr]++;
8614 /* if amoeba touches other amoeba(s) after growing, unify them */
8615 AmoebenVereinigen(newax, neway);
8617 if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
8619 AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
8625 if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
8626 (neway == lev_fieldy - 1 && newax != ax))
8628 Feld[newax][neway] = EL_AMOEBA_GROWING; /* creation of new amoeba */
8629 Store[newax][neway] = element;
8631 else if (neway == ay || element == EL_EMC_DRIPPER)
8633 Feld[newax][neway] = EL_AMOEBA_DROP; /* drop left/right of amoeba */
8635 PlayLevelSoundAction(newax, neway, ACTION_GROWING);
8639 InitMovingField(ax, ay, MV_DOWN); /* drop dripping from amoeba */
8640 Feld[ax][ay] = EL_AMOEBA_DROPPING;
8641 Store[ax][ay] = EL_AMOEBA_DROP;
8642 ContinueMoving(ax, ay);
8646 TEST_DrawLevelField(newax, neway);
8649 void Life(int ax, int ay)
8653 int element = Feld[ax][ay];
8654 int graphic = el2img(element);
8655 int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
8657 boolean changed = FALSE;
8659 if (IS_ANIMATED(graphic))
8660 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
8665 if (!MovDelay[ax][ay]) /* start new "game of life" cycle */
8666 MovDelay[ax][ay] = life_time;
8668 if (MovDelay[ax][ay]) /* wait some time before next cycle */
8671 if (MovDelay[ax][ay])
8675 for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
8677 int xx = ax+x1, yy = ay+y1;
8680 if (!IN_LEV_FIELD(xx, yy))
8683 for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
8685 int x = xx+x2, y = yy+y2;
8687 if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
8690 if (((Feld[x][y] == element ||
8691 (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
8693 (IS_FREE(x, y) && Stop[x][y]))
8697 if (xx == ax && yy == ay) /* field in the middle */
8699 if (nachbarn < life_parameter[0] ||
8700 nachbarn > life_parameter[1])
8702 Feld[xx][yy] = EL_EMPTY;
8704 TEST_DrawLevelField(xx, yy);
8705 Stop[xx][yy] = TRUE;
8709 else if (IS_FREE(xx, yy) || CAN_GROW_INTO(Feld[xx][yy]))
8710 { /* free border field */
8711 if (nachbarn >= life_parameter[2] &&
8712 nachbarn <= life_parameter[3])
8714 Feld[xx][yy] = element;
8715 MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
8717 TEST_DrawLevelField(xx, yy);
8718 Stop[xx][yy] = TRUE;
8725 PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
8726 SND_GAME_OF_LIFE_GROWING);
8729 static void InitRobotWheel(int x, int y)
8731 ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
8734 static void RunRobotWheel(int x, int y)
8736 PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
8739 static void StopRobotWheel(int x, int y)
8741 if (ZX == x && ZY == y)
8745 game.robot_wheel_active = FALSE;
8749 static void InitTimegateWheel(int x, int y)
8751 ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
8754 static void RunTimegateWheel(int x, int y)
8756 PlayLevelSound(x, y, SND_CLASS_TIMEGATE_SWITCH_ACTIVE);
8759 static void InitMagicBallDelay(int x, int y)
8761 ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
8764 static void ActivateMagicBall(int bx, int by)
8768 if (level.ball_random)
8770 int pos_border = RND(8); /* select one of the eight border elements */
8771 int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
8772 int xx = pos_content % 3;
8773 int yy = pos_content / 3;
8778 if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
8779 CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
8783 for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
8785 int xx = x - bx + 1;
8786 int yy = y - by + 1;
8788 if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
8789 CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
8793 game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
8796 void CheckExit(int x, int y)
8798 if (local_player->gems_still_needed > 0 ||
8799 local_player->sokobanfields_still_needed > 0 ||
8800 local_player->lights_still_needed > 0)
8802 int element = Feld[x][y];
8803 int graphic = el2img(element);
8805 if (IS_ANIMATED(graphic))
8806 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8811 if (AllPlayersGone) /* do not re-open exit door closed after last player */
8814 Feld[x][y] = EL_EXIT_OPENING;
8816 PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
8819 void CheckExitEM(int x, int y)
8821 if (local_player->gems_still_needed > 0 ||
8822 local_player->sokobanfields_still_needed > 0 ||
8823 local_player->lights_still_needed > 0)
8825 int element = Feld[x][y];
8826 int graphic = el2img(element);
8828 if (IS_ANIMATED(graphic))
8829 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8834 if (AllPlayersGone) /* do not re-open exit door closed after last player */
8837 Feld[x][y] = EL_EM_EXIT_OPENING;
8839 PlayLevelSoundNearest(x, y, SND_CLASS_EM_EXIT_OPENING);
8842 void CheckExitSteel(int x, int y)
8844 if (local_player->gems_still_needed > 0 ||
8845 local_player->sokobanfields_still_needed > 0 ||
8846 local_player->lights_still_needed > 0)
8848 int element = Feld[x][y];
8849 int graphic = el2img(element);
8851 if (IS_ANIMATED(graphic))
8852 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8857 if (AllPlayersGone) /* do not re-open exit door closed after last player */
8860 Feld[x][y] = EL_STEEL_EXIT_OPENING;
8862 PlayLevelSoundNearest(x, y, SND_CLASS_STEEL_EXIT_OPENING);
8865 void CheckExitSteelEM(int x, int y)
8867 if (local_player->gems_still_needed > 0 ||
8868 local_player->sokobanfields_still_needed > 0 ||
8869 local_player->lights_still_needed > 0)
8871 int element = Feld[x][y];
8872 int graphic = el2img(element);
8874 if (IS_ANIMATED(graphic))
8875 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8880 if (AllPlayersGone) /* do not re-open exit door closed after last player */
8883 Feld[x][y] = EL_EM_STEEL_EXIT_OPENING;
8885 PlayLevelSoundNearest(x, y, SND_CLASS_EM_STEEL_EXIT_OPENING);
8888 void CheckExitSP(int x, int y)
8890 if (local_player->gems_still_needed > 0)
8892 int element = Feld[x][y];
8893 int graphic = el2img(element);
8895 if (IS_ANIMATED(graphic))
8896 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8901 if (AllPlayersGone) /* do not re-open exit door closed after last player */
8904 Feld[x][y] = EL_SP_EXIT_OPENING;
8906 PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
8909 static void CloseAllOpenTimegates()
8913 SCAN_PLAYFIELD(x, y)
8915 int element = Feld[x][y];
8917 if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
8919 Feld[x][y] = EL_TIMEGATE_CLOSING;
8921 PlayLevelSoundAction(x, y, ACTION_CLOSING);
8926 void DrawTwinkleOnField(int x, int y)
8928 if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
8931 if (Feld[x][y] == EL_BD_DIAMOND)
8934 if (MovDelay[x][y] == 0) /* next animation frame */
8935 MovDelay[x][y] = 11 * !GetSimpleRandom(500);
8937 if (MovDelay[x][y] != 0) /* wait some time before next frame */
8941 DrawLevelElementAnimation(x, y, Feld[x][y]);
8943 if (MovDelay[x][y] != 0)
8945 int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
8946 10 - MovDelay[x][y]);
8948 DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
8953 void MauerWaechst(int x, int y)
8957 if (!MovDelay[x][y]) /* next animation frame */
8958 MovDelay[x][y] = 3 * delay;
8960 if (MovDelay[x][y]) /* wait some time before next frame */
8964 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8966 int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
8967 int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
8969 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
8972 if (!MovDelay[x][y])
8974 if (MovDir[x][y] == MV_LEFT)
8976 if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
8977 TEST_DrawLevelField(x - 1, y);
8979 else if (MovDir[x][y] == MV_RIGHT)
8981 if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
8982 TEST_DrawLevelField(x + 1, y);
8984 else if (MovDir[x][y] == MV_UP)
8986 if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
8987 TEST_DrawLevelField(x, y - 1);
8991 if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
8992 TEST_DrawLevelField(x, y + 1);
8995 Feld[x][y] = Store[x][y];
8997 GfxDir[x][y] = MovDir[x][y] = MV_NONE;
8998 TEST_DrawLevelField(x, y);
9003 void MauerAbleger(int ax, int ay)
9005 int element = Feld[ax][ay];
9006 int graphic = el2img(element);
9007 boolean oben_frei = FALSE, unten_frei = FALSE;
9008 boolean links_frei = FALSE, rechts_frei = FALSE;
9009 boolean oben_massiv = FALSE, unten_massiv = FALSE;
9010 boolean links_massiv = FALSE, rechts_massiv = FALSE;
9011 boolean new_wall = FALSE;
9013 if (IS_ANIMATED(graphic))
9014 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9016 if (!MovDelay[ax][ay]) /* start building new wall */
9017 MovDelay[ax][ay] = 6;
9019 if (MovDelay[ax][ay]) /* wait some time before building new wall */
9022 if (MovDelay[ax][ay])
9026 if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9028 if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9030 if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9032 if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9035 if (element == EL_EXPANDABLE_WALL_VERTICAL ||
9036 element == EL_EXPANDABLE_WALL_ANY)
9040 Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
9041 Store[ax][ay-1] = element;
9042 GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9043 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9044 DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9045 IMG_EXPANDABLE_WALL_GROWING_UP, 0);
9050 Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
9051 Store[ax][ay+1] = element;
9052 GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9053 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9054 DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9055 IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
9060 if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9061 element == EL_EXPANDABLE_WALL_ANY ||
9062 element == EL_EXPANDABLE_WALL ||
9063 element == EL_BD_EXPANDABLE_WALL)
9067 Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
9068 Store[ax-1][ay] = element;
9069 GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9070 if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9071 DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9072 IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
9078 Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
9079 Store[ax+1][ay] = element;
9080 GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9081 if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9082 DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9083 IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
9088 if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
9089 TEST_DrawLevelField(ax, ay);
9091 if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
9093 if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
9094 unten_massiv = TRUE;
9095 if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
9096 links_massiv = TRUE;
9097 if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
9098 rechts_massiv = TRUE;
9100 if (((oben_massiv && unten_massiv) ||
9101 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9102 element == EL_EXPANDABLE_WALL) &&
9103 ((links_massiv && rechts_massiv) ||
9104 element == EL_EXPANDABLE_WALL_VERTICAL))
9105 Feld[ax][ay] = EL_WALL;
9108 PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9111 void MauerAblegerStahl(int ax, int ay)
9113 int element = Feld[ax][ay];
9114 int graphic = el2img(element);
9115 boolean oben_frei = FALSE, unten_frei = FALSE;
9116 boolean links_frei = FALSE, rechts_frei = FALSE;
9117 boolean oben_massiv = FALSE, unten_massiv = FALSE;
9118 boolean links_massiv = FALSE, rechts_massiv = FALSE;
9119 boolean new_wall = FALSE;
9121 if (IS_ANIMATED(graphic))
9122 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9124 if (!MovDelay[ax][ay]) /* start building new wall */
9125 MovDelay[ax][ay] = 6;
9127 if (MovDelay[ax][ay]) /* wait some time before building new wall */
9130 if (MovDelay[ax][ay])
9134 if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9136 if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9138 if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9140 if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9143 if (element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
9144 element == EL_EXPANDABLE_STEELWALL_ANY)
9148 Feld[ax][ay-1] = EL_EXPANDABLE_STEELWALL_GROWING;
9149 Store[ax][ay-1] = element;
9150 GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9151 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9152 DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9153 IMG_EXPANDABLE_STEELWALL_GROWING_UP, 0);
9158 Feld[ax][ay+1] = EL_EXPANDABLE_STEELWALL_GROWING;
9159 Store[ax][ay+1] = element;
9160 GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9161 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9162 DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9163 IMG_EXPANDABLE_STEELWALL_GROWING_DOWN, 0);
9168 if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
9169 element == EL_EXPANDABLE_STEELWALL_ANY)
9173 Feld[ax-1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9174 Store[ax-1][ay] = element;
9175 GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9176 if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9177 DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9178 IMG_EXPANDABLE_STEELWALL_GROWING_LEFT, 0);
9184 Feld[ax+1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9185 Store[ax+1][ay] = element;
9186 GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9187 if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9188 DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9189 IMG_EXPANDABLE_STEELWALL_GROWING_RIGHT, 0);
9194 if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
9196 if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
9197 unten_massiv = TRUE;
9198 if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
9199 links_massiv = TRUE;
9200 if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
9201 rechts_massiv = TRUE;
9203 if (((oben_massiv && unten_massiv) ||
9204 element == EL_EXPANDABLE_STEELWALL_HORIZONTAL) &&
9205 ((links_massiv && rechts_massiv) ||
9206 element == EL_EXPANDABLE_STEELWALL_VERTICAL))
9207 Feld[ax][ay] = EL_STEELWALL;
9210 PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9213 void CheckForDragon(int x, int y)
9216 boolean dragon_found = FALSE;
9217 static int xy[4][2] =
9225 for (i = 0; i < NUM_DIRECTIONS; i++)
9227 for (j = 0; j < 4; j++)
9229 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9231 if (IN_LEV_FIELD(xx, yy) &&
9232 (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
9234 if (Feld[xx][yy] == EL_DRAGON)
9235 dragon_found = TRUE;
9244 for (i = 0; i < NUM_DIRECTIONS; i++)
9246 for (j = 0; j < 3; j++)
9248 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9250 if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
9252 Feld[xx][yy] = EL_EMPTY;
9253 TEST_DrawLevelField(xx, yy);
9262 static void InitBuggyBase(int x, int y)
9264 int element = Feld[x][y];
9265 int activating_delay = FRAMES_PER_SECOND / 4;
9268 (element == EL_SP_BUGGY_BASE ?
9269 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
9270 element == EL_SP_BUGGY_BASE_ACTIVATING ?
9272 element == EL_SP_BUGGY_BASE_ACTIVE ?
9273 1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
9276 static void WarnBuggyBase(int x, int y)
9279 static int xy[4][2] =
9287 for (i = 0; i < NUM_DIRECTIONS; i++)
9289 int xx = x + xy[i][0];
9290 int yy = y + xy[i][1];
9292 if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
9294 PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
9301 static void InitTrap(int x, int y)
9303 ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
9306 static void ActivateTrap(int x, int y)
9308 PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
9311 static void ChangeActiveTrap(int x, int y)
9313 int graphic = IMG_TRAP_ACTIVE;
9315 /* if new animation frame was drawn, correct crumbled sand border */
9316 if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
9317 TEST_DrawLevelFieldCrumbled(x, y);
9320 static int getSpecialActionElement(int element, int number, int base_element)
9322 return (element != EL_EMPTY ? element :
9323 number != -1 ? base_element + number - 1 :
9327 static int getModifiedActionNumber(int value_old, int operator, int operand,
9328 int value_min, int value_max)
9330 int value_new = (operator == CA_MODE_SET ? operand :
9331 operator == CA_MODE_ADD ? value_old + operand :
9332 operator == CA_MODE_SUBTRACT ? value_old - operand :
9333 operator == CA_MODE_MULTIPLY ? value_old * operand :
9334 operator == CA_MODE_DIVIDE ? value_old / MAX(1, operand) :
9335 operator == CA_MODE_MODULO ? value_old % MAX(1, operand) :
9338 return (value_new < value_min ? value_min :
9339 value_new > value_max ? value_max :
9343 static void ExecuteCustomElementAction(int x, int y, int element, int page)
9345 struct ElementInfo *ei = &element_info[element];
9346 struct ElementChangeInfo *change = &ei->change_page[page];
9347 int target_element = change->target_element;
9348 int action_type = change->action_type;
9349 int action_mode = change->action_mode;
9350 int action_arg = change->action_arg;
9351 int action_element = change->action_element;
9354 if (!change->has_action)
9357 /* ---------- determine action paramater values -------------------------- */
9359 int level_time_value =
9360 (level.time > 0 ? TimeLeft :
9363 int action_arg_element_raw =
9364 (action_arg == CA_ARG_PLAYER_TRIGGER ? change->actual_trigger_player :
9365 action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
9366 action_arg == CA_ARG_ELEMENT_TARGET ? change->target_element :
9367 action_arg == CA_ARG_ELEMENT_ACTION ? change->action_element :
9368 action_arg == CA_ARG_INVENTORY_RM_TRIGGER ? change->actual_trigger_element:
9369 action_arg == CA_ARG_INVENTORY_RM_TARGET ? change->target_element :
9370 action_arg == CA_ARG_INVENTORY_RM_ACTION ? change->action_element :
9372 int action_arg_element = GetElementFromGroupElement(action_arg_element_raw);
9374 int action_arg_direction =
9375 (action_arg >= CA_ARG_DIRECTION_LEFT &&
9376 action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
9377 action_arg == CA_ARG_DIRECTION_TRIGGER ?
9378 change->actual_trigger_side :
9379 action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
9380 MV_DIR_OPPOSITE(change->actual_trigger_side) :
9383 int action_arg_number_min =
9384 (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
9387 int action_arg_number_max =
9388 (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
9389 action_type == CA_SET_LEVEL_GEMS ? 999 :
9390 action_type == CA_SET_LEVEL_TIME ? 9999 :
9391 action_type == CA_SET_LEVEL_SCORE ? 99999 :
9392 action_type == CA_SET_CE_VALUE ? 9999 :
9393 action_type == CA_SET_CE_SCORE ? 9999 :
9396 int action_arg_number_reset =
9397 (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize[0] :
9398 action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
9399 action_type == CA_SET_LEVEL_TIME ? level.time :
9400 action_type == CA_SET_LEVEL_SCORE ? 0 :
9401 action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
9402 action_type == CA_SET_CE_SCORE ? 0 :
9405 int action_arg_number =
9406 (action_arg <= CA_ARG_MAX ? action_arg :
9407 action_arg >= CA_ARG_SPEED_NOT_MOVING &&
9408 action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
9409 action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
9410 action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
9411 action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
9412 action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
9413 action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
9414 action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
9415 action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
9416 action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
9417 action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? local_player->gems_still_needed :
9418 action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? local_player->score :
9419 action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
9420 action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
9421 action_arg == CA_ARG_ELEMENT_CV_ACTION ? GET_NEW_CE_VALUE(action_element):
9422 action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
9423 action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
9424 action_arg == CA_ARG_ELEMENT_CS_ACTION ? GET_CE_SCORE(action_element) :
9425 action_arg == CA_ARG_ELEMENT_NR_TARGET ? change->target_element :
9426 action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
9427 action_arg == CA_ARG_ELEMENT_NR_ACTION ? change->action_element :
9430 int action_arg_number_old =
9431 (action_type == CA_SET_LEVEL_GEMS ? local_player->gems_still_needed :
9432 action_type == CA_SET_LEVEL_TIME ? TimeLeft :
9433 action_type == CA_SET_LEVEL_SCORE ? local_player->score :
9434 action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
9435 action_type == CA_SET_CE_SCORE ? ei->collect_score :
9438 int action_arg_number_new =
9439 getModifiedActionNumber(action_arg_number_old,
9440 action_mode, action_arg_number,
9441 action_arg_number_min, action_arg_number_max);
9443 int trigger_player_bits =
9444 (change->actual_trigger_player_bits != CH_PLAYER_NONE ?
9445 change->actual_trigger_player_bits : change->trigger_player);
9447 int action_arg_player_bits =
9448 (action_arg >= CA_ARG_PLAYER_1 &&
9449 action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
9450 action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
9451 action_arg == CA_ARG_PLAYER_ACTION ? 1 << GET_PLAYER_NR(action_element) :
9454 /* ---------- execute action -------------------------------------------- */
9456 switch (action_type)
9463 /* ---------- level actions ------------------------------------------- */
9465 case CA_RESTART_LEVEL:
9467 game.restart_level = TRUE;
9472 case CA_SHOW_ENVELOPE:
9474 int element = getSpecialActionElement(action_arg_element,
9475 action_arg_number, EL_ENVELOPE_1);
9477 if (IS_ENVELOPE(element))
9478 local_player->show_envelope = element;
9483 case CA_SET_LEVEL_TIME:
9485 if (level.time > 0) /* only modify limited time value */
9487 TimeLeft = action_arg_number_new;
9489 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
9491 DisplayGameControlValues();
9493 if (!TimeLeft && setup.time_limit)
9494 for (i = 0; i < MAX_PLAYERS; i++)
9495 KillPlayer(&stored_player[i]);
9501 case CA_SET_LEVEL_SCORE:
9503 local_player->score = action_arg_number_new;
9505 game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
9507 DisplayGameControlValues();
9512 case CA_SET_LEVEL_GEMS:
9514 local_player->gems_still_needed = action_arg_number_new;
9516 game.snapshot.collected_item = TRUE;
9518 game_panel_controls[GAME_PANEL_GEMS].value =
9519 local_player->gems_still_needed;
9521 DisplayGameControlValues();
9526 case CA_SET_LEVEL_WIND:
9528 game.wind_direction = action_arg_direction;
9533 case CA_SET_LEVEL_RANDOM_SEED:
9535 /* ensure that setting a new random seed while playing is predictable */
9536 InitRND(action_arg_number_new ? action_arg_number_new : RND(1000000) + 1);
9541 /* ---------- player actions ------------------------------------------ */
9543 case CA_MOVE_PLAYER:
9545 /* automatically move to the next field in specified direction */
9546 for (i = 0; i < MAX_PLAYERS; i++)
9547 if (trigger_player_bits & (1 << i))
9548 stored_player[i].programmed_action = action_arg_direction;
9553 case CA_EXIT_PLAYER:
9555 for (i = 0; i < MAX_PLAYERS; i++)
9556 if (action_arg_player_bits & (1 << i))
9557 PlayerWins(&stored_player[i]);
9562 case CA_KILL_PLAYER:
9564 for (i = 0; i < MAX_PLAYERS; i++)
9565 if (action_arg_player_bits & (1 << i))
9566 KillPlayer(&stored_player[i]);
9571 case CA_SET_PLAYER_KEYS:
9573 int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
9574 int element = getSpecialActionElement(action_arg_element,
9575 action_arg_number, EL_KEY_1);
9577 if (IS_KEY(element))
9579 for (i = 0; i < MAX_PLAYERS; i++)
9581 if (trigger_player_bits & (1 << i))
9583 stored_player[i].key[KEY_NR(element)] = key_state;
9585 DrawGameDoorValues();
9593 case CA_SET_PLAYER_SPEED:
9595 for (i = 0; i < MAX_PLAYERS; i++)
9597 if (trigger_player_bits & (1 << i))
9599 int move_stepsize = TILEX / stored_player[i].move_delay_value;
9601 if (action_arg == CA_ARG_SPEED_FASTER &&
9602 stored_player[i].cannot_move)
9604 action_arg_number = STEPSIZE_VERY_SLOW;
9606 else if (action_arg == CA_ARG_SPEED_SLOWER ||
9607 action_arg == CA_ARG_SPEED_FASTER)
9609 action_arg_number = 2;
9610 action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
9613 else if (action_arg == CA_ARG_NUMBER_RESET)
9615 action_arg_number = level.initial_player_stepsize[i];
9619 getModifiedActionNumber(move_stepsize,
9622 action_arg_number_min,
9623 action_arg_number_max);
9625 SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
9632 case CA_SET_PLAYER_SHIELD:
9634 for (i = 0; i < MAX_PLAYERS; i++)
9636 if (trigger_player_bits & (1 << i))
9638 if (action_arg == CA_ARG_SHIELD_OFF)
9640 stored_player[i].shield_normal_time_left = 0;
9641 stored_player[i].shield_deadly_time_left = 0;
9643 else if (action_arg == CA_ARG_SHIELD_NORMAL)
9645 stored_player[i].shield_normal_time_left = 999999;
9647 else if (action_arg == CA_ARG_SHIELD_DEADLY)
9649 stored_player[i].shield_normal_time_left = 999999;
9650 stored_player[i].shield_deadly_time_left = 999999;
9658 case CA_SET_PLAYER_GRAVITY:
9660 for (i = 0; i < MAX_PLAYERS; i++)
9662 if (trigger_player_bits & (1 << i))
9664 stored_player[i].gravity =
9665 (action_arg == CA_ARG_GRAVITY_OFF ? FALSE :
9666 action_arg == CA_ARG_GRAVITY_ON ? TRUE :
9667 action_arg == CA_ARG_GRAVITY_TOGGLE ? !stored_player[i].gravity :
9668 stored_player[i].gravity);
9675 case CA_SET_PLAYER_ARTWORK:
9677 for (i = 0; i < MAX_PLAYERS; i++)
9679 if (trigger_player_bits & (1 << i))
9681 int artwork_element = action_arg_element;
9683 if (action_arg == CA_ARG_ELEMENT_RESET)
9685 (level.use_artwork_element[i] ? level.artwork_element[i] :
9686 stored_player[i].element_nr);
9688 if (stored_player[i].artwork_element != artwork_element)
9689 stored_player[i].Frame = 0;
9691 stored_player[i].artwork_element = artwork_element;
9693 SetPlayerWaiting(&stored_player[i], FALSE);
9695 /* set number of special actions for bored and sleeping animation */
9696 stored_player[i].num_special_action_bored =
9697 get_num_special_action(artwork_element,
9698 ACTION_BORING_1, ACTION_BORING_LAST);
9699 stored_player[i].num_special_action_sleeping =
9700 get_num_special_action(artwork_element,
9701 ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
9708 case CA_SET_PLAYER_INVENTORY:
9710 for (i = 0; i < MAX_PLAYERS; i++)
9712 struct PlayerInfo *player = &stored_player[i];
9715 if (trigger_player_bits & (1 << i))
9717 int inventory_element = action_arg_element;
9719 if (action_arg == CA_ARG_ELEMENT_TARGET ||
9720 action_arg == CA_ARG_ELEMENT_TRIGGER ||
9721 action_arg == CA_ARG_ELEMENT_ACTION)
9723 int element = inventory_element;
9724 int collect_count = element_info[element].collect_count_initial;
9726 if (!IS_CUSTOM_ELEMENT(element))
9729 if (collect_count == 0)
9730 player->inventory_infinite_element = element;
9732 for (k = 0; k < collect_count; k++)
9733 if (player->inventory_size < MAX_INVENTORY_SIZE)
9734 player->inventory_element[player->inventory_size++] =
9737 else if (action_arg == CA_ARG_INVENTORY_RM_TARGET ||
9738 action_arg == CA_ARG_INVENTORY_RM_TRIGGER ||
9739 action_arg == CA_ARG_INVENTORY_RM_ACTION)
9741 if (player->inventory_infinite_element != EL_UNDEFINED &&
9742 IS_EQUAL_OR_IN_GROUP(player->inventory_infinite_element,
9743 action_arg_element_raw))
9744 player->inventory_infinite_element = EL_UNDEFINED;
9746 for (k = 0, j = 0; j < player->inventory_size; j++)
9748 if (!IS_EQUAL_OR_IN_GROUP(player->inventory_element[j],
9749 action_arg_element_raw))
9750 player->inventory_element[k++] = player->inventory_element[j];
9753 player->inventory_size = k;
9755 else if (action_arg == CA_ARG_INVENTORY_RM_FIRST)
9757 if (player->inventory_size > 0)
9759 for (j = 0; j < player->inventory_size - 1; j++)
9760 player->inventory_element[j] = player->inventory_element[j + 1];
9762 player->inventory_size--;
9765 else if (action_arg == CA_ARG_INVENTORY_RM_LAST)
9767 if (player->inventory_size > 0)
9768 player->inventory_size--;
9770 else if (action_arg == CA_ARG_INVENTORY_RM_ALL)
9772 player->inventory_infinite_element = EL_UNDEFINED;
9773 player->inventory_size = 0;
9775 else if (action_arg == CA_ARG_INVENTORY_RESET)
9777 player->inventory_infinite_element = EL_UNDEFINED;
9778 player->inventory_size = 0;
9780 if (level.use_initial_inventory[i])
9782 for (j = 0; j < level.initial_inventory_size[i]; j++)
9784 int element = level.initial_inventory_content[i][j];
9785 int collect_count = element_info[element].collect_count_initial;
9787 if (!IS_CUSTOM_ELEMENT(element))
9790 if (collect_count == 0)
9791 player->inventory_infinite_element = element;
9793 for (k = 0; k < collect_count; k++)
9794 if (player->inventory_size < MAX_INVENTORY_SIZE)
9795 player->inventory_element[player->inventory_size++] =
9806 /* ---------- CE actions ---------------------------------------------- */
9808 case CA_SET_CE_VALUE:
9810 int last_ce_value = CustomValue[x][y];
9812 CustomValue[x][y] = action_arg_number_new;
9814 if (CustomValue[x][y] != last_ce_value)
9816 CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_CHANGES);
9817 CheckTriggeredElementChange(x, y, element, CE_VALUE_CHANGES_OF_X);
9819 if (CustomValue[x][y] == 0)
9821 CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
9822 CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
9829 case CA_SET_CE_SCORE:
9831 int last_ce_score = ei->collect_score;
9833 ei->collect_score = action_arg_number_new;
9835 if (ei->collect_score != last_ce_score)
9837 CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
9838 CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
9840 if (ei->collect_score == 0)
9844 CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
9845 CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
9848 This is a very special case that seems to be a mixture between
9849 CheckElementChange() and CheckTriggeredElementChange(): while
9850 the first one only affects single elements that are triggered
9851 directly, the second one affects multiple elements in the playfield
9852 that are triggered indirectly by another element. This is a third
9853 case: Changing the CE score always affects multiple identical CEs,
9854 so every affected CE must be checked, not only the single CE for
9855 which the CE score was changed in the first place (as every instance
9856 of that CE shares the same CE score, and therefore also can change)!
9858 SCAN_PLAYFIELD(xx, yy)
9860 if (Feld[xx][yy] == element)
9861 CheckElementChange(xx, yy, element, EL_UNDEFINED,
9862 CE_SCORE_GETS_ZERO);
9870 case CA_SET_CE_ARTWORK:
9872 int artwork_element = action_arg_element;
9873 boolean reset_frame = FALSE;
9876 if (action_arg == CA_ARG_ELEMENT_RESET)
9877 artwork_element = (ei->use_gfx_element ? ei->gfx_element_initial :
9880 if (ei->gfx_element != artwork_element)
9883 ei->gfx_element = artwork_element;
9885 SCAN_PLAYFIELD(xx, yy)
9887 if (Feld[xx][yy] == element)
9891 ResetGfxAnimation(xx, yy);
9892 ResetRandomAnimationValue(xx, yy);
9895 TEST_DrawLevelField(xx, yy);
9902 /* ---------- engine actions ------------------------------------------ */
9904 case CA_SET_ENGINE_SCAN_MODE:
9906 InitPlayfieldScanMode(action_arg);
9916 static void CreateFieldExt(int x, int y, int element, boolean is_change)
9918 int old_element = Feld[x][y];
9919 int new_element = GetElementFromGroupElement(element);
9920 int previous_move_direction = MovDir[x][y];
9921 int last_ce_value = CustomValue[x][y];
9922 boolean player_explosion_protected = PLAYER_EXPLOSION_PROTECTED(x, y);
9923 boolean new_element_is_player = ELEM_IS_PLAYER(new_element);
9924 boolean add_player_onto_element = (new_element_is_player &&
9925 new_element != EL_SOKOBAN_FIELD_PLAYER &&
9926 IS_WALKABLE(old_element));
9928 if (!add_player_onto_element)
9930 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
9931 RemoveMovingField(x, y);
9935 Feld[x][y] = new_element;
9937 if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
9938 MovDir[x][y] = previous_move_direction;
9940 if (element_info[new_element].use_last_ce_value)
9941 CustomValue[x][y] = last_ce_value;
9943 InitField_WithBug1(x, y, FALSE);
9945 new_element = Feld[x][y]; /* element may have changed */
9947 ResetGfxAnimation(x, y);
9948 ResetRandomAnimationValue(x, y);
9950 TEST_DrawLevelField(x, y);
9952 if (GFX_CRUMBLED(new_element))
9953 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
9956 /* check if element under the player changes from accessible to unaccessible
9957 (needed for special case of dropping element which then changes) */
9958 /* (must be checked after creating new element for walkable group elements) */
9959 if (IS_PLAYER(x, y) && !player_explosion_protected &&
9960 IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
9967 /* "ChangeCount" not set yet to allow "entered by player" change one time */
9968 if (new_element_is_player)
9969 RelocatePlayer(x, y, new_element);
9972 ChangeCount[x][y]++; /* count number of changes in the same frame */
9974 TestIfBadThingTouchesPlayer(x, y);
9975 TestIfPlayerTouchesCustomElement(x, y);
9976 TestIfElementTouchesCustomElement(x, y);
9979 static void CreateField(int x, int y, int element)
9981 CreateFieldExt(x, y, element, FALSE);
9984 static void CreateElementFromChange(int x, int y, int element)
9986 element = GET_VALID_RUNTIME_ELEMENT(element);
9988 if (game.engine_version >= VERSION_IDENT(3,2,0,7))
9990 int old_element = Feld[x][y];
9992 /* prevent changed element from moving in same engine frame
9993 unless both old and new element can either fall or move */
9994 if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
9995 (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
9999 CreateFieldExt(x, y, element, TRUE);
10002 static boolean ChangeElement(int x, int y, int element, int page)
10004 struct ElementInfo *ei = &element_info[element];
10005 struct ElementChangeInfo *change = &ei->change_page[page];
10006 int ce_value = CustomValue[x][y];
10007 int ce_score = ei->collect_score;
10008 int target_element;
10009 int old_element = Feld[x][y];
10011 /* always use default change event to prevent running into a loop */
10012 if (ChangeEvent[x][y] == -1)
10013 ChangeEvent[x][y] = CE_DELAY;
10015 if (ChangeEvent[x][y] == CE_DELAY)
10017 /* reset actual trigger element, trigger player and action element */
10018 change->actual_trigger_element = EL_EMPTY;
10019 change->actual_trigger_player = EL_EMPTY;
10020 change->actual_trigger_player_bits = CH_PLAYER_NONE;
10021 change->actual_trigger_side = CH_SIDE_NONE;
10022 change->actual_trigger_ce_value = 0;
10023 change->actual_trigger_ce_score = 0;
10026 /* do not change elements more than a specified maximum number of changes */
10027 if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
10030 ChangeCount[x][y]++; /* count number of changes in the same frame */
10032 if (change->explode)
10039 if (change->use_target_content)
10041 boolean complete_replace = TRUE;
10042 boolean can_replace[3][3];
10045 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10048 boolean is_walkable;
10049 boolean is_diggable;
10050 boolean is_collectible;
10051 boolean is_removable;
10052 boolean is_destructible;
10053 int ex = x + xx - 1;
10054 int ey = y + yy - 1;
10055 int content_element = change->target_content.e[xx][yy];
10058 can_replace[xx][yy] = TRUE;
10060 if (ex == x && ey == y) /* do not check changing element itself */
10063 if (content_element == EL_EMPTY_SPACE)
10065 can_replace[xx][yy] = FALSE; /* do not replace border with space */
10070 if (!IN_LEV_FIELD(ex, ey))
10072 can_replace[xx][yy] = FALSE;
10073 complete_replace = FALSE;
10080 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10081 e = MovingOrBlocked2Element(ex, ey);
10083 is_empty = (IS_FREE(ex, ey) ||
10084 (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
10086 is_walkable = (is_empty || IS_WALKABLE(e));
10087 is_diggable = (is_empty || IS_DIGGABLE(e));
10088 is_collectible = (is_empty || IS_COLLECTIBLE(e));
10089 is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
10090 is_removable = (is_diggable || is_collectible);
10092 can_replace[xx][yy] =
10093 (((change->replace_when == CP_WHEN_EMPTY && is_empty) ||
10094 (change->replace_when == CP_WHEN_WALKABLE && is_walkable) ||
10095 (change->replace_when == CP_WHEN_DIGGABLE && is_diggable) ||
10096 (change->replace_when == CP_WHEN_COLLECTIBLE && is_collectible) ||
10097 (change->replace_when == CP_WHEN_REMOVABLE && is_removable) ||
10098 (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
10099 !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
10101 if (!can_replace[xx][yy])
10102 complete_replace = FALSE;
10105 if (!change->only_if_complete || complete_replace)
10107 boolean something_has_changed = FALSE;
10109 if (change->only_if_complete && change->use_random_replace &&
10110 RND(100) < change->random_percentage)
10113 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10115 int ex = x + xx - 1;
10116 int ey = y + yy - 1;
10117 int content_element;
10119 if (can_replace[xx][yy] && (!change->use_random_replace ||
10120 RND(100) < change->random_percentage))
10122 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10123 RemoveMovingField(ex, ey);
10125 ChangeEvent[ex][ey] = ChangeEvent[x][y];
10127 content_element = change->target_content.e[xx][yy];
10128 target_element = GET_TARGET_ELEMENT(element, content_element, change,
10129 ce_value, ce_score);
10131 CreateElementFromChange(ex, ey, target_element);
10133 something_has_changed = TRUE;
10135 /* for symmetry reasons, freeze newly created border elements */
10136 if (ex != x || ey != y)
10137 Stop[ex][ey] = TRUE; /* no more moving in this frame */
10141 if (something_has_changed)
10143 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10144 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10150 target_element = GET_TARGET_ELEMENT(element, change->target_element, change,
10151 ce_value, ce_score);
10153 if (element == EL_DIAGONAL_GROWING ||
10154 element == EL_DIAGONAL_SHRINKING)
10156 target_element = Store[x][y];
10158 Store[x][y] = EL_EMPTY;
10161 CreateElementFromChange(x, y, target_element);
10163 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10164 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10167 /* this uses direct change before indirect change */
10168 CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
10173 static void HandleElementChange(int x, int y, int page)
10175 int element = MovingOrBlocked2Element(x, y);
10176 struct ElementInfo *ei = &element_info[element];
10177 struct ElementChangeInfo *change = &ei->change_page[page];
10178 boolean handle_action_before_change = FALSE;
10181 if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
10182 !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
10185 printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
10186 x, y, element, element_info[element].token_name);
10187 printf("HandleElementChange(): This should never happen!\n");
10192 /* this can happen with classic bombs on walkable, changing elements */
10193 if (!CAN_CHANGE_OR_HAS_ACTION(element))
10198 if (ChangeDelay[x][y] == 0) /* initialize element change */
10200 ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
10202 if (change->can_change)
10204 /* !!! not clear why graphic animation should be reset at all here !!! */
10205 /* !!! UPDATE: but is needed for correct Snake Bite tail animation !!! */
10206 /* !!! SOLUTION: do not reset if graphics engine set to 4 or above !!! */
10209 GRAPHICAL BUG ADDRESSED BY CHECKING GRAPHICS ENGINE VERSION:
10211 When using an animation frame delay of 1 (this only happens with
10212 "sp_zonk.moving.left/right" in the classic graphics), the default
10213 (non-moving) animation shows wrong animation frames (while the
10214 moving animation, like "sp_zonk.moving.left/right", is correct,
10215 so this graphical bug never shows up with the classic graphics).
10216 For an animation with 4 frames, this causes wrong frames 0,0,1,2
10217 be drawn instead of the correct frames 0,1,2,3. This is caused by
10218 "GfxFrame[][]" being reset *twice* (in two successive frames) after
10219 an element change: First when the change delay ("ChangeDelay[][]")
10220 counter has reached zero after decrementing, then a second time in
10221 the next frame (after "GfxFrame[][]" was already incremented) when
10222 "ChangeDelay[][]" is reset to the initial delay value again.
10224 This causes frame 0 to be drawn twice, while the last frame won't
10225 be drawn anymore, resulting in the wrong frame sequence 0,0,1,2.
10227 As some animations may already be cleverly designed around this bug
10228 (at least the "Snake Bite" snake tail animation does this), it cannot
10229 simply be fixed here without breaking such existing animations.
10230 Unfortunately, it cannot easily be detected if a graphics set was
10231 designed "before" or "after" the bug was fixed. As a workaround,
10232 a new graphics set option "game.graphics_engine_version" was added
10233 to be able to specify the game's major release version for which the
10234 graphics set was designed, which can then be used to decide if the
10235 bugfix should be used (version 4 and above) or not (version 3 or
10236 below, or if no version was specified at all, as with old sets).
10238 (The wrong/fixed animation frames can be tested with the test level set
10239 "test_gfxframe" and level "000", which contains a specially prepared
10240 custom element at level position (x/y) == (11/9) which uses the zonk
10241 animation mentioned above. Using "game.graphics_engine_version: 4"
10242 fixes the wrong animation frames, showing the correct frames 0,1,2,3.
10243 This can also be seen from the debug output for this test element.)
10246 /* when a custom element is about to change (for example by change delay),
10247 do not reset graphic animation when the custom element is moving */
10248 if (game.graphics_engine_version < 4 &&
10251 ResetGfxAnimation(x, y);
10252 ResetRandomAnimationValue(x, y);
10255 if (change->pre_change_function)
10256 change->pre_change_function(x, y);
10260 ChangeDelay[x][y]--;
10262 if (ChangeDelay[x][y] != 0) /* continue element change */
10264 if (change->can_change)
10266 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10268 if (IS_ANIMATED(graphic))
10269 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10271 if (change->change_function)
10272 change->change_function(x, y);
10275 else /* finish element change */
10277 if (ChangePage[x][y] != -1) /* remember page from delayed change */
10279 page = ChangePage[x][y];
10280 ChangePage[x][y] = -1;
10282 change = &ei->change_page[page];
10285 if (IS_MOVING(x, y)) /* never change a running system ;-) */
10287 ChangeDelay[x][y] = 1; /* try change after next move step */
10288 ChangePage[x][y] = page; /* remember page to use for change */
10293 /* special case: set new level random seed before changing element */
10294 if (change->has_action && change->action_type == CA_SET_LEVEL_RANDOM_SEED)
10295 handle_action_before_change = TRUE;
10297 if (change->has_action && handle_action_before_change)
10298 ExecuteCustomElementAction(x, y, element, page);
10300 if (change->can_change)
10302 if (ChangeElement(x, y, element, page))
10304 if (change->post_change_function)
10305 change->post_change_function(x, y);
10309 if (change->has_action && !handle_action_before_change)
10310 ExecuteCustomElementAction(x, y, element, page);
10314 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
10315 int trigger_element,
10317 int trigger_player,
10321 boolean change_done_any = FALSE;
10322 int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
10325 if (!(trigger_events[trigger_element][trigger_event]))
10328 RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
10330 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
10332 int element = EL_CUSTOM_START + i;
10333 boolean change_done = FALSE;
10336 if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
10337 !HAS_ANY_CHANGE_EVENT(element, trigger_event))
10340 for (p = 0; p < element_info[element].num_change_pages; p++)
10342 struct ElementChangeInfo *change = &element_info[element].change_page[p];
10344 if (change->can_change_or_has_action &&
10345 change->has_event[trigger_event] &&
10346 change->trigger_side & trigger_side &&
10347 change->trigger_player & trigger_player &&
10348 change->trigger_page & trigger_page_bits &&
10349 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
10351 change->actual_trigger_element = trigger_element;
10352 change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
10353 change->actual_trigger_player_bits = trigger_player;
10354 change->actual_trigger_side = trigger_side;
10355 change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
10356 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10358 if ((change->can_change && !change_done) || change->has_action)
10362 SCAN_PLAYFIELD(x, y)
10364 if (Feld[x][y] == element)
10366 if (change->can_change && !change_done)
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 ChangeDelay[x][y] = 1;
10377 ChangeEvent[x][y] = trigger_event;
10379 HandleElementChange(x, y, p);
10381 else if (change->has_action)
10383 /* if element already changed in this frame, not only prevent
10384 another element change (checked in ChangeElement()), but
10385 also prevent additional element actions for this element */
10387 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
10388 !level.use_action_after_change_bug)
10391 ExecuteCustomElementAction(x, y, element, p);
10392 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10397 if (change->can_change)
10399 change_done = TRUE;
10400 change_done_any = TRUE;
10407 RECURSION_LOOP_DETECTION_END();
10409 return change_done_any;
10412 static boolean CheckElementChangeExt(int x, int y,
10414 int trigger_element,
10416 int trigger_player,
10419 boolean change_done = FALSE;
10422 if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
10423 !HAS_ANY_CHANGE_EVENT(element, trigger_event))
10426 if (Feld[x][y] == EL_BLOCKED)
10428 Blocked2Moving(x, y, &x, &y);
10429 element = Feld[x][y];
10432 /* check if element has already changed or is about to change after moving */
10433 if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
10434 Feld[x][y] != element) ||
10436 (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
10437 (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
10438 ChangePage[x][y] != -1)))
10441 RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
10443 for (p = 0; p < element_info[element].num_change_pages; p++)
10445 struct ElementChangeInfo *change = &element_info[element].change_page[p];
10447 /* check trigger element for all events where the element that is checked
10448 for changing interacts with a directly adjacent element -- this is
10449 different to element changes that affect other elements to change on the
10450 whole playfield (which is handeld by CheckTriggeredElementChangeExt()) */
10451 boolean check_trigger_element =
10452 (trigger_event == CE_TOUCHING_X ||
10453 trigger_event == CE_HITTING_X ||
10454 trigger_event == CE_HIT_BY_X ||
10455 trigger_event == CE_DIGGING_X); /* this one was forgotten until 3.2.3 */
10457 if (change->can_change_or_has_action &&
10458 change->has_event[trigger_event] &&
10459 change->trigger_side & trigger_side &&
10460 change->trigger_player & trigger_player &&
10461 (!check_trigger_element ||
10462 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
10464 change->actual_trigger_element = trigger_element;
10465 change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
10466 change->actual_trigger_player_bits = trigger_player;
10467 change->actual_trigger_side = trigger_side;
10468 change->actual_trigger_ce_value = CustomValue[x][y];
10469 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10471 /* special case: trigger element not at (x,y) position for some events */
10472 if (check_trigger_element)
10484 { 0, 0 }, { 0, 0 }, { 0, 0 },
10488 int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
10489 int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
10491 change->actual_trigger_ce_value = CustomValue[xx][yy];
10492 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10495 if (change->can_change && !change_done)
10497 ChangeDelay[x][y] = 1;
10498 ChangeEvent[x][y] = trigger_event;
10500 HandleElementChange(x, y, p);
10502 change_done = TRUE;
10504 else if (change->has_action)
10506 ExecuteCustomElementAction(x, y, element, p);
10507 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10512 RECURSION_LOOP_DETECTION_END();
10514 return change_done;
10517 static void PlayPlayerSound(struct PlayerInfo *player)
10519 int jx = player->jx, jy = player->jy;
10520 int sound_element = player->artwork_element;
10521 int last_action = player->last_action_waiting;
10522 int action = player->action_waiting;
10524 if (player->is_waiting)
10526 if (action != last_action)
10527 PlayLevelSoundElementAction(jx, jy, sound_element, action);
10529 PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
10533 if (action != last_action)
10534 StopSound(element_info[sound_element].sound[last_action]);
10536 if (last_action == ACTION_SLEEPING)
10537 PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
10541 static void PlayAllPlayersSound()
10545 for (i = 0; i < MAX_PLAYERS; i++)
10546 if (stored_player[i].active)
10547 PlayPlayerSound(&stored_player[i]);
10550 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
10552 boolean last_waiting = player->is_waiting;
10553 int move_dir = player->MovDir;
10555 player->dir_waiting = move_dir;
10556 player->last_action_waiting = player->action_waiting;
10560 if (!last_waiting) /* not waiting -> waiting */
10562 player->is_waiting = TRUE;
10564 player->frame_counter_bored =
10566 game.player_boring_delay_fixed +
10567 GetSimpleRandom(game.player_boring_delay_random);
10568 player->frame_counter_sleeping =
10570 game.player_sleeping_delay_fixed +
10571 GetSimpleRandom(game.player_sleeping_delay_random);
10573 InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
10576 if (game.player_sleeping_delay_fixed +
10577 game.player_sleeping_delay_random > 0 &&
10578 player->anim_delay_counter == 0 &&
10579 player->post_delay_counter == 0 &&
10580 FrameCounter >= player->frame_counter_sleeping)
10581 player->is_sleeping = TRUE;
10582 else if (game.player_boring_delay_fixed +
10583 game.player_boring_delay_random > 0 &&
10584 FrameCounter >= player->frame_counter_bored)
10585 player->is_bored = TRUE;
10587 player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
10588 player->is_bored ? ACTION_BORING :
10591 if (player->is_sleeping && player->use_murphy)
10593 /* special case for sleeping Murphy when leaning against non-free tile */
10595 if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
10596 (Feld[player->jx - 1][player->jy] != EL_EMPTY &&
10597 !IS_MOVING(player->jx - 1, player->jy)))
10598 move_dir = MV_LEFT;
10599 else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
10600 (Feld[player->jx + 1][player->jy] != EL_EMPTY &&
10601 !IS_MOVING(player->jx + 1, player->jy)))
10602 move_dir = MV_RIGHT;
10604 player->is_sleeping = FALSE;
10606 player->dir_waiting = move_dir;
10609 if (player->is_sleeping)
10611 if (player->num_special_action_sleeping > 0)
10613 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
10615 int last_special_action = player->special_action_sleeping;
10616 int num_special_action = player->num_special_action_sleeping;
10617 int special_action =
10618 (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
10619 last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
10620 last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
10621 last_special_action + 1 : ACTION_SLEEPING);
10622 int special_graphic =
10623 el_act_dir2img(player->artwork_element, special_action, move_dir);
10625 player->anim_delay_counter =
10626 graphic_info[special_graphic].anim_delay_fixed +
10627 GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
10628 player->post_delay_counter =
10629 graphic_info[special_graphic].post_delay_fixed +
10630 GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
10632 player->special_action_sleeping = special_action;
10635 if (player->anim_delay_counter > 0)
10637 player->action_waiting = player->special_action_sleeping;
10638 player->anim_delay_counter--;
10640 else if (player->post_delay_counter > 0)
10642 player->post_delay_counter--;
10646 else if (player->is_bored)
10648 if (player->num_special_action_bored > 0)
10650 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
10652 int special_action =
10653 ACTION_BORING_1 + GetSimpleRandom(player->num_special_action_bored);
10654 int special_graphic =
10655 el_act_dir2img(player->artwork_element, special_action, move_dir);
10657 player->anim_delay_counter =
10658 graphic_info[special_graphic].anim_delay_fixed +
10659 GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
10660 player->post_delay_counter =
10661 graphic_info[special_graphic].post_delay_fixed +
10662 GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
10664 player->special_action_bored = special_action;
10667 if (player->anim_delay_counter > 0)
10669 player->action_waiting = player->special_action_bored;
10670 player->anim_delay_counter--;
10672 else if (player->post_delay_counter > 0)
10674 player->post_delay_counter--;
10679 else if (last_waiting) /* waiting -> not waiting */
10681 player->is_waiting = FALSE;
10682 player->is_bored = FALSE;
10683 player->is_sleeping = FALSE;
10685 player->frame_counter_bored = -1;
10686 player->frame_counter_sleeping = -1;
10688 player->anim_delay_counter = 0;
10689 player->post_delay_counter = 0;
10691 player->dir_waiting = player->MovDir;
10692 player->action_waiting = ACTION_DEFAULT;
10694 player->special_action_bored = ACTION_DEFAULT;
10695 player->special_action_sleeping = ACTION_DEFAULT;
10699 static void CheckSaveEngineSnapshot(struct PlayerInfo *player)
10701 if ((!player->is_moving && player->was_moving) ||
10702 (player->MovPos == 0 && player->was_moving) ||
10703 (player->is_snapping && !player->was_snapping) ||
10704 (player->is_dropping && !player->was_dropping))
10706 if (!CheckSaveEngineSnapshotToList())
10709 player->was_moving = FALSE;
10710 player->was_snapping = TRUE;
10711 player->was_dropping = TRUE;
10715 if (player->is_moving)
10716 player->was_moving = TRUE;
10718 if (!player->is_snapping)
10719 player->was_snapping = FALSE;
10721 if (!player->is_dropping)
10722 player->was_dropping = FALSE;
10726 static void CheckSingleStepMode(struct PlayerInfo *player)
10728 if (tape.single_step && tape.recording && !tape.pausing)
10730 /* as it is called "single step mode", just return to pause mode when the
10731 player stopped moving after one tile (or never starts moving at all) */
10732 if (!player->is_moving &&
10733 !player->is_pushing &&
10734 !player->is_dropping_pressed)
10736 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
10737 SnapField(player, 0, 0); /* stop snapping */
10741 CheckSaveEngineSnapshot(player);
10744 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
10746 int left = player_action & JOY_LEFT;
10747 int right = player_action & JOY_RIGHT;
10748 int up = player_action & JOY_UP;
10749 int down = player_action & JOY_DOWN;
10750 int button1 = player_action & JOY_BUTTON_1;
10751 int button2 = player_action & JOY_BUTTON_2;
10752 int dx = (left ? -1 : right ? 1 : 0);
10753 int dy = (up ? -1 : down ? 1 : 0);
10755 if (!player->active || tape.pausing)
10761 SnapField(player, dx, dy);
10765 DropElement(player);
10767 MovePlayer(player, dx, dy);
10770 CheckSingleStepMode(player);
10772 SetPlayerWaiting(player, FALSE);
10774 return player_action;
10778 /* no actions for this player (no input at player's configured device) */
10780 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
10781 SnapField(player, 0, 0);
10782 CheckGravityMovementWhenNotMoving(player);
10784 if (player->MovPos == 0)
10785 SetPlayerWaiting(player, TRUE);
10787 if (player->MovPos == 0) /* needed for tape.playing */
10788 player->is_moving = FALSE;
10790 player->is_dropping = FALSE;
10791 player->is_dropping_pressed = FALSE;
10792 player->drop_pressed_delay = 0;
10794 CheckSingleStepMode(player);
10800 static void CheckLevelTime()
10804 /* !!! SAME CODE AS IN "GameActions()" -- FIX THIS !!! */
10805 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
10807 if (level.native_em_level->lev->home == 0) /* all players at home */
10809 PlayerWins(local_player);
10811 AllPlayersGone = TRUE;
10813 level.native_em_level->lev->home = -1;
10816 if (level.native_em_level->ply[0]->alive == 0 &&
10817 level.native_em_level->ply[1]->alive == 0 &&
10818 level.native_em_level->ply[2]->alive == 0 &&
10819 level.native_em_level->ply[3]->alive == 0) /* all dead */
10820 AllPlayersGone = TRUE;
10822 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
10824 if (game_sp.LevelSolved &&
10825 !game_sp.GameOver) /* game won */
10827 PlayerWins(local_player);
10829 game_sp.GameOver = TRUE;
10831 AllPlayersGone = TRUE;
10834 if (game_sp.GameOver) /* game lost */
10835 AllPlayersGone = TRUE;
10838 if (TimeFrames >= FRAMES_PER_SECOND)
10843 for (i = 0; i < MAX_PLAYERS; i++)
10845 struct PlayerInfo *player = &stored_player[i];
10847 if (SHIELD_ON(player))
10849 player->shield_normal_time_left--;
10851 if (player->shield_deadly_time_left > 0)
10852 player->shield_deadly_time_left--;
10856 if (!local_player->LevelSolved && !level.use_step_counter)
10864 if (TimeLeft <= 10 && setup.time_limit)
10865 PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
10867 /* this does not make sense: game_panel_controls[GAME_PANEL_TIME].value
10868 is reset from other values in UpdateGameDoorValues() -- FIX THIS */
10870 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
10872 if (!TimeLeft && setup.time_limit)
10874 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
10875 level.native_em_level->lev->killed_out_of_time = TRUE;
10877 for (i = 0; i < MAX_PLAYERS; i++)
10878 KillPlayer(&stored_player[i]);
10881 else if (game.no_time_limit && !AllPlayersGone) /* level w/o time limit */
10883 game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
10886 level.native_em_level->lev->time =
10887 (game.no_time_limit ? TimePlayed : TimeLeft);
10890 if (tape.recording || tape.playing)
10891 DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
10894 if (tape.recording || tape.playing)
10895 DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
10897 UpdateAndDisplayGameControlValues();
10900 void AdvanceFrameAndPlayerCounters(int player_nr)
10904 /* advance frame counters (global frame counter and time frame counter) */
10908 /* advance player counters (counters for move delay, move animation etc.) */
10909 for (i = 0; i < MAX_PLAYERS; i++)
10911 boolean advance_player_counters = (player_nr == -1 || player_nr == i);
10912 int move_delay_value = stored_player[i].move_delay_value;
10913 int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
10915 if (!advance_player_counters) /* not all players may be affected */
10918 if (move_frames == 0) /* less than one move per game frame */
10920 int stepsize = TILEX / move_delay_value;
10921 int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
10922 int count = (stored_player[i].is_moving ?
10923 ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
10925 if (count % delay == 0)
10929 stored_player[i].Frame += move_frames;
10931 if (stored_player[i].MovPos != 0)
10932 stored_player[i].StepFrame += move_frames;
10934 if (stored_player[i].move_delay > 0)
10935 stored_player[i].move_delay--;
10937 /* due to bugs in previous versions, counter must count up, not down */
10938 if (stored_player[i].push_delay != -1)
10939 stored_player[i].push_delay++;
10941 if (stored_player[i].drop_delay > 0)
10942 stored_player[i].drop_delay--;
10944 if (stored_player[i].is_dropping_pressed)
10945 stored_player[i].drop_pressed_delay++;
10949 void StartGameActions(boolean init_network_game, boolean record_tape,
10952 unsigned int new_random_seed = InitRND(random_seed);
10955 TapeStartRecording(new_random_seed);
10957 #if defined(NETWORK_AVALIABLE)
10958 if (init_network_game)
10960 SendToServer_StartPlaying();
10969 void GameActionsExt()
10972 static unsigned int game_frame_delay = 0;
10974 unsigned int game_frame_delay_value;
10975 byte *recorded_player_action;
10976 byte summarized_player_action = 0;
10977 byte tape_action[MAX_PLAYERS];
10980 /* detect endless loops, caused by custom element programming */
10981 if (recursion_loop_detected && recursion_loop_depth == 0)
10983 char *message = getStringCat3("Internal Error! Element ",
10984 EL_NAME(recursion_loop_element),
10985 " caused endless loop! Quit the game?");
10987 Error(ERR_WARN, "element '%s' caused endless loop in game engine",
10988 EL_NAME(recursion_loop_element));
10990 RequestQuitGameExt(FALSE, level_editor_test_game, message);
10992 recursion_loop_detected = FALSE; /* if game should be continued */
10999 if (game.restart_level)
11000 StartGameActions(options.network, setup.autorecord, level.random_seed);
11002 /* !!! SAME CODE AS IN "CheckLevelTime()" -- FIX THIS !!! */
11003 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11005 if (level.native_em_level->lev->home == 0) /* all players at home */
11007 PlayerWins(local_player);
11009 AllPlayersGone = TRUE;
11011 level.native_em_level->lev->home = -1;
11014 if (level.native_em_level->ply[0]->alive == 0 &&
11015 level.native_em_level->ply[1]->alive == 0 &&
11016 level.native_em_level->ply[2]->alive == 0 &&
11017 level.native_em_level->ply[3]->alive == 0) /* all dead */
11018 AllPlayersGone = TRUE;
11020 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11022 if (game_sp.LevelSolved &&
11023 !game_sp.GameOver) /* game won */
11025 PlayerWins(local_player);
11027 game_sp.GameOver = TRUE;
11029 AllPlayersGone = TRUE;
11032 if (game_sp.GameOver) /* game lost */
11033 AllPlayersGone = TRUE;
11036 if (local_player->LevelSolved && !local_player->LevelSolved_GameEnd)
11039 if (AllPlayersGone && !TAPE_IS_STOPPED(tape))
11042 if (game_status != GAME_MODE_PLAYING) /* status might have changed */
11045 game_frame_delay_value =
11046 (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
11048 if (tape.playing && tape.warp_forward && !tape.pausing)
11049 game_frame_delay_value = 0;
11051 SetVideoFrameDelay(game_frame_delay_value);
11055 /* ---------- main game synchronization point ---------- */
11057 int skip = WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11059 printf("::: skip == %d\n", skip);
11062 /* ---------- main game synchronization point ---------- */
11064 WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11068 if (network_playing && !network_player_action_received)
11070 /* try to get network player actions in time */
11072 #if defined(NETWORK_AVALIABLE)
11073 /* last chance to get network player actions without main loop delay */
11074 HandleNetworking();
11077 /* game was quit by network peer */
11078 if (game_status != GAME_MODE_PLAYING)
11081 if (!network_player_action_received)
11082 return; /* failed to get network player actions in time */
11084 /* do not yet reset "network_player_action_received" (for tape.pausing) */
11090 /* at this point we know that we really continue executing the game */
11092 network_player_action_received = FALSE;
11094 /* when playing tape, read previously recorded player input from tape data */
11095 recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
11097 /* TapePlayAction() may return NULL when toggling to "pause before death" */
11101 if (tape.set_centered_player)
11103 game.centered_player_nr_next = tape.centered_player_nr_next;
11104 game.set_centered_player = TRUE;
11107 for (i = 0; i < MAX_PLAYERS; i++)
11109 summarized_player_action |= stored_player[i].action;
11111 if (!network_playing && (game.team_mode || tape.playing))
11112 stored_player[i].effective_action = stored_player[i].action;
11115 #if defined(NETWORK_AVALIABLE)
11116 if (network_playing)
11117 SendToServer_MovePlayer(summarized_player_action);
11120 // summarize all actions at local players mapped input device position
11121 // (this allows using different input devices in single player mode)
11122 if (!options.network && !game.team_mode)
11123 stored_player[map_player_action[local_player->index_nr]].effective_action =
11124 summarized_player_action;
11126 if (tape.recording &&
11128 setup.input_on_focus &&
11129 game.centered_player_nr != -1)
11131 for (i = 0; i < MAX_PLAYERS; i++)
11132 stored_player[i].effective_action =
11133 (i == game.centered_player_nr ? summarized_player_action : 0);
11136 if (recorded_player_action != NULL)
11137 for (i = 0; i < MAX_PLAYERS; i++)
11138 stored_player[i].effective_action = recorded_player_action[i];
11140 for (i = 0; i < MAX_PLAYERS; i++)
11142 tape_action[i] = stored_player[i].effective_action;
11144 /* (this may happen in the RND game engine if a player was not present on
11145 the playfield on level start, but appeared later from a custom element */
11146 if (setup.team_mode &&
11149 !tape.player_participates[i])
11150 tape.player_participates[i] = TRUE;
11153 /* only record actions from input devices, but not programmed actions */
11154 if (tape.recording)
11155 TapeRecordAction(tape_action);
11157 #if USE_NEW_PLAYER_ASSIGNMENTS
11158 // !!! also map player actions in single player mode !!!
11159 // if (game.team_mode)
11162 byte mapped_action[MAX_PLAYERS];
11164 #if DEBUG_PLAYER_ACTIONS
11166 for (i = 0; i < MAX_PLAYERS; i++)
11167 printf(" %d, ", stored_player[i].effective_action);
11170 for (i = 0; i < MAX_PLAYERS; i++)
11171 mapped_action[i] = stored_player[map_player_action[i]].effective_action;
11173 for (i = 0; i < MAX_PLAYERS; i++)
11174 stored_player[i].effective_action = mapped_action[i];
11176 #if DEBUG_PLAYER_ACTIONS
11178 for (i = 0; i < MAX_PLAYERS; i++)
11179 printf(" %d, ", stored_player[i].effective_action);
11183 #if DEBUG_PLAYER_ACTIONS
11187 for (i = 0; i < MAX_PLAYERS; i++)
11188 printf(" %d, ", stored_player[i].effective_action);
11194 for (i = 0; i < MAX_PLAYERS; i++)
11196 // allow engine snapshot in case of changed movement attempt
11197 if ((game.snapshot.last_action[i] & KEY_MOTION) !=
11198 (stored_player[i].effective_action & KEY_MOTION))
11199 game.snapshot.changed_action = TRUE;
11201 // allow engine snapshot in case of snapping/dropping attempt
11202 if ((game.snapshot.last_action[i] & KEY_BUTTON) == 0 &&
11203 (stored_player[i].effective_action & KEY_BUTTON) != 0)
11204 game.snapshot.changed_action = TRUE;
11206 game.snapshot.last_action[i] = stored_player[i].effective_action;
11209 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11211 GameActions_EM_Main();
11213 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11215 GameActions_SP_Main();
11219 GameActions_RND_Main();
11222 BlitScreenToBitmap(backbuffer);
11226 AdvanceFrameAndPlayerCounters(-1); /* advance counters for all players */
11228 if (global.show_frames_per_second)
11230 static unsigned int fps_counter = 0;
11231 static int fps_frames = 0;
11232 unsigned int fps_delay_ms = Counter() - fps_counter;
11236 if (fps_delay_ms >= 500) /* calculate FPS every 0.5 seconds */
11238 global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
11241 fps_counter = Counter();
11243 /* always draw FPS to screen after FPS value was updated */
11244 redraw_mask |= REDRAW_FPS;
11247 /* only draw FPS if no screen areas are deactivated (invisible warp mode) */
11248 if (GetDrawDeactivationMask() == REDRAW_NONE)
11249 redraw_mask |= REDRAW_FPS;
11253 static void GameActions_CheckSaveEngineSnapshot()
11255 if (!game.snapshot.save_snapshot)
11258 // clear flag for saving snapshot _before_ saving snapshot
11259 game.snapshot.save_snapshot = FALSE;
11261 SaveEngineSnapshotToList();
11268 GameActions_CheckSaveEngineSnapshot();
11271 void GameActions_EM_Main()
11273 byte effective_action[MAX_PLAYERS];
11274 boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11277 for (i = 0; i < MAX_PLAYERS; i++)
11278 effective_action[i] = stored_player[i].effective_action;
11280 GameActions_EM(effective_action, warp_mode);
11283 void GameActions_SP_Main()
11285 byte effective_action[MAX_PLAYERS];
11286 boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11289 for (i = 0; i < MAX_PLAYERS; i++)
11290 effective_action[i] = stored_player[i].effective_action;
11292 GameActions_SP(effective_action, warp_mode);
11294 for (i = 0; i < MAX_PLAYERS; i++)
11296 if (stored_player[i].force_dropping)
11297 stored_player[i].action |= KEY_BUTTON_DROP;
11299 stored_player[i].force_dropping = FALSE;
11303 void GameActions_RND_Main()
11308 void GameActions_RND()
11310 int magic_wall_x = 0, magic_wall_y = 0;
11311 int i, x, y, element, graphic, last_gfx_frame;
11313 InitPlayfieldScanModeVars();
11315 if (game.engine_version >= VERSION_IDENT(3,2,0,7))
11317 SCAN_PLAYFIELD(x, y)
11319 ChangeCount[x][y] = 0;
11320 ChangeEvent[x][y] = -1;
11324 if (game.set_centered_player)
11326 boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
11328 /* switching to "all players" only possible if all players fit to screen */
11329 if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
11331 game.centered_player_nr_next = game.centered_player_nr;
11332 game.set_centered_player = FALSE;
11335 /* do not switch focus to non-existing (or non-active) player */
11336 if (game.centered_player_nr_next >= 0 &&
11337 !stored_player[game.centered_player_nr_next].active)
11339 game.centered_player_nr_next = game.centered_player_nr;
11340 game.set_centered_player = FALSE;
11344 if (game.set_centered_player &&
11345 ScreenMovPos == 0) /* screen currently aligned at tile position */
11349 if (game.centered_player_nr_next == -1)
11351 setScreenCenteredToAllPlayers(&sx, &sy);
11355 sx = stored_player[game.centered_player_nr_next].jx;
11356 sy = stored_player[game.centered_player_nr_next].jy;
11359 game.centered_player_nr = game.centered_player_nr_next;
11360 game.set_centered_player = FALSE;
11362 DrawRelocateScreen(0, 0, sx, sy, MV_NONE, TRUE, setup.quick_switch);
11363 DrawGameDoorValues();
11366 for (i = 0; i < MAX_PLAYERS; i++)
11368 int actual_player_action = stored_player[i].effective_action;
11371 /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
11372 - rnd_equinox_tetrachloride 048
11373 - rnd_equinox_tetrachloride_ii 096
11374 - rnd_emanuel_schmieg 002
11375 - doctor_sloan_ww 001, 020
11377 if (stored_player[i].MovPos == 0)
11378 CheckGravityMovement(&stored_player[i]);
11381 /* overwrite programmed action with tape action */
11382 if (stored_player[i].programmed_action)
11383 actual_player_action = stored_player[i].programmed_action;
11385 PlayerActions(&stored_player[i], actual_player_action);
11387 ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
11390 ScrollScreen(NULL, SCROLL_GO_ON);
11392 /* for backwards compatibility, the following code emulates a fixed bug that
11393 occured when pushing elements (causing elements that just made their last
11394 pushing step to already (if possible) make their first falling step in the
11395 same game frame, which is bad); this code is also needed to use the famous
11396 "spring push bug" which is used in older levels and might be wanted to be
11397 used also in newer levels, but in this case the buggy pushing code is only
11398 affecting the "spring" element and no other elements */
11400 if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
11402 for (i = 0; i < MAX_PLAYERS; i++)
11404 struct PlayerInfo *player = &stored_player[i];
11405 int x = player->jx;
11406 int y = player->jy;
11408 if (player->active && player->is_pushing && player->is_moving &&
11410 (game.engine_version < VERSION_IDENT(2,2,0,7) ||
11411 Feld[x][y] == EL_SPRING))
11413 ContinueMoving(x, y);
11415 /* continue moving after pushing (this is actually a bug) */
11416 if (!IS_MOVING(x, y))
11417 Stop[x][y] = FALSE;
11422 SCAN_PLAYFIELD(x, y)
11424 ChangeCount[x][y] = 0;
11425 ChangeEvent[x][y] = -1;
11427 /* this must be handled before main playfield loop */
11428 if (Feld[x][y] == EL_PLAYER_IS_LEAVING)
11431 if (MovDelay[x][y] <= 0)
11435 if (Feld[x][y] == EL_ELEMENT_SNAPPING)
11438 if (MovDelay[x][y] <= 0)
11441 TEST_DrawLevelField(x, y);
11443 TestIfElementTouchesCustomElement(x, y); /* for empty space */
11448 if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
11450 printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
11451 printf("GameActions(): This should never happen!\n");
11453 ChangePage[x][y] = -1;
11457 Stop[x][y] = FALSE;
11458 if (WasJustMoving[x][y] > 0)
11459 WasJustMoving[x][y]--;
11460 if (WasJustFalling[x][y] > 0)
11461 WasJustFalling[x][y]--;
11462 if (CheckCollision[x][y] > 0)
11463 CheckCollision[x][y]--;
11464 if (CheckImpact[x][y] > 0)
11465 CheckImpact[x][y]--;
11469 /* reset finished pushing action (not done in ContinueMoving() to allow
11470 continuous pushing animation for elements with zero push delay) */
11471 if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
11473 ResetGfxAnimation(x, y);
11474 TEST_DrawLevelField(x, y);
11478 if (IS_BLOCKED(x, y))
11482 Blocked2Moving(x, y, &oldx, &oldy);
11483 if (!IS_MOVING(oldx, oldy))
11485 printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
11486 printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
11487 printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
11488 printf("GameActions(): This should never happen!\n");
11494 SCAN_PLAYFIELD(x, y)
11496 element = Feld[x][y];
11497 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11498 last_gfx_frame = GfxFrame[x][y];
11500 ResetGfxFrame(x, y);
11502 if (GfxFrame[x][y] != last_gfx_frame && !Stop[x][y])
11503 DrawLevelGraphicAnimation(x, y, graphic);
11505 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
11506 IS_NEXT_FRAME(GfxFrame[x][y], graphic))
11507 ResetRandomAnimationValue(x, y);
11509 SetRandomAnimationValue(x, y);
11511 PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
11513 if (IS_INACTIVE(element))
11515 if (IS_ANIMATED(graphic))
11516 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11521 /* this may take place after moving, so 'element' may have changed */
11522 if (IS_CHANGING(x, y) &&
11523 (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
11525 int page = element_info[element].event_page_nr[CE_DELAY];
11527 HandleElementChange(x, y, page);
11529 element = Feld[x][y];
11530 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11533 if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
11537 element = Feld[x][y];
11538 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11540 if (IS_ANIMATED(graphic) &&
11541 !IS_MOVING(x, y) &&
11543 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11545 if (IS_GEM(element) || element == EL_SP_INFOTRON)
11546 TEST_DrawTwinkleOnField(x, y);
11548 else if (element == EL_ACID)
11551 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11553 else if ((element == EL_EXIT_OPEN ||
11554 element == EL_EM_EXIT_OPEN ||
11555 element == EL_SP_EXIT_OPEN ||
11556 element == EL_STEEL_EXIT_OPEN ||
11557 element == EL_EM_STEEL_EXIT_OPEN ||
11558 element == EL_SP_TERMINAL ||
11559 element == EL_SP_TERMINAL_ACTIVE ||
11560 element == EL_EXTRA_TIME ||
11561 element == EL_SHIELD_NORMAL ||
11562 element == EL_SHIELD_DEADLY) &&
11563 IS_ANIMATED(graphic))
11564 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11565 else if (IS_MOVING(x, y))
11566 ContinueMoving(x, y);
11567 else if (IS_ACTIVE_BOMB(element))
11568 CheckDynamite(x, y);
11569 else if (element == EL_AMOEBA_GROWING)
11570 AmoebeWaechst(x, y);
11571 else if (element == EL_AMOEBA_SHRINKING)
11572 AmoebaDisappearing(x, y);
11574 #if !USE_NEW_AMOEBA_CODE
11575 else if (IS_AMOEBALIVE(element))
11576 AmoebeAbleger(x, y);
11579 else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
11581 else if (element == EL_EXIT_CLOSED)
11583 else if (element == EL_EM_EXIT_CLOSED)
11585 else if (element == EL_STEEL_EXIT_CLOSED)
11586 CheckExitSteel(x, y);
11587 else if (element == EL_EM_STEEL_EXIT_CLOSED)
11588 CheckExitSteelEM(x, y);
11589 else if (element == EL_SP_EXIT_CLOSED)
11591 else if (element == EL_EXPANDABLE_WALL_GROWING ||
11592 element == EL_EXPANDABLE_STEELWALL_GROWING)
11593 MauerWaechst(x, y);
11594 else if (element == EL_EXPANDABLE_WALL ||
11595 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
11596 element == EL_EXPANDABLE_WALL_VERTICAL ||
11597 element == EL_EXPANDABLE_WALL_ANY ||
11598 element == EL_BD_EXPANDABLE_WALL)
11599 MauerAbleger(x, y);
11600 else if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
11601 element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
11602 element == EL_EXPANDABLE_STEELWALL_ANY)
11603 MauerAblegerStahl(x, y);
11604 else if (element == EL_FLAMES)
11605 CheckForDragon(x, y);
11606 else if (element == EL_EXPLOSION)
11607 ; /* drawing of correct explosion animation is handled separately */
11608 else if (element == EL_ELEMENT_SNAPPING ||
11609 element == EL_DIAGONAL_SHRINKING ||
11610 element == EL_DIAGONAL_GROWING)
11612 graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
11614 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11616 else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
11617 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11619 if (IS_BELT_ACTIVE(element))
11620 PlayLevelSoundAction(x, y, ACTION_ACTIVE);
11622 if (game.magic_wall_active)
11624 int jx = local_player->jx, jy = local_player->jy;
11626 /* play the element sound at the position nearest to the player */
11627 if ((element == EL_MAGIC_WALL_FULL ||
11628 element == EL_MAGIC_WALL_ACTIVE ||
11629 element == EL_MAGIC_WALL_EMPTYING ||
11630 element == EL_BD_MAGIC_WALL_FULL ||
11631 element == EL_BD_MAGIC_WALL_ACTIVE ||
11632 element == EL_BD_MAGIC_WALL_EMPTYING ||
11633 element == EL_DC_MAGIC_WALL_FULL ||
11634 element == EL_DC_MAGIC_WALL_ACTIVE ||
11635 element == EL_DC_MAGIC_WALL_EMPTYING) &&
11636 ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
11644 #if USE_NEW_AMOEBA_CODE
11645 /* new experimental amoeba growth stuff */
11646 if (!(FrameCounter % 8))
11648 static unsigned int random = 1684108901;
11650 for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
11652 x = RND(lev_fieldx);
11653 y = RND(lev_fieldy);
11654 element = Feld[x][y];
11656 if (!IS_PLAYER(x,y) &&
11657 (element == EL_EMPTY ||
11658 CAN_GROW_INTO(element) ||
11659 element == EL_QUICKSAND_EMPTY ||
11660 element == EL_QUICKSAND_FAST_EMPTY ||
11661 element == EL_ACID_SPLASH_LEFT ||
11662 element == EL_ACID_SPLASH_RIGHT))
11664 if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
11665 (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
11666 (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
11667 (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
11668 Feld[x][y] = EL_AMOEBA_DROP;
11671 random = random * 129 + 1;
11676 game.explosions_delayed = FALSE;
11678 SCAN_PLAYFIELD(x, y)
11680 element = Feld[x][y];
11682 if (ExplodeField[x][y])
11683 Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
11684 else if (element == EL_EXPLOSION)
11685 Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
11687 ExplodeField[x][y] = EX_TYPE_NONE;
11690 game.explosions_delayed = TRUE;
11692 if (game.magic_wall_active)
11694 if (!(game.magic_wall_time_left % 4))
11696 int element = Feld[magic_wall_x][magic_wall_y];
11698 if (element == EL_BD_MAGIC_WALL_FULL ||
11699 element == EL_BD_MAGIC_WALL_ACTIVE ||
11700 element == EL_BD_MAGIC_WALL_EMPTYING)
11701 PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
11702 else if (element == EL_DC_MAGIC_WALL_FULL ||
11703 element == EL_DC_MAGIC_WALL_ACTIVE ||
11704 element == EL_DC_MAGIC_WALL_EMPTYING)
11705 PlayLevelSound(magic_wall_x, magic_wall_y, SND_DC_MAGIC_WALL_ACTIVE);
11707 PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
11710 if (game.magic_wall_time_left > 0)
11712 game.magic_wall_time_left--;
11714 if (!game.magic_wall_time_left)
11716 SCAN_PLAYFIELD(x, y)
11718 element = Feld[x][y];
11720 if (element == EL_MAGIC_WALL_ACTIVE ||
11721 element == EL_MAGIC_WALL_FULL)
11723 Feld[x][y] = EL_MAGIC_WALL_DEAD;
11724 TEST_DrawLevelField(x, y);
11726 else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
11727 element == EL_BD_MAGIC_WALL_FULL)
11729 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
11730 TEST_DrawLevelField(x, y);
11732 else if (element == EL_DC_MAGIC_WALL_ACTIVE ||
11733 element == EL_DC_MAGIC_WALL_FULL)
11735 Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
11736 TEST_DrawLevelField(x, y);
11740 game.magic_wall_active = FALSE;
11745 if (game.light_time_left > 0)
11747 game.light_time_left--;
11749 if (game.light_time_left == 0)
11750 RedrawAllLightSwitchesAndInvisibleElements();
11753 if (game.timegate_time_left > 0)
11755 game.timegate_time_left--;
11757 if (game.timegate_time_left == 0)
11758 CloseAllOpenTimegates();
11761 if (game.lenses_time_left > 0)
11763 game.lenses_time_left--;
11765 if (game.lenses_time_left == 0)
11766 RedrawAllInvisibleElementsForLenses();
11769 if (game.magnify_time_left > 0)
11771 game.magnify_time_left--;
11773 if (game.magnify_time_left == 0)
11774 RedrawAllInvisibleElementsForMagnifier();
11777 for (i = 0; i < MAX_PLAYERS; i++)
11779 struct PlayerInfo *player = &stored_player[i];
11781 if (SHIELD_ON(player))
11783 if (player->shield_deadly_time_left)
11784 PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
11785 else if (player->shield_normal_time_left)
11786 PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
11790 #if USE_DELAYED_GFX_REDRAW
11791 SCAN_PLAYFIELD(x, y)
11793 if (GfxRedraw[x][y] != GFX_REDRAW_NONE)
11795 /* !!! PROBLEM: THIS REDRAWS THE PLAYFIELD _AFTER_ THE SCAN, BUT TILES
11796 !!! MAY HAVE CHANGED AFTER BEING DRAWN DURING PLAYFIELD SCAN !!! */
11798 if (GfxRedraw[x][y] & GFX_REDRAW_TILE)
11799 DrawLevelField(x, y);
11801 if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED)
11802 DrawLevelFieldCrumbled(x, y);
11804 if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS)
11805 DrawLevelFieldCrumbledNeighbours(x, y);
11807 if (GfxRedraw[x][y] & GFX_REDRAW_TILE_TWINKLED)
11808 DrawTwinkleOnField(x, y);
11811 GfxRedraw[x][y] = GFX_REDRAW_NONE;
11816 PlayAllPlayersSound();
11818 if (local_player->show_envelope != 0 && local_player->MovPos == 0)
11820 ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
11822 local_player->show_envelope = 0;
11825 /* use random number generator in every frame to make it less predictable */
11826 if (game.engine_version >= VERSION_IDENT(3,1,1,0))
11830 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
11832 int min_x = x, min_y = y, max_x = x, max_y = y;
11835 for (i = 0; i < MAX_PLAYERS; i++)
11837 int jx = stored_player[i].jx, jy = stored_player[i].jy;
11839 if (!stored_player[i].active || &stored_player[i] == player)
11842 min_x = MIN(min_x, jx);
11843 min_y = MIN(min_y, jy);
11844 max_x = MAX(max_x, jx);
11845 max_y = MAX(max_y, jy);
11848 return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
11851 static boolean AllPlayersInVisibleScreen()
11855 for (i = 0; i < MAX_PLAYERS; i++)
11857 int jx = stored_player[i].jx, jy = stored_player[i].jy;
11859 if (!stored_player[i].active)
11862 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
11869 void ScrollLevel(int dx, int dy)
11871 int scroll_offset = 2 * TILEX_VAR;
11874 BlitBitmap(drawto_field, drawto_field,
11875 FX + TILEX_VAR * (dx == -1) - scroll_offset,
11876 FY + TILEY_VAR * (dy == -1) - scroll_offset,
11877 SXSIZE - TILEX_VAR * (dx != 0) + 2 * scroll_offset,
11878 SYSIZE - TILEY_VAR * (dy != 0) + 2 * scroll_offset,
11879 FX + TILEX_VAR * (dx == 1) - scroll_offset,
11880 FY + TILEY_VAR * (dy == 1) - scroll_offset);
11884 x = (dx == 1 ? BX1 : BX2);
11885 for (y = BY1; y <= BY2; y++)
11886 DrawScreenField(x, y);
11891 y = (dy == 1 ? BY1 : BY2);
11892 for (x = BX1; x <= BX2; x++)
11893 DrawScreenField(x, y);
11896 redraw_mask |= REDRAW_FIELD;
11899 static boolean canFallDown(struct PlayerInfo *player)
11901 int jx = player->jx, jy = player->jy;
11903 return (IN_LEV_FIELD(jx, jy + 1) &&
11904 (IS_FREE(jx, jy + 1) ||
11905 (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
11906 IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
11907 !IS_WALKABLE_INSIDE(Feld[jx][jy]));
11910 static boolean canPassField(int x, int y, int move_dir)
11912 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
11913 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
11914 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
11915 int nextx = x + dx;
11916 int nexty = y + dy;
11917 int element = Feld[x][y];
11919 return (IS_PASSABLE_FROM(element, opposite_dir) &&
11920 !CAN_MOVE(element) &&
11921 IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
11922 IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
11923 (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
11926 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
11928 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
11929 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
11930 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
11934 return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
11935 IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
11936 (IS_DIGGABLE(Feld[newx][newy]) ||
11937 IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
11938 canPassField(newx, newy, move_dir)));
11941 static void CheckGravityMovement(struct PlayerInfo *player)
11943 if (player->gravity && !player->programmed_action)
11945 int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
11946 int move_dir_vertical = player->effective_action & MV_VERTICAL;
11947 boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
11948 int jx = player->jx, jy = player->jy;
11949 boolean player_is_moving_to_valid_field =
11950 (!player_is_snapping &&
11951 (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
11952 canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
11953 boolean player_can_fall_down = canFallDown(player);
11955 if (player_can_fall_down &&
11956 !player_is_moving_to_valid_field)
11957 player->programmed_action = MV_DOWN;
11961 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
11963 return CheckGravityMovement(player);
11965 if (player->gravity && !player->programmed_action)
11967 int jx = player->jx, jy = player->jy;
11968 boolean field_under_player_is_free =
11969 (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
11970 boolean player_is_standing_on_valid_field =
11971 (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
11972 (IS_WALKABLE(Feld[jx][jy]) &&
11973 !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
11975 if (field_under_player_is_free && !player_is_standing_on_valid_field)
11976 player->programmed_action = MV_DOWN;
11981 MovePlayerOneStep()
11982 -----------------------------------------------------------------------------
11983 dx, dy: direction (non-diagonal) to try to move the player to
11984 real_dx, real_dy: direction as read from input device (can be diagonal)
11987 boolean MovePlayerOneStep(struct PlayerInfo *player,
11988 int dx, int dy, int real_dx, int real_dy)
11990 int jx = player->jx, jy = player->jy;
11991 int new_jx = jx + dx, new_jy = jy + dy;
11993 boolean player_can_move = !player->cannot_move;
11995 if (!player->active || (!dx && !dy))
11996 return MP_NO_ACTION;
11998 player->MovDir = (dx < 0 ? MV_LEFT :
11999 dx > 0 ? MV_RIGHT :
12001 dy > 0 ? MV_DOWN : MV_NONE);
12003 if (!IN_LEV_FIELD(new_jx, new_jy))
12004 return MP_NO_ACTION;
12006 if (!player_can_move)
12008 if (player->MovPos == 0)
12010 player->is_moving = FALSE;
12011 player->is_digging = FALSE;
12012 player->is_collecting = FALSE;
12013 player->is_snapping = FALSE;
12014 player->is_pushing = FALSE;
12018 if (!options.network && game.centered_player_nr == -1 &&
12019 !AllPlayersInSight(player, new_jx, new_jy))
12020 return MP_NO_ACTION;
12022 can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
12023 if (can_move != MP_MOVING)
12026 /* check if DigField() has caused relocation of the player */
12027 if (player->jx != jx || player->jy != jy)
12028 return MP_NO_ACTION; /* <-- !!! CHECK THIS [-> MP_ACTION ?] !!! */
12030 StorePlayer[jx][jy] = 0;
12031 player->last_jx = jx;
12032 player->last_jy = jy;
12033 player->jx = new_jx;
12034 player->jy = new_jy;
12035 StorePlayer[new_jx][new_jy] = player->element_nr;
12037 if (player->move_delay_value_next != -1)
12039 player->move_delay_value = player->move_delay_value_next;
12040 player->move_delay_value_next = -1;
12044 (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
12046 player->step_counter++;
12048 PlayerVisit[jx][jy] = FrameCounter;
12050 player->is_moving = TRUE;
12053 /* should better be called in MovePlayer(), but this breaks some tapes */
12054 ScrollPlayer(player, SCROLL_INIT);
12060 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
12062 int jx = player->jx, jy = player->jy;
12063 int old_jx = jx, old_jy = jy;
12064 int moved = MP_NO_ACTION;
12066 if (!player->active)
12071 if (player->MovPos == 0)
12073 player->is_moving = FALSE;
12074 player->is_digging = FALSE;
12075 player->is_collecting = FALSE;
12076 player->is_snapping = FALSE;
12077 player->is_pushing = FALSE;
12083 if (player->move_delay > 0)
12086 player->move_delay = -1; /* set to "uninitialized" value */
12088 /* store if player is automatically moved to next field */
12089 player->is_auto_moving = (player->programmed_action != MV_NONE);
12091 /* remove the last programmed player action */
12092 player->programmed_action = 0;
12094 if (player->MovPos)
12096 /* should only happen if pre-1.2 tape recordings are played */
12097 /* this is only for backward compatibility */
12099 int original_move_delay_value = player->move_delay_value;
12102 printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%d]\n",
12106 /* scroll remaining steps with finest movement resolution */
12107 player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
12109 while (player->MovPos)
12111 ScrollPlayer(player, SCROLL_GO_ON);
12112 ScrollScreen(NULL, SCROLL_GO_ON);
12114 AdvanceFrameAndPlayerCounters(player->index_nr);
12117 BackToFront_WithFrameDelay(0);
12120 player->move_delay_value = original_move_delay_value;
12123 player->is_active = FALSE;
12125 if (player->last_move_dir & MV_HORIZONTAL)
12127 if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
12128 moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
12132 if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
12133 moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
12136 if (!moved && !player->is_active)
12138 player->is_moving = FALSE;
12139 player->is_digging = FALSE;
12140 player->is_collecting = FALSE;
12141 player->is_snapping = FALSE;
12142 player->is_pushing = FALSE;
12148 if (moved & MP_MOVING && !ScreenMovPos &&
12149 (player->index_nr == game.centered_player_nr ||
12150 game.centered_player_nr == -1))
12152 int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
12153 int offset = game.scroll_delay_value;
12155 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12157 /* actual player has left the screen -- scroll in that direction */
12158 if (jx != old_jx) /* player has moved horizontally */
12159 scroll_x += (jx - old_jx);
12160 else /* player has moved vertically */
12161 scroll_y += (jy - old_jy);
12165 if (jx != old_jx) /* player has moved horizontally */
12167 if ((player->MovDir == MV_LEFT && scroll_x > jx - MIDPOSX + offset) ||
12168 (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
12169 scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
12171 /* don't scroll over playfield boundaries */
12172 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
12173 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
12175 /* don't scroll more than one field at a time */
12176 scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
12178 /* don't scroll against the player's moving direction */
12179 if ((player->MovDir == MV_LEFT && scroll_x > old_scroll_x) ||
12180 (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
12181 scroll_x = old_scroll_x;
12183 else /* player has moved vertically */
12185 if ((player->MovDir == MV_UP && scroll_y > jy - MIDPOSY + offset) ||
12186 (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
12187 scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
12189 /* don't scroll over playfield boundaries */
12190 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
12191 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
12193 /* don't scroll more than one field at a time */
12194 scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
12196 /* don't scroll against the player's moving direction */
12197 if ((player->MovDir == MV_UP && scroll_y > old_scroll_y) ||
12198 (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
12199 scroll_y = old_scroll_y;
12203 if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
12205 if (!options.network && game.centered_player_nr == -1 &&
12206 !AllPlayersInVisibleScreen())
12208 scroll_x = old_scroll_x;
12209 scroll_y = old_scroll_y;
12213 ScrollScreen(player, SCROLL_INIT);
12214 ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
12219 player->StepFrame = 0;
12221 if (moved & MP_MOVING)
12223 if (old_jx != jx && old_jy == jy)
12224 player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
12225 else if (old_jx == jx && old_jy != jy)
12226 player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
12228 TEST_DrawLevelField(jx, jy); /* for "crumbled sand" */
12230 player->last_move_dir = player->MovDir;
12231 player->is_moving = TRUE;
12232 player->is_snapping = FALSE;
12233 player->is_switching = FALSE;
12234 player->is_dropping = FALSE;
12235 player->is_dropping_pressed = FALSE;
12236 player->drop_pressed_delay = 0;
12239 /* should better be called here than above, but this breaks some tapes */
12240 ScrollPlayer(player, SCROLL_INIT);
12245 CheckGravityMovementWhenNotMoving(player);
12247 player->is_moving = FALSE;
12249 /* at this point, the player is allowed to move, but cannot move right now
12250 (e.g. because of something blocking the way) -- ensure that the player
12251 is also allowed to move in the next frame (in old versions before 3.1.1,
12252 the player was forced to wait again for eight frames before next try) */
12254 if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12255 player->move_delay = 0; /* allow direct movement in the next frame */
12258 if (player->move_delay == -1) /* not yet initialized by DigField() */
12259 player->move_delay = player->move_delay_value;
12261 if (game.engine_version < VERSION_IDENT(3,0,7,0))
12263 TestIfPlayerTouchesBadThing(jx, jy);
12264 TestIfPlayerTouchesCustomElement(jx, jy);
12267 if (!player->active)
12268 RemovePlayer(player);
12273 void ScrollPlayer(struct PlayerInfo *player, int mode)
12275 int jx = player->jx, jy = player->jy;
12276 int last_jx = player->last_jx, last_jy = player->last_jy;
12277 int move_stepsize = TILEX / player->move_delay_value;
12279 if (!player->active)
12282 if (player->MovPos == 0 && mode == SCROLL_GO_ON) /* player not moving */
12285 if (mode == SCROLL_INIT)
12287 player->actual_frame_counter = FrameCounter;
12288 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
12290 if ((player->block_last_field || player->block_delay_adjustment > 0) &&
12291 Feld[last_jx][last_jy] == EL_EMPTY)
12293 int last_field_block_delay = 0; /* start with no blocking at all */
12294 int block_delay_adjustment = player->block_delay_adjustment;
12296 /* if player blocks last field, add delay for exactly one move */
12297 if (player->block_last_field)
12299 last_field_block_delay += player->move_delay_value;
12301 /* when blocking enabled, prevent moving up despite gravity */
12302 if (player->gravity && player->MovDir == MV_UP)
12303 block_delay_adjustment = -1;
12306 /* add block delay adjustment (also possible when not blocking) */
12307 last_field_block_delay += block_delay_adjustment;
12309 Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
12310 MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
12313 if (player->MovPos != 0) /* player has not yet reached destination */
12316 else if (!FrameReached(&player->actual_frame_counter, 1))
12319 if (player->MovPos != 0)
12321 player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
12322 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
12324 /* before DrawPlayer() to draw correct player graphic for this case */
12325 if (player->MovPos == 0)
12326 CheckGravityMovement(player);
12329 if (player->MovPos == 0) /* player reached destination field */
12331 if (player->move_delay_reset_counter > 0)
12333 player->move_delay_reset_counter--;
12335 if (player->move_delay_reset_counter == 0)
12337 /* continue with normal speed after quickly moving through gate */
12338 HALVE_PLAYER_SPEED(player);
12340 /* be able to make the next move without delay */
12341 player->move_delay = 0;
12345 player->last_jx = jx;
12346 player->last_jy = jy;
12348 if (Feld[jx][jy] == EL_EXIT_OPEN ||
12349 Feld[jx][jy] == EL_EM_EXIT_OPEN ||
12350 Feld[jx][jy] == EL_EM_EXIT_OPENING ||
12351 Feld[jx][jy] == EL_STEEL_EXIT_OPEN ||
12352 Feld[jx][jy] == EL_EM_STEEL_EXIT_OPEN ||
12353 Feld[jx][jy] == EL_EM_STEEL_EXIT_OPENING ||
12354 Feld[jx][jy] == EL_SP_EXIT_OPEN ||
12355 Feld[jx][jy] == EL_SP_EXIT_OPENING) /* <-- special case */
12357 DrawPlayer(player); /* needed here only to cleanup last field */
12358 RemovePlayer(player);
12360 if (local_player->friends_still_needed == 0 ||
12361 IS_SP_ELEMENT(Feld[jx][jy]))
12362 PlayerWins(player);
12365 /* this breaks one level: "machine", level 000 */
12367 int move_direction = player->MovDir;
12368 int enter_side = MV_DIR_OPPOSITE(move_direction);
12369 int leave_side = move_direction;
12370 int old_jx = last_jx;
12371 int old_jy = last_jy;
12372 int old_element = Feld[old_jx][old_jy];
12373 int new_element = Feld[jx][jy];
12375 if (IS_CUSTOM_ELEMENT(old_element))
12376 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
12378 player->index_bit, leave_side);
12380 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
12381 CE_PLAYER_LEAVES_X,
12382 player->index_bit, leave_side);
12384 if (IS_CUSTOM_ELEMENT(new_element))
12385 CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
12386 player->index_bit, enter_side);
12388 CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
12389 CE_PLAYER_ENTERS_X,
12390 player->index_bit, enter_side);
12392 CheckTriggeredElementChangeBySide(jx, jy, player->initial_element,
12393 CE_MOVE_OF_X, move_direction);
12396 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
12398 TestIfPlayerTouchesBadThing(jx, jy);
12399 TestIfPlayerTouchesCustomElement(jx, jy);
12401 /* needed because pushed element has not yet reached its destination,
12402 so it would trigger a change event at its previous field location */
12403 if (!player->is_pushing)
12404 TestIfElementTouchesCustomElement(jx, jy); /* for empty space */
12406 if (!player->active)
12407 RemovePlayer(player);
12410 if (!local_player->LevelSolved && level.use_step_counter)
12420 if (TimeLeft <= 10 && setup.time_limit)
12421 PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
12423 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
12425 DisplayGameControlValues();
12427 if (!TimeLeft && setup.time_limit)
12428 for (i = 0; i < MAX_PLAYERS; i++)
12429 KillPlayer(&stored_player[i]);
12431 else if (game.no_time_limit && !AllPlayersGone) /* level w/o time limit */
12433 game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
12435 DisplayGameControlValues();
12439 if (tape.single_step && tape.recording && !tape.pausing &&
12440 !player->programmed_action)
12441 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
12443 if (!player->programmed_action)
12444 CheckSaveEngineSnapshot(player);
12448 void ScrollScreen(struct PlayerInfo *player, int mode)
12450 static unsigned int screen_frame_counter = 0;
12452 if (mode == SCROLL_INIT)
12454 /* set scrolling step size according to actual player's moving speed */
12455 ScrollStepSize = TILEX / player->move_delay_value;
12457 screen_frame_counter = FrameCounter;
12458 ScreenMovDir = player->MovDir;
12459 ScreenMovPos = player->MovPos;
12460 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
12463 else if (!FrameReached(&screen_frame_counter, 1))
12468 ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
12469 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
12470 redraw_mask |= REDRAW_FIELD;
12473 ScreenMovDir = MV_NONE;
12476 void TestIfPlayerTouchesCustomElement(int x, int y)
12478 static int xy[4][2] =
12485 static int trigger_sides[4][2] =
12487 /* center side border side */
12488 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
12489 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
12490 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
12491 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
12493 static int touch_dir[4] =
12495 MV_LEFT | MV_RIGHT,
12500 int center_element = Feld[x][y]; /* should always be non-moving! */
12503 for (i = 0; i < NUM_DIRECTIONS; i++)
12505 int xx = x + xy[i][0];
12506 int yy = y + xy[i][1];
12507 int center_side = trigger_sides[i][0];
12508 int border_side = trigger_sides[i][1];
12509 int border_element;
12511 if (!IN_LEV_FIELD(xx, yy))
12514 if (IS_PLAYER(x, y)) /* player found at center element */
12516 struct PlayerInfo *player = PLAYERINFO(x, y);
12518 if (game.engine_version < VERSION_IDENT(3,0,7,0))
12519 border_element = Feld[xx][yy]; /* may be moving! */
12520 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
12521 border_element = Feld[xx][yy];
12522 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
12523 border_element = MovingOrBlocked2Element(xx, yy);
12525 continue; /* center and border element do not touch */
12527 CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
12528 player->index_bit, border_side);
12529 CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
12530 CE_PLAYER_TOUCHES_X,
12531 player->index_bit, border_side);
12534 /* use player element that is initially defined in the level playfield,
12535 not the player element that corresponds to the runtime player number
12536 (example: a level that contains EL_PLAYER_3 as the only player would
12537 incorrectly give EL_PLAYER_1 for "player->element_nr") */
12538 int player_element = PLAYERINFO(x, y)->initial_element;
12540 CheckElementChangeBySide(xx, yy, border_element, player_element,
12541 CE_TOUCHING_X, border_side);
12544 else if (IS_PLAYER(xx, yy)) /* player found at border element */
12546 struct PlayerInfo *player = PLAYERINFO(xx, yy);
12548 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
12550 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
12551 continue; /* center and border element do not touch */
12554 CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
12555 player->index_bit, center_side);
12556 CheckTriggeredElementChangeByPlayer(x, y, center_element,
12557 CE_PLAYER_TOUCHES_X,
12558 player->index_bit, center_side);
12561 /* use player element that is initially defined in the level playfield,
12562 not the player element that corresponds to the runtime player number
12563 (example: a level that contains EL_PLAYER_3 as the only player would
12564 incorrectly give EL_PLAYER_1 for "player->element_nr") */
12565 int player_element = PLAYERINFO(xx, yy)->initial_element;
12567 CheckElementChangeBySide(x, y, center_element, player_element,
12568 CE_TOUCHING_X, center_side);
12576 void TestIfElementTouchesCustomElement(int x, int y)
12578 static int xy[4][2] =
12585 static int trigger_sides[4][2] =
12587 /* center side border side */
12588 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
12589 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
12590 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
12591 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
12593 static int touch_dir[4] =
12595 MV_LEFT | MV_RIGHT,
12600 boolean change_center_element = FALSE;
12601 int center_element = Feld[x][y]; /* should always be non-moving! */
12602 int border_element_old[NUM_DIRECTIONS];
12605 for (i = 0; i < NUM_DIRECTIONS; i++)
12607 int xx = x + xy[i][0];
12608 int yy = y + xy[i][1];
12609 int border_element;
12611 border_element_old[i] = -1;
12613 if (!IN_LEV_FIELD(xx, yy))
12616 if (game.engine_version < VERSION_IDENT(3,0,7,0))
12617 border_element = Feld[xx][yy]; /* may be moving! */
12618 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
12619 border_element = Feld[xx][yy];
12620 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
12621 border_element = MovingOrBlocked2Element(xx, yy);
12623 continue; /* center and border element do not touch */
12625 border_element_old[i] = border_element;
12628 for (i = 0; i < NUM_DIRECTIONS; i++)
12630 int xx = x + xy[i][0];
12631 int yy = y + xy[i][1];
12632 int center_side = trigger_sides[i][0];
12633 int border_element = border_element_old[i];
12635 if (border_element == -1)
12638 /* check for change of border element */
12639 CheckElementChangeBySide(xx, yy, border_element, center_element,
12640 CE_TOUCHING_X, center_side);
12642 /* (center element cannot be player, so we dont have to check this here) */
12645 for (i = 0; i < NUM_DIRECTIONS; i++)
12647 int xx = x + xy[i][0];
12648 int yy = y + xy[i][1];
12649 int border_side = trigger_sides[i][1];
12650 int border_element = border_element_old[i];
12652 if (border_element == -1)
12655 /* check for change of center element (but change it only once) */
12656 if (!change_center_element)
12657 change_center_element =
12658 CheckElementChangeBySide(x, y, center_element, border_element,
12659 CE_TOUCHING_X, border_side);
12661 if (IS_PLAYER(xx, yy))
12663 /* use player element that is initially defined in the level playfield,
12664 not the player element that corresponds to the runtime player number
12665 (example: a level that contains EL_PLAYER_3 as the only player would
12666 incorrectly give EL_PLAYER_1 for "player->element_nr") */
12667 int player_element = PLAYERINFO(xx, yy)->initial_element;
12669 CheckElementChangeBySide(x, y, center_element, player_element,
12670 CE_TOUCHING_X, border_side);
12675 void TestIfElementHitsCustomElement(int x, int y, int direction)
12677 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
12678 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
12679 int hitx = x + dx, hity = y + dy;
12680 int hitting_element = Feld[x][y];
12681 int touched_element;
12683 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
12686 touched_element = (IN_LEV_FIELD(hitx, hity) ?
12687 MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
12689 if (IN_LEV_FIELD(hitx, hity))
12691 int opposite_direction = MV_DIR_OPPOSITE(direction);
12692 int hitting_side = direction;
12693 int touched_side = opposite_direction;
12694 boolean object_hit = (!IS_MOVING(hitx, hity) ||
12695 MovDir[hitx][hity] != direction ||
12696 ABS(MovPos[hitx][hity]) <= TILEY / 2);
12702 CheckElementChangeBySide(x, y, hitting_element, touched_element,
12703 CE_HITTING_X, touched_side);
12705 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
12706 CE_HIT_BY_X, hitting_side);
12708 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
12709 CE_HIT_BY_SOMETHING, opposite_direction);
12711 if (IS_PLAYER(hitx, hity))
12713 /* use player element that is initially defined in the level playfield,
12714 not the player element that corresponds to the runtime player number
12715 (example: a level that contains EL_PLAYER_3 as the only player would
12716 incorrectly give EL_PLAYER_1 for "player->element_nr") */
12717 int player_element = PLAYERINFO(hitx, hity)->initial_element;
12719 CheckElementChangeBySide(x, y, hitting_element, player_element,
12720 CE_HITTING_X, touched_side);
12725 /* "hitting something" is also true when hitting the playfield border */
12726 CheckElementChangeBySide(x, y, hitting_element, touched_element,
12727 CE_HITTING_SOMETHING, direction);
12730 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
12732 int i, kill_x = -1, kill_y = -1;
12734 int bad_element = -1;
12735 static int test_xy[4][2] =
12742 static int test_dir[4] =
12750 for (i = 0; i < NUM_DIRECTIONS; i++)
12752 int test_x, test_y, test_move_dir, test_element;
12754 test_x = good_x + test_xy[i][0];
12755 test_y = good_y + test_xy[i][1];
12757 if (!IN_LEV_FIELD(test_x, test_y))
12761 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
12763 test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
12765 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
12766 2nd case: DONT_TOUCH style bad thing does not move away from good thing
12768 if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
12769 (DONT_TOUCH(test_element) && test_move_dir != test_dir[i]))
12773 bad_element = test_element;
12779 if (kill_x != -1 || kill_y != -1)
12781 if (IS_PLAYER(good_x, good_y))
12783 struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
12785 if (player->shield_deadly_time_left > 0 &&
12786 !IS_INDESTRUCTIBLE(bad_element))
12787 Bang(kill_x, kill_y);
12788 else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
12789 KillPlayer(player);
12792 Bang(good_x, good_y);
12796 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
12798 int i, kill_x = -1, kill_y = -1;
12799 int bad_element = Feld[bad_x][bad_y];
12800 static int test_xy[4][2] =
12807 static int touch_dir[4] =
12809 MV_LEFT | MV_RIGHT,
12814 static int test_dir[4] =
12822 if (bad_element == EL_EXPLOSION) /* skip just exploding bad things */
12825 for (i = 0; i < NUM_DIRECTIONS; i++)
12827 int test_x, test_y, test_move_dir, test_element;
12829 test_x = bad_x + test_xy[i][0];
12830 test_y = bad_y + test_xy[i][1];
12832 if (!IN_LEV_FIELD(test_x, test_y))
12836 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
12838 test_element = Feld[test_x][test_y];
12840 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
12841 2nd case: DONT_TOUCH style bad thing does not move away from good thing
12843 if ((DONT_RUN_INTO(bad_element) && bad_move_dir == test_dir[i]) ||
12844 (DONT_TOUCH(bad_element) && test_move_dir != test_dir[i]))
12846 /* good thing is player or penguin that does not move away */
12847 if (IS_PLAYER(test_x, test_y))
12849 struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
12851 if (bad_element == EL_ROBOT && player->is_moving)
12852 continue; /* robot does not kill player if he is moving */
12854 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
12856 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
12857 continue; /* center and border element do not touch */
12865 else if (test_element == EL_PENGUIN)
12875 if (kill_x != -1 || kill_y != -1)
12877 if (IS_PLAYER(kill_x, kill_y))
12879 struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
12881 if (player->shield_deadly_time_left > 0 &&
12882 !IS_INDESTRUCTIBLE(bad_element))
12883 Bang(bad_x, bad_y);
12884 else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
12885 KillPlayer(player);
12888 Bang(kill_x, kill_y);
12892 void TestIfGoodThingGetsHitByBadThing(int bad_x, int bad_y, int bad_move_dir)
12894 int bad_element = Feld[bad_x][bad_y];
12895 int dx = (bad_move_dir == MV_LEFT ? -1 : bad_move_dir == MV_RIGHT ? +1 : 0);
12896 int dy = (bad_move_dir == MV_UP ? -1 : bad_move_dir == MV_DOWN ? +1 : 0);
12897 int test_x = bad_x + dx, test_y = bad_y + dy;
12898 int test_move_dir, test_element;
12899 int kill_x = -1, kill_y = -1;
12901 if (!IN_LEV_FIELD(test_x, test_y))
12905 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
12907 test_element = Feld[test_x][test_y];
12909 if (test_move_dir != bad_move_dir)
12911 /* good thing can be player or penguin that does not move away */
12912 if (IS_PLAYER(test_x, test_y))
12914 struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
12916 /* (note: in comparison to DONT_RUN_TO and DONT_TOUCH, also handle the
12917 player as being hit when he is moving towards the bad thing, because
12918 the "get hit by" condition would be lost after the player stops) */
12919 if (player->MovPos != 0 && player->MovDir == bad_move_dir)
12920 return; /* player moves away from bad thing */
12925 else if (test_element == EL_PENGUIN)
12932 if (kill_x != -1 || kill_y != -1)
12934 if (IS_PLAYER(kill_x, kill_y))
12936 struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
12938 if (player->shield_deadly_time_left > 0 &&
12939 !IS_INDESTRUCTIBLE(bad_element))
12940 Bang(bad_x, bad_y);
12941 else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
12942 KillPlayer(player);
12945 Bang(kill_x, kill_y);
12949 void TestIfPlayerTouchesBadThing(int x, int y)
12951 TestIfGoodThingHitsBadThing(x, y, MV_NONE);
12954 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
12956 TestIfGoodThingHitsBadThing(x, y, move_dir);
12959 void TestIfBadThingTouchesPlayer(int x, int y)
12961 TestIfBadThingHitsGoodThing(x, y, MV_NONE);
12964 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
12966 TestIfBadThingHitsGoodThing(x, y, move_dir);
12969 void TestIfFriendTouchesBadThing(int x, int y)
12971 TestIfGoodThingHitsBadThing(x, y, MV_NONE);
12974 void TestIfBadThingTouchesFriend(int x, int y)
12976 TestIfBadThingHitsGoodThing(x, y, MV_NONE);
12979 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
12981 int i, kill_x = bad_x, kill_y = bad_y;
12982 static int xy[4][2] =
12990 for (i = 0; i < NUM_DIRECTIONS; i++)
12994 x = bad_x + xy[i][0];
12995 y = bad_y + xy[i][1];
12996 if (!IN_LEV_FIELD(x, y))
12999 element = Feld[x][y];
13000 if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
13001 element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
13009 if (kill_x != bad_x || kill_y != bad_y)
13010 Bang(bad_x, bad_y);
13013 void KillPlayer(struct PlayerInfo *player)
13015 int jx = player->jx, jy = player->jy;
13017 if (!player->active)
13021 printf("::: 0: killed == %d, active == %d, reanimated == %d\n",
13022 player->killed, player->active, player->reanimated);
13025 /* the following code was introduced to prevent an infinite loop when calling
13027 -> CheckTriggeredElementChangeExt()
13028 -> ExecuteCustomElementAction()
13030 -> (infinitely repeating the above sequence of function calls)
13031 which occurs when killing the player while having a CE with the setting
13032 "kill player X when explosion of <player X>"; the solution using a new
13033 field "player->killed" was chosen for backwards compatibility, although
13034 clever use of the fields "player->active" etc. would probably also work */
13036 if (player->killed)
13040 player->killed = TRUE;
13042 /* remove accessible field at the player's position */
13043 Feld[jx][jy] = EL_EMPTY;
13045 /* deactivate shield (else Bang()/Explode() would not work right) */
13046 player->shield_normal_time_left = 0;
13047 player->shield_deadly_time_left = 0;
13050 printf("::: 1: killed == %d, active == %d, reanimated == %d\n",
13051 player->killed, player->active, player->reanimated);
13057 printf("::: 2: killed == %d, active == %d, reanimated == %d\n",
13058 player->killed, player->active, player->reanimated);
13061 if (player->reanimated) /* killed player may have been reanimated */
13062 player->killed = player->reanimated = FALSE;
13064 BuryPlayer(player);
13067 static void KillPlayerUnlessEnemyProtected(int x, int y)
13069 if (!PLAYER_ENEMY_PROTECTED(x, y))
13070 KillPlayer(PLAYERINFO(x, y));
13073 static void KillPlayerUnlessExplosionProtected(int x, int y)
13075 if (!PLAYER_EXPLOSION_PROTECTED(x, y))
13076 KillPlayer(PLAYERINFO(x, y));
13079 void BuryPlayer(struct PlayerInfo *player)
13081 int jx = player->jx, jy = player->jy;
13083 if (!player->active)
13086 PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
13087 PlayLevelSound(jx, jy, SND_GAME_LOSING);
13089 player->GameOver = TRUE;
13090 RemovePlayer(player);
13093 void RemovePlayer(struct PlayerInfo *player)
13095 int jx = player->jx, jy = player->jy;
13096 int i, found = FALSE;
13098 player->present = FALSE;
13099 player->active = FALSE;
13101 if (!ExplodeField[jx][jy])
13102 StorePlayer[jx][jy] = 0;
13104 if (player->is_moving)
13105 TEST_DrawLevelField(player->last_jx, player->last_jy);
13107 for (i = 0; i < MAX_PLAYERS; i++)
13108 if (stored_player[i].active)
13112 AllPlayersGone = TRUE;
13118 static void setFieldForSnapping(int x, int y, int element, int direction)
13120 struct ElementInfo *ei = &element_info[element];
13121 int direction_bit = MV_DIR_TO_BIT(direction);
13122 int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
13123 int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
13124 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
13126 Feld[x][y] = EL_ELEMENT_SNAPPING;
13127 MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
13129 ResetGfxAnimation(x, y);
13131 GfxElement[x][y] = element;
13132 GfxAction[x][y] = action;
13133 GfxDir[x][y] = direction;
13134 GfxFrame[x][y] = -1;
13138 =============================================================================
13139 checkDiagonalPushing()
13140 -----------------------------------------------------------------------------
13141 check if diagonal input device direction results in pushing of object
13142 (by checking if the alternative direction is walkable, diggable, ...)
13143 =============================================================================
13146 static boolean checkDiagonalPushing(struct PlayerInfo *player,
13147 int x, int y, int real_dx, int real_dy)
13149 int jx, jy, dx, dy, xx, yy;
13151 if (real_dx == 0 || real_dy == 0) /* no diagonal direction => push */
13154 /* diagonal direction: check alternative direction */
13159 xx = jx + (dx == 0 ? real_dx : 0);
13160 yy = jy + (dy == 0 ? real_dy : 0);
13162 return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
13166 =============================================================================
13168 -----------------------------------------------------------------------------
13169 x, y: field next to player (non-diagonal) to try to dig to
13170 real_dx, real_dy: direction as read from input device (can be diagonal)
13171 =============================================================================
13174 static int DigField(struct PlayerInfo *player,
13175 int oldx, int oldy, int x, int y,
13176 int real_dx, int real_dy, int mode)
13178 boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
13179 boolean player_was_pushing = player->is_pushing;
13180 boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
13181 boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
13182 int jx = oldx, jy = oldy;
13183 int dx = x - jx, dy = y - jy;
13184 int nextx = x + dx, nexty = y + dy;
13185 int move_direction = (dx == -1 ? MV_LEFT :
13186 dx == +1 ? MV_RIGHT :
13188 dy == +1 ? MV_DOWN : MV_NONE);
13189 int opposite_direction = MV_DIR_OPPOSITE(move_direction);
13190 int dig_side = MV_DIR_OPPOSITE(move_direction);
13191 int old_element = Feld[jx][jy];
13192 int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
13195 if (is_player) /* function can also be called by EL_PENGUIN */
13197 if (player->MovPos == 0)
13199 player->is_digging = FALSE;
13200 player->is_collecting = FALSE;
13203 if (player->MovPos == 0) /* last pushing move finished */
13204 player->is_pushing = FALSE;
13206 if (mode == DF_NO_PUSH) /* player just stopped pushing */
13208 player->is_switching = FALSE;
13209 player->push_delay = -1;
13211 return MP_NO_ACTION;
13215 if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
13216 old_element = Back[jx][jy];
13218 /* in case of element dropped at player position, check background */
13219 else if (Back[jx][jy] != EL_EMPTY &&
13220 game.engine_version >= VERSION_IDENT(2,2,0,0))
13221 old_element = Back[jx][jy];
13223 if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
13224 return MP_NO_ACTION; /* field has no opening in this direction */
13226 if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
13227 return MP_NO_ACTION; /* field has no opening in this direction */
13229 if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
13233 Feld[jx][jy] = player->artwork_element;
13234 InitMovingField(jx, jy, MV_DOWN);
13235 Store[jx][jy] = EL_ACID;
13236 ContinueMoving(jx, jy);
13237 BuryPlayer(player);
13239 return MP_DONT_RUN_INTO;
13242 if (player_can_move && DONT_RUN_INTO(element))
13244 TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
13246 return MP_DONT_RUN_INTO;
13249 if (IS_MOVING(x, y) || IS_PLAYER(x, y))
13250 return MP_NO_ACTION;
13252 collect_count = element_info[element].collect_count_initial;
13254 if (!is_player && !IS_COLLECTIBLE(element)) /* penguin cannot collect it */
13255 return MP_NO_ACTION;
13257 if (game.engine_version < VERSION_IDENT(2,2,0,0))
13258 player_can_move = player_can_move_or_snap;
13260 if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
13261 game.engine_version >= VERSION_IDENT(2,2,0,0))
13263 CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
13264 player->index_bit, dig_side);
13265 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13266 player->index_bit, dig_side);
13268 if (element == EL_DC_LANDMINE)
13271 if (Feld[x][y] != element) /* field changed by snapping */
13274 return MP_NO_ACTION;
13277 if (player->gravity && is_player && !player->is_auto_moving &&
13278 canFallDown(player) && move_direction != MV_DOWN &&
13279 !canMoveToValidFieldWithGravity(jx, jy, move_direction))
13280 return MP_NO_ACTION; /* player cannot walk here due to gravity */
13282 if (player_can_move &&
13283 IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
13285 int sound_element = SND_ELEMENT(element);
13286 int sound_action = ACTION_WALKING;
13288 if (IS_RND_GATE(element))
13290 if (!player->key[RND_GATE_NR(element)])
13291 return MP_NO_ACTION;
13293 else if (IS_RND_GATE_GRAY(element))
13295 if (!player->key[RND_GATE_GRAY_NR(element)])
13296 return MP_NO_ACTION;
13298 else if (IS_RND_GATE_GRAY_ACTIVE(element))
13300 if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
13301 return MP_NO_ACTION;
13303 else if (element == EL_EXIT_OPEN ||
13304 element == EL_EM_EXIT_OPEN ||
13305 element == EL_EM_EXIT_OPENING ||
13306 element == EL_STEEL_EXIT_OPEN ||
13307 element == EL_EM_STEEL_EXIT_OPEN ||
13308 element == EL_EM_STEEL_EXIT_OPENING ||
13309 element == EL_SP_EXIT_OPEN ||
13310 element == EL_SP_EXIT_OPENING)
13312 sound_action = ACTION_PASSING; /* player is passing exit */
13314 else if (element == EL_EMPTY)
13316 sound_action = ACTION_MOVING; /* nothing to walk on */
13319 /* play sound from background or player, whatever is available */
13320 if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
13321 PlayLevelSoundElementAction(x, y, sound_element, sound_action);
13323 PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
13325 else if (player_can_move &&
13326 IS_PASSABLE(element) && canPassField(x, y, move_direction))
13328 if (!ACCESS_FROM(element, opposite_direction))
13329 return MP_NO_ACTION; /* field not accessible from this direction */
13331 if (CAN_MOVE(element)) /* only fixed elements can be passed! */
13332 return MP_NO_ACTION;
13334 if (IS_EM_GATE(element))
13336 if (!player->key[EM_GATE_NR(element)])
13337 return MP_NO_ACTION;
13339 else if (IS_EM_GATE_GRAY(element))
13341 if (!player->key[EM_GATE_GRAY_NR(element)])
13342 return MP_NO_ACTION;
13344 else if (IS_EM_GATE_GRAY_ACTIVE(element))
13346 if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
13347 return MP_NO_ACTION;
13349 else if (IS_EMC_GATE(element))
13351 if (!player->key[EMC_GATE_NR(element)])
13352 return MP_NO_ACTION;
13354 else if (IS_EMC_GATE_GRAY(element))
13356 if (!player->key[EMC_GATE_GRAY_NR(element)])
13357 return MP_NO_ACTION;
13359 else if (IS_EMC_GATE_GRAY_ACTIVE(element))
13361 if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
13362 return MP_NO_ACTION;
13364 else if (element == EL_DC_GATE_WHITE ||
13365 element == EL_DC_GATE_WHITE_GRAY ||
13366 element == EL_DC_GATE_WHITE_GRAY_ACTIVE)
13368 if (player->num_white_keys == 0)
13369 return MP_NO_ACTION;
13371 player->num_white_keys--;
13373 else if (IS_SP_PORT(element))
13375 if (element == EL_SP_GRAVITY_PORT_LEFT ||
13376 element == EL_SP_GRAVITY_PORT_RIGHT ||
13377 element == EL_SP_GRAVITY_PORT_UP ||
13378 element == EL_SP_GRAVITY_PORT_DOWN)
13379 player->gravity = !player->gravity;
13380 else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
13381 element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
13382 element == EL_SP_GRAVITY_ON_PORT_UP ||
13383 element == EL_SP_GRAVITY_ON_PORT_DOWN)
13384 player->gravity = TRUE;
13385 else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
13386 element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
13387 element == EL_SP_GRAVITY_OFF_PORT_UP ||
13388 element == EL_SP_GRAVITY_OFF_PORT_DOWN)
13389 player->gravity = FALSE;
13392 /* automatically move to the next field with double speed */
13393 player->programmed_action = move_direction;
13395 if (player->move_delay_reset_counter == 0)
13397 player->move_delay_reset_counter = 2; /* two double speed steps */
13399 DOUBLE_PLAYER_SPEED(player);
13402 PlayLevelSoundAction(x, y, ACTION_PASSING);
13404 else if (player_can_move_or_snap && IS_DIGGABLE(element))
13408 if (mode != DF_SNAP)
13410 GfxElement[x][y] = GFX_ELEMENT(element);
13411 player->is_digging = TRUE;
13414 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
13416 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
13417 player->index_bit, dig_side);
13419 if (mode == DF_SNAP)
13421 if (level.block_snap_field)
13422 setFieldForSnapping(x, y, element, move_direction);
13424 TestIfElementTouchesCustomElement(x, y); /* for empty space */
13426 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13427 player->index_bit, dig_side);
13430 else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
13434 if (is_player && mode != DF_SNAP)
13436 GfxElement[x][y] = element;
13437 player->is_collecting = TRUE;
13440 if (element == EL_SPEED_PILL)
13442 player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
13444 else if (element == EL_EXTRA_TIME && level.time > 0)
13446 TimeLeft += level.extra_time;
13448 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
13450 DisplayGameControlValues();
13452 else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
13454 player->shield_normal_time_left += level.shield_normal_time;
13455 if (element == EL_SHIELD_DEADLY)
13456 player->shield_deadly_time_left += level.shield_deadly_time;
13458 else if (element == EL_DYNAMITE ||
13459 element == EL_EM_DYNAMITE ||
13460 element == EL_SP_DISK_RED)
13462 if (player->inventory_size < MAX_INVENTORY_SIZE)
13463 player->inventory_element[player->inventory_size++] = element;
13465 DrawGameDoorValues();
13467 else if (element == EL_DYNABOMB_INCREASE_NUMBER)
13469 player->dynabomb_count++;
13470 player->dynabombs_left++;
13472 else if (element == EL_DYNABOMB_INCREASE_SIZE)
13474 player->dynabomb_size++;
13476 else if (element == EL_DYNABOMB_INCREASE_POWER)
13478 player->dynabomb_xl = TRUE;
13480 else if (IS_KEY(element))
13482 player->key[KEY_NR(element)] = TRUE;
13484 DrawGameDoorValues();
13486 else if (element == EL_DC_KEY_WHITE)
13488 player->num_white_keys++;
13490 /* display white keys? */
13491 /* DrawGameDoorValues(); */
13493 else if (IS_ENVELOPE(element))
13495 player->show_envelope = element;
13497 else if (element == EL_EMC_LENSES)
13499 game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
13501 RedrawAllInvisibleElementsForLenses();
13503 else if (element == EL_EMC_MAGNIFIER)
13505 game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
13507 RedrawAllInvisibleElementsForMagnifier();
13509 else if (IS_DROPPABLE(element) ||
13510 IS_THROWABLE(element)) /* can be collected and dropped */
13514 if (collect_count == 0)
13515 player->inventory_infinite_element = element;
13517 for (i = 0; i < collect_count; i++)
13518 if (player->inventory_size < MAX_INVENTORY_SIZE)
13519 player->inventory_element[player->inventory_size++] = element;
13521 DrawGameDoorValues();
13523 else if (collect_count > 0)
13525 local_player->gems_still_needed -= collect_count;
13526 if (local_player->gems_still_needed < 0)
13527 local_player->gems_still_needed = 0;
13529 game.snapshot.collected_item = TRUE;
13531 game_panel_controls[GAME_PANEL_GEMS].value = local_player->gems_still_needed;
13533 DisplayGameControlValues();
13536 RaiseScoreElement(element);
13537 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
13540 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
13541 player->index_bit, dig_side);
13543 if (mode == DF_SNAP)
13545 if (level.block_snap_field)
13546 setFieldForSnapping(x, y, element, move_direction);
13548 TestIfElementTouchesCustomElement(x, y); /* for empty space */
13550 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13551 player->index_bit, dig_side);
13554 else if (player_can_move_or_snap && IS_PUSHABLE(element))
13556 if (mode == DF_SNAP && element != EL_BD_ROCK)
13557 return MP_NO_ACTION;
13559 if (CAN_FALL(element) && dy)
13560 return MP_NO_ACTION;
13562 if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
13563 !(element == EL_SPRING && level.use_spring_bug))
13564 return MP_NO_ACTION;
13566 if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
13567 ((move_direction & MV_VERTICAL &&
13568 ((element_info[element].move_pattern & MV_LEFT &&
13569 IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
13570 (element_info[element].move_pattern & MV_RIGHT &&
13571 IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
13572 (move_direction & MV_HORIZONTAL &&
13573 ((element_info[element].move_pattern & MV_UP &&
13574 IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
13575 (element_info[element].move_pattern & MV_DOWN &&
13576 IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
13577 return MP_NO_ACTION;
13579 /* do not push elements already moving away faster than player */
13580 if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
13581 ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
13582 return MP_NO_ACTION;
13584 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
13586 if (player->push_delay_value == -1 || !player_was_pushing)
13587 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13589 else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
13591 if (player->push_delay_value == -1)
13592 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13594 else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
13596 if (!player->is_pushing)
13597 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13600 player->is_pushing = TRUE;
13601 player->is_active = TRUE;
13603 if (!(IN_LEV_FIELD(nextx, nexty) &&
13604 (IS_FREE(nextx, nexty) ||
13605 (IS_SB_ELEMENT(element) &&
13606 Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY) ||
13607 (IS_CUSTOM_ELEMENT(element) &&
13608 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty)))))
13609 return MP_NO_ACTION;
13611 if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
13612 return MP_NO_ACTION;
13614 if (player->push_delay == -1) /* new pushing; restart delay */
13615 player->push_delay = 0;
13617 if (player->push_delay < player->push_delay_value &&
13618 !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
13619 element != EL_SPRING && element != EL_BALLOON)
13621 /* make sure that there is no move delay before next try to push */
13622 if (game.engine_version >= VERSION_IDENT(3,0,7,1))
13623 player->move_delay = 0;
13625 return MP_NO_ACTION;
13628 if (IS_CUSTOM_ELEMENT(element) &&
13629 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty))
13631 if (!DigFieldByCE(nextx, nexty, element))
13632 return MP_NO_ACTION;
13635 if (IS_SB_ELEMENT(element))
13637 if (element == EL_SOKOBAN_FIELD_FULL)
13639 Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
13640 local_player->sokobanfields_still_needed++;
13643 if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
13645 Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
13646 local_player->sokobanfields_still_needed--;
13649 Feld[x][y] = EL_SOKOBAN_OBJECT;
13651 if (Back[x][y] == Back[nextx][nexty])
13652 PlayLevelSoundAction(x, y, ACTION_PUSHING);
13653 else if (Back[x][y] != 0)
13654 PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
13657 PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
13660 if (local_player->sokobanfields_still_needed == 0 &&
13661 (game.emulation == EMU_SOKOBAN || level.auto_exit_sokoban))
13663 PlayerWins(player);
13665 PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
13669 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
13671 InitMovingField(x, y, move_direction);
13672 GfxAction[x][y] = ACTION_PUSHING;
13674 if (mode == DF_SNAP)
13675 ContinueMoving(x, y);
13677 MovPos[x][y] = (dx != 0 ? dx : dy);
13679 Pushed[x][y] = TRUE;
13680 Pushed[nextx][nexty] = TRUE;
13682 if (game.engine_version < VERSION_IDENT(2,2,0,7))
13683 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13685 player->push_delay_value = -1; /* get new value later */
13687 /* check for element change _after_ element has been pushed */
13688 if (game.use_change_when_pushing_bug)
13690 CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
13691 player->index_bit, dig_side);
13692 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
13693 player->index_bit, dig_side);
13696 else if (IS_SWITCHABLE(element))
13698 if (PLAYER_SWITCHING(player, x, y))
13700 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
13701 player->index_bit, dig_side);
13706 player->is_switching = TRUE;
13707 player->switch_x = x;
13708 player->switch_y = y;
13710 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
13712 if (element == EL_ROBOT_WHEEL)
13714 Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
13718 game.robot_wheel_active = TRUE;
13720 TEST_DrawLevelField(x, y);
13722 else if (element == EL_SP_TERMINAL)
13726 SCAN_PLAYFIELD(xx, yy)
13728 if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
13732 else if (Feld[xx][yy] == EL_SP_TERMINAL)
13734 Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
13736 ResetGfxAnimation(xx, yy);
13737 TEST_DrawLevelField(xx, yy);
13741 else if (IS_BELT_SWITCH(element))
13743 ToggleBeltSwitch(x, y);
13745 else if (element == EL_SWITCHGATE_SWITCH_UP ||
13746 element == EL_SWITCHGATE_SWITCH_DOWN ||
13747 element == EL_DC_SWITCHGATE_SWITCH_UP ||
13748 element == EL_DC_SWITCHGATE_SWITCH_DOWN)
13750 ToggleSwitchgateSwitch(x, y);
13752 else if (element == EL_LIGHT_SWITCH ||
13753 element == EL_LIGHT_SWITCH_ACTIVE)
13755 ToggleLightSwitch(x, y);
13757 else if (element == EL_TIMEGATE_SWITCH ||
13758 element == EL_DC_TIMEGATE_SWITCH)
13760 ActivateTimegateSwitch(x, y);
13762 else if (element == EL_BALLOON_SWITCH_LEFT ||
13763 element == EL_BALLOON_SWITCH_RIGHT ||
13764 element == EL_BALLOON_SWITCH_UP ||
13765 element == EL_BALLOON_SWITCH_DOWN ||
13766 element == EL_BALLOON_SWITCH_NONE ||
13767 element == EL_BALLOON_SWITCH_ANY)
13769 game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT ? MV_LEFT :
13770 element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
13771 element == EL_BALLOON_SWITCH_UP ? MV_UP :
13772 element == EL_BALLOON_SWITCH_DOWN ? MV_DOWN :
13773 element == EL_BALLOON_SWITCH_NONE ? MV_NONE :
13776 else if (element == EL_LAMP)
13778 Feld[x][y] = EL_LAMP_ACTIVE;
13779 local_player->lights_still_needed--;
13781 ResetGfxAnimation(x, y);
13782 TEST_DrawLevelField(x, y);
13784 else if (element == EL_TIME_ORB_FULL)
13786 Feld[x][y] = EL_TIME_ORB_EMPTY;
13788 if (level.time > 0 || level.use_time_orb_bug)
13790 TimeLeft += level.time_orb_time;
13791 game.no_time_limit = FALSE;
13793 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
13795 DisplayGameControlValues();
13798 ResetGfxAnimation(x, y);
13799 TEST_DrawLevelField(x, y);
13801 else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
13802 element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
13806 game.ball_state = !game.ball_state;
13808 SCAN_PLAYFIELD(xx, yy)
13810 int e = Feld[xx][yy];
13812 if (game.ball_state)
13814 if (e == EL_EMC_MAGIC_BALL)
13815 CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
13816 else if (e == EL_EMC_MAGIC_BALL_SWITCH)
13817 CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
13821 if (e == EL_EMC_MAGIC_BALL_ACTIVE)
13822 CreateField(xx, yy, EL_EMC_MAGIC_BALL);
13823 else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
13824 CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
13829 CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
13830 player->index_bit, dig_side);
13832 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
13833 player->index_bit, dig_side);
13835 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
13836 player->index_bit, dig_side);
13842 if (!PLAYER_SWITCHING(player, x, y))
13844 player->is_switching = TRUE;
13845 player->switch_x = x;
13846 player->switch_y = y;
13848 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
13849 player->index_bit, dig_side);
13850 CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
13851 player->index_bit, dig_side);
13853 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
13854 player->index_bit, dig_side);
13855 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
13856 player->index_bit, dig_side);
13859 CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
13860 player->index_bit, dig_side);
13861 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
13862 player->index_bit, dig_side);
13864 return MP_NO_ACTION;
13867 player->push_delay = -1;
13869 if (is_player) /* function can also be called by EL_PENGUIN */
13871 if (Feld[x][y] != element) /* really digged/collected something */
13873 player->is_collecting = !player->is_digging;
13874 player->is_active = TRUE;
13881 static boolean DigFieldByCE(int x, int y, int digging_element)
13883 int element = Feld[x][y];
13885 if (!IS_FREE(x, y))
13887 int action = (IS_DIGGABLE(element) ? ACTION_DIGGING :
13888 IS_COLLECTIBLE(element) ? ACTION_COLLECTING :
13891 /* no element can dig solid indestructible elements */
13892 if (IS_INDESTRUCTIBLE(element) &&
13893 !IS_DIGGABLE(element) &&
13894 !IS_COLLECTIBLE(element))
13897 if (AmoebaNr[x][y] &&
13898 (element == EL_AMOEBA_FULL ||
13899 element == EL_BD_AMOEBA ||
13900 element == EL_AMOEBA_GROWING))
13902 AmoebaCnt[AmoebaNr[x][y]]--;
13903 AmoebaCnt2[AmoebaNr[x][y]]--;
13906 if (IS_MOVING(x, y))
13907 RemoveMovingField(x, y);
13911 TEST_DrawLevelField(x, y);
13914 /* if digged element was about to explode, prevent the explosion */
13915 ExplodeField[x][y] = EX_TYPE_NONE;
13917 PlayLevelSoundAction(x, y, action);
13920 Store[x][y] = EL_EMPTY;
13922 /* this makes it possible to leave the removed element again */
13923 if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element)))
13924 Store[x][y] = element;
13929 static boolean SnapField(struct PlayerInfo *player, int dx, int dy)
13931 int jx = player->jx, jy = player->jy;
13932 int x = jx + dx, y = jy + dy;
13933 int snap_direction = (dx == -1 ? MV_LEFT :
13934 dx == +1 ? MV_RIGHT :
13936 dy == +1 ? MV_DOWN : MV_NONE);
13937 boolean can_continue_snapping = (level.continuous_snapping &&
13938 WasJustFalling[x][y] < CHECK_DELAY_FALLING);
13940 if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
13943 if (!player->active || !IN_LEV_FIELD(x, y))
13951 if (player->MovPos == 0)
13952 player->is_pushing = FALSE;
13954 player->is_snapping = FALSE;
13956 if (player->MovPos == 0)
13958 player->is_moving = FALSE;
13959 player->is_digging = FALSE;
13960 player->is_collecting = FALSE;
13966 /* prevent snapping with already pressed snap key when not allowed */
13967 if (player->is_snapping && !can_continue_snapping)
13970 player->MovDir = snap_direction;
13972 if (player->MovPos == 0)
13974 player->is_moving = FALSE;
13975 player->is_digging = FALSE;
13976 player->is_collecting = FALSE;
13979 player->is_dropping = FALSE;
13980 player->is_dropping_pressed = FALSE;
13981 player->drop_pressed_delay = 0;
13983 if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
13986 player->is_snapping = TRUE;
13987 player->is_active = TRUE;
13989 if (player->MovPos == 0)
13991 player->is_moving = FALSE;
13992 player->is_digging = FALSE;
13993 player->is_collecting = FALSE;
13996 if (player->MovPos != 0) /* prevent graphic bugs in versions < 2.2.0 */
13997 TEST_DrawLevelField(player->last_jx, player->last_jy);
13999 TEST_DrawLevelField(x, y);
14004 static boolean DropElement(struct PlayerInfo *player)
14006 int old_element, new_element;
14007 int dropx = player->jx, dropy = player->jy;
14008 int drop_direction = player->MovDir;
14009 int drop_side = drop_direction;
14010 int drop_element = get_next_dropped_element(player);
14012 /* do not drop an element on top of another element; when holding drop key
14013 pressed without moving, dropped element must move away before the next
14014 element can be dropped (this is especially important if the next element
14015 is dynamite, which can be placed on background for historical reasons) */
14016 if (PLAYER_DROPPING(player, dropx, dropy) && Feld[dropx][dropy] != EL_EMPTY)
14019 if (IS_THROWABLE(drop_element))
14021 dropx += GET_DX_FROM_DIR(drop_direction);
14022 dropy += GET_DY_FROM_DIR(drop_direction);
14024 if (!IN_LEV_FIELD(dropx, dropy))
14028 old_element = Feld[dropx][dropy]; /* old element at dropping position */
14029 new_element = drop_element; /* default: no change when dropping */
14031 /* check if player is active, not moving and ready to drop */
14032 if (!player->active || player->MovPos || player->drop_delay > 0)
14035 /* check if player has anything that can be dropped */
14036 if (new_element == EL_UNDEFINED)
14039 /* only set if player has anything that can be dropped */
14040 player->is_dropping_pressed = TRUE;
14042 /* check if drop key was pressed long enough for EM style dynamite */
14043 if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
14046 /* check if anything can be dropped at the current position */
14047 if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
14050 /* collected custom elements can only be dropped on empty fields */
14051 if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
14054 if (old_element != EL_EMPTY)
14055 Back[dropx][dropy] = old_element; /* store old element on this field */
14057 ResetGfxAnimation(dropx, dropy);
14058 ResetRandomAnimationValue(dropx, dropy);
14060 if (player->inventory_size > 0 ||
14061 player->inventory_infinite_element != EL_UNDEFINED)
14063 if (player->inventory_size > 0)
14065 player->inventory_size--;
14067 DrawGameDoorValues();
14069 if (new_element == EL_DYNAMITE)
14070 new_element = EL_DYNAMITE_ACTIVE;
14071 else if (new_element == EL_EM_DYNAMITE)
14072 new_element = EL_EM_DYNAMITE_ACTIVE;
14073 else if (new_element == EL_SP_DISK_RED)
14074 new_element = EL_SP_DISK_RED_ACTIVE;
14077 Feld[dropx][dropy] = new_element;
14079 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
14080 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
14081 el2img(Feld[dropx][dropy]), 0);
14083 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
14085 /* needed if previous element just changed to "empty" in the last frame */
14086 ChangeCount[dropx][dropy] = 0; /* allow at least one more change */
14088 CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
14089 player->index_bit, drop_side);
14090 CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
14092 player->index_bit, drop_side);
14094 TestIfElementTouchesCustomElement(dropx, dropy);
14096 else /* player is dropping a dyna bomb */
14098 player->dynabombs_left--;
14100 Feld[dropx][dropy] = new_element;
14102 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
14103 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
14104 el2img(Feld[dropx][dropy]), 0);
14106 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
14109 if (Feld[dropx][dropy] == new_element) /* uninitialized unless CE change */
14110 InitField_WithBug1(dropx, dropy, FALSE);
14112 new_element = Feld[dropx][dropy]; /* element might have changed */
14114 if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
14115 element_info[new_element].move_pattern == MV_WHEN_DROPPED)
14117 if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
14118 MovDir[dropx][dropy] = drop_direction;
14120 ChangeCount[dropx][dropy] = 0; /* allow at least one more change */
14122 /* do not cause impact style collision by dropping elements that can fall */
14123 CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
14126 player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
14127 player->is_dropping = TRUE;
14129 player->drop_pressed_delay = 0;
14130 player->is_dropping_pressed = FALSE;
14132 player->drop_x = dropx;
14133 player->drop_y = dropy;
14138 /* ------------------------------------------------------------------------- */
14139 /* game sound playing functions */
14140 /* ------------------------------------------------------------------------- */
14142 static int *loop_sound_frame = NULL;
14143 static int *loop_sound_volume = NULL;
14145 void InitPlayLevelSound()
14147 int num_sounds = getSoundListSize();
14149 checked_free(loop_sound_frame);
14150 checked_free(loop_sound_volume);
14152 loop_sound_frame = checked_calloc(num_sounds * sizeof(int));
14153 loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
14156 static void PlayLevelSound(int x, int y, int nr)
14158 int sx = SCREENX(x), sy = SCREENY(y);
14159 int volume, stereo_position;
14160 int max_distance = 8;
14161 int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
14163 if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
14164 (!setup.sound_loops && IS_LOOP_SOUND(nr)))
14167 if (!IN_LEV_FIELD(x, y) ||
14168 sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
14169 sy < -max_distance || sy >= SCR_FIELDY + max_distance)
14172 volume = SOUND_MAX_VOLUME;
14174 if (!IN_SCR_FIELD(sx, sy))
14176 int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
14177 int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
14179 volume -= volume * (dx > dy ? dx : dy) / max_distance;
14182 stereo_position = (SOUND_MAX_LEFT +
14183 (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
14184 (SCR_FIELDX + 2 * max_distance));
14186 if (IS_LOOP_SOUND(nr))
14188 /* This assures that quieter loop sounds do not overwrite louder ones,
14189 while restarting sound volume comparison with each new game frame. */
14191 if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
14194 loop_sound_volume[nr] = volume;
14195 loop_sound_frame[nr] = FrameCounter;
14198 PlaySoundExt(nr, volume, stereo_position, type);
14201 static void PlayLevelSoundNearest(int x, int y, int sound_action)
14203 PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
14204 x > LEVELX(BX2) ? LEVELX(BX2) : x,
14205 y < LEVELY(BY1) ? LEVELY(BY1) :
14206 y > LEVELY(BY2) ? LEVELY(BY2) : y,
14210 static void PlayLevelSoundAction(int x, int y, int action)
14212 PlayLevelSoundElementAction(x, y, Feld[x][y], action);
14215 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
14217 int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
14219 if (sound_effect != SND_UNDEFINED)
14220 PlayLevelSound(x, y, sound_effect);
14223 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
14226 int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
14228 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14229 PlayLevelSound(x, y, sound_effect);
14232 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
14234 int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
14236 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14237 PlayLevelSound(x, y, sound_effect);
14240 static void StopLevelSoundActionIfLoop(int x, int y, int action)
14242 int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
14244 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14245 StopSound(sound_effect);
14248 static int getLevelMusicNr()
14250 if (levelset.music[level_nr] != MUS_UNDEFINED)
14251 return levelset.music[level_nr]; /* from config file */
14253 return MAP_NOCONF_MUSIC(level_nr); /* from music dir */
14256 static void FadeLevelSounds()
14261 static void FadeLevelMusic()
14263 int music_nr = getLevelMusicNr();
14264 char *curr_music = getCurrentlyPlayingMusicFilename();
14265 char *next_music = getMusicInfoEntryFilename(music_nr);
14267 if (!strEqual(curr_music, next_music))
14271 void FadeLevelSoundsAndMusic()
14277 static void PlayLevelMusic()
14279 int music_nr = getLevelMusicNr();
14280 char *curr_music = getCurrentlyPlayingMusicFilename();
14281 char *next_music = getMusicInfoEntryFilename(music_nr);
14283 if (!strEqual(curr_music, next_music))
14284 PlayMusic(music_nr);
14287 void PlayLevelSound_EM(int xx, int yy, int element_em, int sample)
14289 int element = (element_em > -1 ? map_element_EM_to_RND(element_em) : 0);
14290 int offset = (BorderElement == EL_STEELWALL ? 1 : 0);
14291 int x = xx - 1 - offset;
14292 int y = yy - 1 - offset;
14297 PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
14301 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14305 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14309 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14313 PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
14317 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14321 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14324 case SAMPLE_android_clone:
14325 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14328 case SAMPLE_android_move:
14329 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14332 case SAMPLE_spring:
14333 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14337 PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
14341 PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
14344 case SAMPLE_eater_eat:
14345 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14349 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14352 case SAMPLE_collect:
14353 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
14356 case SAMPLE_diamond:
14357 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14360 case SAMPLE_squash:
14361 /* !!! CHECK THIS !!! */
14363 PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
14365 PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
14369 case SAMPLE_wonderfall:
14370 PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
14374 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14378 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14382 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14386 PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
14390 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14394 PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
14397 case SAMPLE_wonder:
14398 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14402 PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
14405 case SAMPLE_exit_open:
14406 PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
14409 case SAMPLE_exit_leave:
14410 PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
14413 case SAMPLE_dynamite:
14414 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14418 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14422 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
14426 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14430 PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
14434 PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
14438 PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
14442 PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
14447 void PlayLevelSound_SP(int xx, int yy, int element_sp, int action_sp)
14449 int element = map_element_SP_to_RND(element_sp);
14450 int action = map_action_SP_to_RND(action_sp);
14451 int offset = (setup.sp_show_border_elements ? 0 : 1);
14452 int x = xx - offset;
14453 int y = yy - offset;
14455 PlayLevelSoundElementAction(x, y, element, action);
14458 void RaiseScore(int value)
14460 local_player->score += value;
14462 game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
14464 DisplayGameControlValues();
14467 void RaiseScoreElement(int element)
14472 case EL_BD_DIAMOND:
14473 case EL_EMERALD_YELLOW:
14474 case EL_EMERALD_RED:
14475 case EL_EMERALD_PURPLE:
14476 case EL_SP_INFOTRON:
14477 RaiseScore(level.score[SC_EMERALD]);
14480 RaiseScore(level.score[SC_DIAMOND]);
14483 RaiseScore(level.score[SC_CRYSTAL]);
14486 RaiseScore(level.score[SC_PEARL]);
14489 case EL_BD_BUTTERFLY:
14490 case EL_SP_ELECTRON:
14491 RaiseScore(level.score[SC_BUG]);
14494 case EL_BD_FIREFLY:
14495 case EL_SP_SNIKSNAK:
14496 RaiseScore(level.score[SC_SPACESHIP]);
14499 case EL_DARK_YAMYAM:
14500 RaiseScore(level.score[SC_YAMYAM]);
14503 RaiseScore(level.score[SC_ROBOT]);
14506 RaiseScore(level.score[SC_PACMAN]);
14509 RaiseScore(level.score[SC_NUT]);
14512 case EL_EM_DYNAMITE:
14513 case EL_SP_DISK_RED:
14514 case EL_DYNABOMB_INCREASE_NUMBER:
14515 case EL_DYNABOMB_INCREASE_SIZE:
14516 case EL_DYNABOMB_INCREASE_POWER:
14517 RaiseScore(level.score[SC_DYNAMITE]);
14519 case EL_SHIELD_NORMAL:
14520 case EL_SHIELD_DEADLY:
14521 RaiseScore(level.score[SC_SHIELD]);
14523 case EL_EXTRA_TIME:
14524 RaiseScore(level.extra_time_score);
14538 case EL_DC_KEY_WHITE:
14539 RaiseScore(level.score[SC_KEY]);
14542 RaiseScore(element_info[element].collect_score);
14547 void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message)
14549 if (skip_request || Request(message, REQ_ASK | REQ_STAY_CLOSED))
14551 /* closing door required in case of envelope style request dialogs */
14553 CloseDoor(DOOR_CLOSE_1);
14555 #if defined(NETWORK_AVALIABLE)
14556 if (options.network)
14557 SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
14562 FadeSkipNextFadeIn();
14564 SetGameStatus(GAME_MODE_MAIN);
14569 else /* continue playing the game */
14571 if (tape.playing && tape.deactivate_display)
14572 TapeDeactivateDisplayOff(TRUE);
14574 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
14576 if (tape.playing && tape.deactivate_display)
14577 TapeDeactivateDisplayOn();
14581 void RequestQuitGame(boolean ask_if_really_quit)
14583 boolean quick_quit = (!ask_if_really_quit || level_editor_test_game);
14584 boolean skip_request = AllPlayersGone || quick_quit;
14586 RequestQuitGameExt(skip_request, quick_quit,
14587 "Do you really want to quit the game?");
14591 /* ------------------------------------------------------------------------- */
14592 /* random generator functions */
14593 /* ------------------------------------------------------------------------- */
14595 unsigned int InitEngineRandom_RND(int seed)
14597 game.num_random_calls = 0;
14599 return InitEngineRandom(seed);
14602 unsigned int RND(int max)
14606 game.num_random_calls++;
14608 return GetEngineRandom(max);
14615 /* ------------------------------------------------------------------------- */
14616 /* game engine snapshot handling functions */
14617 /* ------------------------------------------------------------------------- */
14619 struct EngineSnapshotInfo
14621 /* runtime values for custom element collect score */
14622 int collect_score[NUM_CUSTOM_ELEMENTS];
14624 /* runtime values for group element choice position */
14625 int choice_pos[NUM_GROUP_ELEMENTS];
14627 /* runtime values for belt position animations */
14628 int belt_graphic[4][NUM_BELT_PARTS];
14629 int belt_anim_mode[4][NUM_BELT_PARTS];
14632 static struct EngineSnapshotInfo engine_snapshot_rnd;
14633 static char *snapshot_level_identifier = NULL;
14634 static int snapshot_level_nr = -1;
14636 static void SaveEngineSnapshotValues_RND()
14638 static int belt_base_active_element[4] =
14640 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
14641 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
14642 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
14643 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
14647 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
14649 int element = EL_CUSTOM_START + i;
14651 engine_snapshot_rnd.collect_score[i] = element_info[element].collect_score;
14654 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
14656 int element = EL_GROUP_START + i;
14658 engine_snapshot_rnd.choice_pos[i] = element_info[element].group->choice_pos;
14661 for (i = 0; i < 4; i++)
14663 for (j = 0; j < NUM_BELT_PARTS; j++)
14665 int element = belt_base_active_element[i] + j;
14666 int graphic = el2img(element);
14667 int anim_mode = graphic_info[graphic].anim_mode;
14669 engine_snapshot_rnd.belt_graphic[i][j] = graphic;
14670 engine_snapshot_rnd.belt_anim_mode[i][j] = anim_mode;
14675 static void LoadEngineSnapshotValues_RND()
14677 unsigned int num_random_calls = game.num_random_calls;
14680 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
14682 int element = EL_CUSTOM_START + i;
14684 element_info[element].collect_score = engine_snapshot_rnd.collect_score[i];
14687 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
14689 int element = EL_GROUP_START + i;
14691 element_info[element].group->choice_pos = engine_snapshot_rnd.choice_pos[i];
14694 for (i = 0; i < 4; i++)
14696 for (j = 0; j < NUM_BELT_PARTS; j++)
14698 int graphic = engine_snapshot_rnd.belt_graphic[i][j];
14699 int anim_mode = engine_snapshot_rnd.belt_anim_mode[i][j];
14701 graphic_info[graphic].anim_mode = anim_mode;
14705 if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
14707 InitRND(tape.random_seed);
14708 for (i = 0; i < num_random_calls; i++)
14712 if (game.num_random_calls != num_random_calls)
14714 Error(ERR_INFO, "number of random calls out of sync");
14715 Error(ERR_INFO, "number of random calls should be %d", num_random_calls);
14716 Error(ERR_INFO, "number of random calls is %d", game.num_random_calls);
14717 Error(ERR_EXIT, "this should not happen -- please debug");
14721 void FreeEngineSnapshotSingle()
14723 FreeSnapshotSingle();
14725 setString(&snapshot_level_identifier, NULL);
14726 snapshot_level_nr = -1;
14729 void FreeEngineSnapshotList()
14731 FreeSnapshotList();
14734 ListNode *SaveEngineSnapshotBuffers()
14736 ListNode *buffers = NULL;
14738 /* copy some special values to a structure better suited for the snapshot */
14740 if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
14741 SaveEngineSnapshotValues_RND();
14742 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
14743 SaveEngineSnapshotValues_EM();
14744 if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
14745 SaveEngineSnapshotValues_SP(&buffers);
14747 /* save values stored in special snapshot structure */
14749 if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
14750 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
14751 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
14752 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
14753 if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
14754 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_sp));
14756 /* save further RND engine values */
14758 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(stored_player));
14759 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(game));
14760 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(tape));
14762 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ZX));
14763 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ZY));
14764 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExitX));
14765 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExitY));
14767 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(FrameCounter));
14768 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeFrames));
14769 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimePlayed));
14770 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeLeft));
14771 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TapeTime));
14773 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovDir));
14774 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovPos));
14775 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenGfxPos));
14777 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScrollStepSize));
14779 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AllPlayersGone));
14781 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt));
14782 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2));
14784 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Feld));
14785 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovPos));
14786 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDir));
14787 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDelay));
14788 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeDelay));
14789 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangePage));
14790 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CustomValue));
14791 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store));
14792 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store2));
14793 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(StorePlayer));
14794 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Back));
14795 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaNr));
14796 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustMoving));
14797 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustFalling));
14798 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckCollision));
14799 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckImpact));
14800 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Stop));
14801 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Pushed));
14803 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeCount));
14804 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeEvent));
14806 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodePhase));
14807 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeDelay));
14808 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeField));
14810 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(RunnerVisit));
14811 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(PlayerVisit));
14813 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxFrame));
14814 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxRandom));
14815 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxElement));
14816 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxAction));
14817 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxDir));
14819 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_x));
14820 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_y));
14823 ListNode *node = engine_snapshot_list_rnd;
14826 while (node != NULL)
14828 num_bytes += ((struct EngineSnapshotNodeInfo *)node->content)->size;
14833 printf("::: size of engine snapshot: %d bytes\n", num_bytes);
14839 void SaveEngineSnapshotSingle()
14841 ListNode *buffers = SaveEngineSnapshotBuffers();
14843 /* finally save all snapshot buffers to single snapshot */
14844 SaveSnapshotSingle(buffers);
14846 /* save level identification information */
14847 setString(&snapshot_level_identifier, leveldir_current->identifier);
14848 snapshot_level_nr = level_nr;
14851 boolean CheckSaveEngineSnapshotToList()
14853 boolean save_snapshot =
14854 ((game.snapshot.mode == SNAPSHOT_MODE_EVERY_STEP) ||
14855 (game.snapshot.mode == SNAPSHOT_MODE_EVERY_MOVE &&
14856 game.snapshot.changed_action) ||
14857 (game.snapshot.mode == SNAPSHOT_MODE_EVERY_COLLECT &&
14858 game.snapshot.collected_item));
14860 game.snapshot.changed_action = FALSE;
14861 game.snapshot.collected_item = FALSE;
14862 game.snapshot.save_snapshot = save_snapshot;
14864 return save_snapshot;
14867 void SaveEngineSnapshotToList()
14869 if (game.snapshot.mode == SNAPSHOT_MODE_OFF ||
14873 ListNode *buffers = SaveEngineSnapshotBuffers();
14875 /* finally save all snapshot buffers to snapshot list */
14876 SaveSnapshotToList(buffers);
14879 void SaveEngineSnapshotToListInitial()
14881 FreeEngineSnapshotList();
14883 SaveEngineSnapshotToList();
14886 void LoadEngineSnapshotValues()
14888 /* restore special values from snapshot structure */
14890 if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
14891 LoadEngineSnapshotValues_RND();
14892 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
14893 LoadEngineSnapshotValues_EM();
14894 if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
14895 LoadEngineSnapshotValues_SP();
14898 void LoadEngineSnapshotSingle()
14900 LoadSnapshotSingle();
14902 LoadEngineSnapshotValues();
14905 void LoadEngineSnapshot_Undo(int steps)
14907 LoadSnapshotFromList_Older(steps);
14909 LoadEngineSnapshotValues();
14912 void LoadEngineSnapshot_Redo(int steps)
14914 LoadSnapshotFromList_Newer(steps);
14916 LoadEngineSnapshotValues();
14919 boolean CheckEngineSnapshotSingle()
14921 return (strEqual(snapshot_level_identifier, leveldir_current->identifier) &&
14922 snapshot_level_nr == level_nr);
14925 boolean CheckEngineSnapshotList()
14927 return CheckSnapshotList();
14931 /* ---------- new game button stuff ---------------------------------------- */
14939 } gamebutton_info[NUM_GAME_BUTTONS] =
14942 IMG_GFX_GAME_BUTTON_STOP, &game.button.stop,
14943 GAME_CTRL_ID_STOP, "stop game"
14946 IMG_GFX_GAME_BUTTON_PAUSE, &game.button.pause,
14947 GAME_CTRL_ID_PAUSE, "pause game"
14950 IMG_GFX_GAME_BUTTON_PLAY, &game.button.play,
14951 GAME_CTRL_ID_PLAY, "play game"
14954 IMG_GFX_GAME_BUTTON_UNDO, &game.button.undo,
14955 GAME_CTRL_ID_UNDO, "undo step"
14958 IMG_GFX_GAME_BUTTON_REDO, &game.button.redo,
14959 GAME_CTRL_ID_REDO, "redo step"
14962 IMG_GFX_GAME_BUTTON_SAVE, &game.button.save,
14963 GAME_CTRL_ID_SAVE, "save game"
14966 IMG_GFX_GAME_BUTTON_PAUSE2, &game.button.pause2,
14967 GAME_CTRL_ID_PAUSE2, "pause game"
14970 IMG_GFX_GAME_BUTTON_LOAD, &game.button.load,
14971 GAME_CTRL_ID_LOAD, "load game"
14974 IMG_GFX_GAME_BUTTON_SOUND_MUSIC, &game.button.sound_music,
14975 SOUND_CTRL_ID_MUSIC, "background music on/off"
14978 IMG_GFX_GAME_BUTTON_SOUND_LOOPS, &game.button.sound_loops,
14979 SOUND_CTRL_ID_LOOPS, "sound loops on/off"
14982 IMG_GFX_GAME_BUTTON_SOUND_SIMPLE, &game.button.sound_simple,
14983 SOUND_CTRL_ID_SIMPLE, "normal sounds on/off"
14987 void CreateGameButtons()
14991 for (i = 0; i < NUM_GAME_BUTTONS; i++)
14993 struct GraphicInfo *gfx = &graphic_info[gamebutton_info[i].graphic];
14994 struct XY *pos = gamebutton_info[i].pos;
14995 struct GadgetInfo *gi;
14998 unsigned int event_mask;
14999 int base_x = (tape.show_game_buttons ? VX : DX);
15000 int base_y = (tape.show_game_buttons ? VY : DY);
15001 int gd_x = gfx->src_x;
15002 int gd_y = gfx->src_y;
15003 int gd_xp = gfx->src_x + gfx->pressed_xoffset;
15004 int gd_yp = gfx->src_y + gfx->pressed_yoffset;
15005 int gd_xa = gfx->src_x + gfx->active_xoffset;
15006 int gd_ya = gfx->src_y + gfx->active_yoffset;
15007 int gd_xap = gfx->src_x + gfx->active_xoffset + gfx->pressed_xoffset;
15008 int gd_yap = gfx->src_y + gfx->active_yoffset + gfx->pressed_yoffset;
15011 if (gfx->bitmap == NULL)
15013 game_gadget[id] = NULL;
15018 if (id == GAME_CTRL_ID_STOP ||
15019 id == GAME_CTRL_ID_PLAY ||
15020 id == GAME_CTRL_ID_SAVE ||
15021 id == GAME_CTRL_ID_LOAD)
15023 button_type = GD_TYPE_NORMAL_BUTTON;
15025 event_mask = GD_EVENT_RELEASED;
15027 else if (id == GAME_CTRL_ID_UNDO ||
15028 id == GAME_CTRL_ID_REDO)
15030 button_type = GD_TYPE_NORMAL_BUTTON;
15032 event_mask = GD_EVENT_PRESSED | GD_EVENT_REPEATED;
15036 button_type = GD_TYPE_CHECK_BUTTON;
15038 ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
15039 (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
15040 (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
15041 event_mask = GD_EVENT_PRESSED;
15044 gi = CreateGadget(GDI_CUSTOM_ID, id,
15045 GDI_INFO_TEXT, gamebutton_info[i].infotext,
15046 GDI_X, base_x + GDI_ACTIVE_POS(pos->x),
15047 GDI_Y, base_y + GDI_ACTIVE_POS(pos->y),
15048 GDI_WIDTH, gfx->width,
15049 GDI_HEIGHT, gfx->height,
15050 GDI_TYPE, button_type,
15051 GDI_STATE, GD_BUTTON_UNPRESSED,
15052 GDI_CHECKED, checked,
15053 GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
15054 GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
15055 GDI_ALT_DESIGN_UNPRESSED, gfx->bitmap, gd_xa, gd_ya,
15056 GDI_ALT_DESIGN_PRESSED, gfx->bitmap, gd_xap, gd_yap,
15057 GDI_DIRECT_DRAW, FALSE,
15058 GDI_EVENT_MASK, event_mask,
15059 GDI_CALLBACK_ACTION, HandleGameButtons,
15063 Error(ERR_EXIT, "cannot create gadget");
15065 game_gadget[id] = gi;
15069 void FreeGameButtons()
15073 for (i = 0; i < NUM_GAME_BUTTONS; i++)
15074 FreeGadget(game_gadget[i]);
15077 static void UnmapGameButtonsAtSamePosition(int id)
15081 for (i = 0; i < NUM_GAME_BUTTONS; i++)
15083 gamebutton_info[i].pos->x == gamebutton_info[id].pos->x &&
15084 gamebutton_info[i].pos->y == gamebutton_info[id].pos->y)
15085 UnmapGadget(game_gadget[i]);
15088 static void UnmapGameButtonsAtSamePosition_All()
15090 if (setup.show_snapshot_buttons)
15092 UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_SAVE);
15093 UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE2);
15094 UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_LOAD);
15098 UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_STOP);
15099 UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE);
15100 UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PLAY);
15104 static void MapGameButtonsAtSamePosition(int id)
15108 for (i = 0; i < NUM_GAME_BUTTONS; i++)
15110 gamebutton_info[i].pos->x == gamebutton_info[id].pos->x &&
15111 gamebutton_info[i].pos->y == gamebutton_info[id].pos->y)
15112 MapGadget(game_gadget[i]);
15114 UnmapGameButtonsAtSamePosition_All();
15117 void MapUndoRedoButtons()
15119 UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
15120 UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
15122 MapGadget(game_gadget[GAME_CTRL_ID_UNDO]);
15123 MapGadget(game_gadget[GAME_CTRL_ID_REDO]);
15125 ModifyGadget(game_gadget[GAME_CTRL_ID_PAUSE2], GDI_CHECKED, TRUE, GDI_END);
15128 void UnmapUndoRedoButtons()
15130 UnmapGadget(game_gadget[GAME_CTRL_ID_UNDO]);
15131 UnmapGadget(game_gadget[GAME_CTRL_ID_REDO]);
15133 MapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
15134 MapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
15136 ModifyGadget(game_gadget[GAME_CTRL_ID_PAUSE2], GDI_CHECKED, FALSE, GDI_END);
15139 void MapGameButtons()
15143 for (i = 0; i < NUM_GAME_BUTTONS; i++)
15144 if (i != GAME_CTRL_ID_UNDO &&
15145 i != GAME_CTRL_ID_REDO)
15146 MapGadget(game_gadget[i]);
15148 UnmapGameButtonsAtSamePosition_All();
15150 RedrawGameButtons();
15153 void UnmapGameButtons()
15157 for (i = 0; i < NUM_GAME_BUTTONS; i++)
15158 UnmapGadget(game_gadget[i]);
15161 void RedrawGameButtons()
15165 for (i = 0; i < NUM_GAME_BUTTONS; i++)
15166 RedrawGadget(game_gadget[i]);
15168 // RedrawGadget() may have set REDRAW_ALL if buttons are defined off-area
15169 redraw_mask &= ~REDRAW_ALL;
15172 void GameUndoRedoExt()
15174 ClearPlayerAction();
15176 tape.pausing = TRUE;
15179 UpdateAndDisplayGameControlValues();
15181 DrawCompleteVideoDisplay();
15182 DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
15183 DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
15184 DrawVideoDisplay(VIDEO_STATE_1STEP(tape.single_step), 0);
15189 void GameUndo(int steps)
15191 if (!CheckEngineSnapshotList())
15194 LoadEngineSnapshot_Undo(steps);
15199 void GameRedo(int steps)
15201 if (!CheckEngineSnapshotList())
15204 LoadEngineSnapshot_Redo(steps);
15209 static void HandleGameButtonsExt(int id, int button)
15211 static boolean game_undo_executed = FALSE;
15212 int steps = BUTTON_STEPSIZE(button);
15213 boolean handle_game_buttons =
15214 (game_status == GAME_MODE_PLAYING ||
15215 (game_status == GAME_MODE_MAIN && tape.show_game_buttons));
15217 if (!handle_game_buttons)
15222 case GAME_CTRL_ID_STOP:
15223 if (game_status == GAME_MODE_MAIN)
15229 RequestQuitGame(TRUE);
15233 case GAME_CTRL_ID_PAUSE:
15234 case GAME_CTRL_ID_PAUSE2:
15235 if (options.network && game_status == GAME_MODE_PLAYING)
15237 #if defined(NETWORK_AVALIABLE)
15239 SendToServer_ContinuePlaying();
15241 SendToServer_PausePlaying();
15245 TapeTogglePause(TAPE_TOGGLE_MANUAL);
15247 game_undo_executed = FALSE;
15251 case GAME_CTRL_ID_PLAY:
15252 if (game_status == GAME_MODE_MAIN)
15254 StartGameActions(options.network, setup.autorecord, level.random_seed);
15256 else if (tape.pausing)
15258 #if defined(NETWORK_AVALIABLE)
15259 if (options.network)
15260 SendToServer_ContinuePlaying();
15263 TapeTogglePause(TAPE_TOGGLE_MANUAL | TAPE_TOGGLE_PLAY_PAUSE);
15267 case GAME_CTRL_ID_UNDO:
15268 // Important: When using "save snapshot when collecting an item" mode,
15269 // load last (current) snapshot for first "undo" after pressing "pause"
15270 // (else the last-but-one snapshot would be loaded, because the snapshot
15271 // pointer already points to the last snapshot when pressing "pause",
15272 // which is fine for "every step/move" mode, but not for "every collect")
15273 if (game.snapshot.mode == SNAPSHOT_MODE_EVERY_COLLECT &&
15274 !game_undo_executed)
15277 game_undo_executed = TRUE;
15282 case GAME_CTRL_ID_REDO:
15286 case GAME_CTRL_ID_SAVE:
15290 case GAME_CTRL_ID_LOAD:
15294 case SOUND_CTRL_ID_MUSIC:
15295 if (setup.sound_music)
15297 setup.sound_music = FALSE;
15301 else if (audio.music_available)
15303 setup.sound = setup.sound_music = TRUE;
15305 SetAudioMode(setup.sound);
15311 case SOUND_CTRL_ID_LOOPS:
15312 if (setup.sound_loops)
15313 setup.sound_loops = FALSE;
15314 else if (audio.loops_available)
15316 setup.sound = setup.sound_loops = TRUE;
15318 SetAudioMode(setup.sound);
15322 case SOUND_CTRL_ID_SIMPLE:
15323 if (setup.sound_simple)
15324 setup.sound_simple = FALSE;
15325 else if (audio.sound_available)
15327 setup.sound = setup.sound_simple = TRUE;
15329 SetAudioMode(setup.sound);
15338 static void HandleGameButtons(struct GadgetInfo *gi)
15340 HandleGameButtonsExt(gi->custom_id, gi->event.button);
15343 void HandleSoundButtonKeys(Key key)
15346 if (key == setup.shortcut.sound_simple)
15347 ClickOnGadget(game_gadget[SOUND_CTRL_ID_SIMPLE], MB_LEFTBUTTON);
15348 else if (key == setup.shortcut.sound_loops)
15349 ClickOnGadget(game_gadget[SOUND_CTRL_ID_LOOPS], MB_LEFTBUTTON);
15350 else if (key == setup.shortcut.sound_music)
15351 ClickOnGadget(game_gadget[SOUND_CTRL_ID_MUSIC], MB_LEFTBUTTON);