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_TIME_ANIM 35
126 #define GAME_PANEL_HEALTH 36
127 #define GAME_PANEL_HEALTH_ANIM 37
128 #define GAME_PANEL_FRAME 38
129 #define GAME_PANEL_SHIELD_NORMAL 39
130 #define GAME_PANEL_SHIELD_NORMAL_TIME 40
131 #define GAME_PANEL_SHIELD_DEADLY 41
132 #define GAME_PANEL_SHIELD_DEADLY_TIME 42
133 #define GAME_PANEL_EXIT 43
134 #define GAME_PANEL_EMC_MAGIC_BALL 44
135 #define GAME_PANEL_EMC_MAGIC_BALL_SWITCH 45
136 #define GAME_PANEL_LIGHT_SWITCH 46
137 #define GAME_PANEL_LIGHT_SWITCH_TIME 47
138 #define GAME_PANEL_TIMEGATE_SWITCH 48
139 #define GAME_PANEL_TIMEGATE_SWITCH_TIME 49
140 #define GAME_PANEL_SWITCHGATE_SWITCH 50
141 #define GAME_PANEL_EMC_LENSES 51
142 #define GAME_PANEL_EMC_LENSES_TIME 52
143 #define GAME_PANEL_EMC_MAGNIFIER 53
144 #define GAME_PANEL_EMC_MAGNIFIER_TIME 54
145 #define GAME_PANEL_BALLOON_SWITCH 55
146 #define GAME_PANEL_DYNABOMB_NUMBER 56
147 #define GAME_PANEL_DYNABOMB_SIZE 57
148 #define GAME_PANEL_DYNABOMB_POWER 58
149 #define GAME_PANEL_PENGUINS 59
150 #define GAME_PANEL_SOKOBAN_OBJECTS 60
151 #define GAME_PANEL_SOKOBAN_FIELDS 61
152 #define GAME_PANEL_ROBOT_WHEEL 62
153 #define GAME_PANEL_CONVEYOR_BELT_1 63
154 #define GAME_PANEL_CONVEYOR_BELT_2 64
155 #define GAME_PANEL_CONVEYOR_BELT_3 65
156 #define GAME_PANEL_CONVEYOR_BELT_4 66
157 #define GAME_PANEL_CONVEYOR_BELT_1_SWITCH 67
158 #define GAME_PANEL_CONVEYOR_BELT_2_SWITCH 68
159 #define GAME_PANEL_CONVEYOR_BELT_3_SWITCH 69
160 #define GAME_PANEL_CONVEYOR_BELT_4_SWITCH 70
161 #define GAME_PANEL_MAGIC_WALL 71
162 #define GAME_PANEL_MAGIC_WALL_TIME 72
163 #define GAME_PANEL_GRAVITY_STATE 73
164 #define GAME_PANEL_GRAPHIC_1 74
165 #define GAME_PANEL_GRAPHIC_2 75
166 #define GAME_PANEL_GRAPHIC_3 76
167 #define GAME_PANEL_GRAPHIC_4 77
168 #define GAME_PANEL_GRAPHIC_5 78
169 #define GAME_PANEL_GRAPHIC_6 79
170 #define GAME_PANEL_GRAPHIC_7 80
171 #define GAME_PANEL_GRAPHIC_8 81
172 #define GAME_PANEL_ELEMENT_1 82
173 #define GAME_PANEL_ELEMENT_2 83
174 #define GAME_PANEL_ELEMENT_3 84
175 #define GAME_PANEL_ELEMENT_4 85
176 #define GAME_PANEL_ELEMENT_5 86
177 #define GAME_PANEL_ELEMENT_6 87
178 #define GAME_PANEL_ELEMENT_7 88
179 #define GAME_PANEL_ELEMENT_8 89
180 #define GAME_PANEL_ELEMENT_COUNT_1 90
181 #define GAME_PANEL_ELEMENT_COUNT_2 91
182 #define GAME_PANEL_ELEMENT_COUNT_3 92
183 #define GAME_PANEL_ELEMENT_COUNT_4 93
184 #define GAME_PANEL_ELEMENT_COUNT_5 94
185 #define GAME_PANEL_ELEMENT_COUNT_6 95
186 #define GAME_PANEL_ELEMENT_COUNT_7 96
187 #define GAME_PANEL_ELEMENT_COUNT_8 97
188 #define GAME_PANEL_CE_SCORE_1 98
189 #define GAME_PANEL_CE_SCORE_2 99
190 #define GAME_PANEL_CE_SCORE_3 100
191 #define GAME_PANEL_CE_SCORE_4 101
192 #define GAME_PANEL_CE_SCORE_5 102
193 #define GAME_PANEL_CE_SCORE_6 103
194 #define GAME_PANEL_CE_SCORE_7 104
195 #define GAME_PANEL_CE_SCORE_8 105
196 #define GAME_PANEL_CE_SCORE_1_ELEMENT 106
197 #define GAME_PANEL_CE_SCORE_2_ELEMENT 107
198 #define GAME_PANEL_CE_SCORE_3_ELEMENT 108
199 #define GAME_PANEL_CE_SCORE_4_ELEMENT 109
200 #define GAME_PANEL_CE_SCORE_5_ELEMENT 110
201 #define GAME_PANEL_CE_SCORE_6_ELEMENT 111
202 #define GAME_PANEL_CE_SCORE_7_ELEMENT 112
203 #define GAME_PANEL_CE_SCORE_8_ELEMENT 113
204 #define GAME_PANEL_PLAYER_NAME 114
205 #define GAME_PANEL_LEVEL_NAME 115
206 #define GAME_PANEL_LEVEL_AUTHOR 116
208 #define NUM_GAME_PANEL_CONTROLS 117
210 struct GamePanelOrderInfo
216 static struct GamePanelOrderInfo game_panel_order[NUM_GAME_PANEL_CONTROLS];
218 struct GamePanelControlInfo
222 struct TextPosInfo *pos;
225 int graphic, graphic_active;
227 int value, last_value;
228 int frame, last_frame;
233 static struct GamePanelControlInfo game_panel_controls[] =
236 GAME_PANEL_LEVEL_NUMBER,
237 &game.panel.level_number,
246 GAME_PANEL_INVENTORY_COUNT,
247 &game.panel.inventory_count,
251 GAME_PANEL_INVENTORY_FIRST_1,
252 &game.panel.inventory_first[0],
256 GAME_PANEL_INVENTORY_FIRST_2,
257 &game.panel.inventory_first[1],
261 GAME_PANEL_INVENTORY_FIRST_3,
262 &game.panel.inventory_first[2],
266 GAME_PANEL_INVENTORY_FIRST_4,
267 &game.panel.inventory_first[3],
271 GAME_PANEL_INVENTORY_FIRST_5,
272 &game.panel.inventory_first[4],
276 GAME_PANEL_INVENTORY_FIRST_6,
277 &game.panel.inventory_first[5],
281 GAME_PANEL_INVENTORY_FIRST_7,
282 &game.panel.inventory_first[6],
286 GAME_PANEL_INVENTORY_FIRST_8,
287 &game.panel.inventory_first[7],
291 GAME_PANEL_INVENTORY_LAST_1,
292 &game.panel.inventory_last[0],
296 GAME_PANEL_INVENTORY_LAST_2,
297 &game.panel.inventory_last[1],
301 GAME_PANEL_INVENTORY_LAST_3,
302 &game.panel.inventory_last[2],
306 GAME_PANEL_INVENTORY_LAST_4,
307 &game.panel.inventory_last[3],
311 GAME_PANEL_INVENTORY_LAST_5,
312 &game.panel.inventory_last[4],
316 GAME_PANEL_INVENTORY_LAST_6,
317 &game.panel.inventory_last[5],
321 GAME_PANEL_INVENTORY_LAST_7,
322 &game.panel.inventory_last[6],
326 GAME_PANEL_INVENTORY_LAST_8,
327 &game.panel.inventory_last[7],
371 GAME_PANEL_KEY_WHITE,
372 &game.panel.key_white,
376 GAME_PANEL_KEY_WHITE_COUNT,
377 &game.panel.key_white_count,
386 GAME_PANEL_HIGHSCORE,
387 &game.panel.highscore,
411 GAME_PANEL_TIME_ANIM,
412 &game.panel.time_anim,
415 IMG_GFX_GAME_PANEL_TIME_ANIM,
416 IMG_GFX_GAME_PANEL_TIME_ANIM_ACTIVE
424 GAME_PANEL_HEALTH_ANIM,
425 &game.panel.health_anim,
428 IMG_GFX_GAME_PANEL_HEALTH_ANIM,
429 IMG_GFX_GAME_PANEL_HEALTH_ANIM_ACTIVE
437 GAME_PANEL_SHIELD_NORMAL,
438 &game.panel.shield_normal,
442 GAME_PANEL_SHIELD_NORMAL_TIME,
443 &game.panel.shield_normal_time,
447 GAME_PANEL_SHIELD_DEADLY,
448 &game.panel.shield_deadly,
452 GAME_PANEL_SHIELD_DEADLY_TIME,
453 &game.panel.shield_deadly_time,
462 GAME_PANEL_EMC_MAGIC_BALL,
463 &game.panel.emc_magic_ball,
467 GAME_PANEL_EMC_MAGIC_BALL_SWITCH,
468 &game.panel.emc_magic_ball_switch,
472 GAME_PANEL_LIGHT_SWITCH,
473 &game.panel.light_switch,
477 GAME_PANEL_LIGHT_SWITCH_TIME,
478 &game.panel.light_switch_time,
482 GAME_PANEL_TIMEGATE_SWITCH,
483 &game.panel.timegate_switch,
487 GAME_PANEL_TIMEGATE_SWITCH_TIME,
488 &game.panel.timegate_switch_time,
492 GAME_PANEL_SWITCHGATE_SWITCH,
493 &game.panel.switchgate_switch,
497 GAME_PANEL_EMC_LENSES,
498 &game.panel.emc_lenses,
502 GAME_PANEL_EMC_LENSES_TIME,
503 &game.panel.emc_lenses_time,
507 GAME_PANEL_EMC_MAGNIFIER,
508 &game.panel.emc_magnifier,
512 GAME_PANEL_EMC_MAGNIFIER_TIME,
513 &game.panel.emc_magnifier_time,
517 GAME_PANEL_BALLOON_SWITCH,
518 &game.panel.balloon_switch,
522 GAME_PANEL_DYNABOMB_NUMBER,
523 &game.panel.dynabomb_number,
527 GAME_PANEL_DYNABOMB_SIZE,
528 &game.panel.dynabomb_size,
532 GAME_PANEL_DYNABOMB_POWER,
533 &game.panel.dynabomb_power,
538 &game.panel.penguins,
542 GAME_PANEL_SOKOBAN_OBJECTS,
543 &game.panel.sokoban_objects,
547 GAME_PANEL_SOKOBAN_FIELDS,
548 &game.panel.sokoban_fields,
552 GAME_PANEL_ROBOT_WHEEL,
553 &game.panel.robot_wheel,
557 GAME_PANEL_CONVEYOR_BELT_1,
558 &game.panel.conveyor_belt[0],
562 GAME_PANEL_CONVEYOR_BELT_2,
563 &game.panel.conveyor_belt[1],
567 GAME_PANEL_CONVEYOR_BELT_3,
568 &game.panel.conveyor_belt[2],
572 GAME_PANEL_CONVEYOR_BELT_4,
573 &game.panel.conveyor_belt[3],
577 GAME_PANEL_CONVEYOR_BELT_1_SWITCH,
578 &game.panel.conveyor_belt_switch[0],
582 GAME_PANEL_CONVEYOR_BELT_2_SWITCH,
583 &game.panel.conveyor_belt_switch[1],
587 GAME_PANEL_CONVEYOR_BELT_3_SWITCH,
588 &game.panel.conveyor_belt_switch[2],
592 GAME_PANEL_CONVEYOR_BELT_4_SWITCH,
593 &game.panel.conveyor_belt_switch[3],
597 GAME_PANEL_MAGIC_WALL,
598 &game.panel.magic_wall,
602 GAME_PANEL_MAGIC_WALL_TIME,
603 &game.panel.magic_wall_time,
607 GAME_PANEL_GRAVITY_STATE,
608 &game.panel.gravity_state,
612 GAME_PANEL_GRAPHIC_1,
613 &game.panel.graphic[0],
617 GAME_PANEL_GRAPHIC_2,
618 &game.panel.graphic[1],
622 GAME_PANEL_GRAPHIC_3,
623 &game.panel.graphic[2],
627 GAME_PANEL_GRAPHIC_4,
628 &game.panel.graphic[3],
632 GAME_PANEL_GRAPHIC_5,
633 &game.panel.graphic[4],
637 GAME_PANEL_GRAPHIC_6,
638 &game.panel.graphic[5],
642 GAME_PANEL_GRAPHIC_7,
643 &game.panel.graphic[6],
647 GAME_PANEL_GRAPHIC_8,
648 &game.panel.graphic[7],
652 GAME_PANEL_ELEMENT_1,
653 &game.panel.element[0],
657 GAME_PANEL_ELEMENT_2,
658 &game.panel.element[1],
662 GAME_PANEL_ELEMENT_3,
663 &game.panel.element[2],
667 GAME_PANEL_ELEMENT_4,
668 &game.panel.element[3],
672 GAME_PANEL_ELEMENT_5,
673 &game.panel.element[4],
677 GAME_PANEL_ELEMENT_6,
678 &game.panel.element[5],
682 GAME_PANEL_ELEMENT_7,
683 &game.panel.element[6],
687 GAME_PANEL_ELEMENT_8,
688 &game.panel.element[7],
692 GAME_PANEL_ELEMENT_COUNT_1,
693 &game.panel.element_count[0],
697 GAME_PANEL_ELEMENT_COUNT_2,
698 &game.panel.element_count[1],
702 GAME_PANEL_ELEMENT_COUNT_3,
703 &game.panel.element_count[2],
707 GAME_PANEL_ELEMENT_COUNT_4,
708 &game.panel.element_count[3],
712 GAME_PANEL_ELEMENT_COUNT_5,
713 &game.panel.element_count[4],
717 GAME_PANEL_ELEMENT_COUNT_6,
718 &game.panel.element_count[5],
722 GAME_PANEL_ELEMENT_COUNT_7,
723 &game.panel.element_count[6],
727 GAME_PANEL_ELEMENT_COUNT_8,
728 &game.panel.element_count[7],
732 GAME_PANEL_CE_SCORE_1,
733 &game.panel.ce_score[0],
737 GAME_PANEL_CE_SCORE_2,
738 &game.panel.ce_score[1],
742 GAME_PANEL_CE_SCORE_3,
743 &game.panel.ce_score[2],
747 GAME_PANEL_CE_SCORE_4,
748 &game.panel.ce_score[3],
752 GAME_PANEL_CE_SCORE_5,
753 &game.panel.ce_score[4],
757 GAME_PANEL_CE_SCORE_6,
758 &game.panel.ce_score[5],
762 GAME_PANEL_CE_SCORE_7,
763 &game.panel.ce_score[6],
767 GAME_PANEL_CE_SCORE_8,
768 &game.panel.ce_score[7],
772 GAME_PANEL_CE_SCORE_1_ELEMENT,
773 &game.panel.ce_score_element[0],
777 GAME_PANEL_CE_SCORE_2_ELEMENT,
778 &game.panel.ce_score_element[1],
782 GAME_PANEL_CE_SCORE_3_ELEMENT,
783 &game.panel.ce_score_element[2],
787 GAME_PANEL_CE_SCORE_4_ELEMENT,
788 &game.panel.ce_score_element[3],
792 GAME_PANEL_CE_SCORE_5_ELEMENT,
793 &game.panel.ce_score_element[4],
797 GAME_PANEL_CE_SCORE_6_ELEMENT,
798 &game.panel.ce_score_element[5],
802 GAME_PANEL_CE_SCORE_7_ELEMENT,
803 &game.panel.ce_score_element[6],
807 GAME_PANEL_CE_SCORE_8_ELEMENT,
808 &game.panel.ce_score_element[7],
812 GAME_PANEL_PLAYER_NAME,
813 &game.panel.player_name,
817 GAME_PANEL_LEVEL_NAME,
818 &game.panel.level_name,
822 GAME_PANEL_LEVEL_AUTHOR,
823 &game.panel.level_author,
834 /* values for delayed check of falling and moving elements and for collision */
835 #define CHECK_DELAY_MOVING 3
836 #define CHECK_DELAY_FALLING CHECK_DELAY_MOVING
837 #define CHECK_DELAY_COLLISION 2
838 #define CHECK_DELAY_IMPACT CHECK_DELAY_COLLISION
840 /* values for initial player move delay (initial delay counter value) */
841 #define INITIAL_MOVE_DELAY_OFF -1
842 #define INITIAL_MOVE_DELAY_ON 0
844 /* values for player movement speed (which is in fact a delay value) */
845 #define MOVE_DELAY_MIN_SPEED 32
846 #define MOVE_DELAY_NORMAL_SPEED 8
847 #define MOVE_DELAY_HIGH_SPEED 4
848 #define MOVE_DELAY_MAX_SPEED 1
850 #define DOUBLE_MOVE_DELAY(x) (x = (x < MOVE_DELAY_MIN_SPEED ? x * 2 : x))
851 #define HALVE_MOVE_DELAY(x) (x = (x > MOVE_DELAY_MAX_SPEED ? x / 2 : x))
853 #define DOUBLE_PLAYER_SPEED(p) (HALVE_MOVE_DELAY( (p)->move_delay_value))
854 #define HALVE_PLAYER_SPEED(p) (DOUBLE_MOVE_DELAY((p)->move_delay_value))
856 /* values for scroll positions */
857 #define SCROLL_POSITION_X(x) ((x) < SBX_Left + MIDPOSX ? SBX_Left : \
858 (x) > SBX_Right + MIDPOSX ? SBX_Right :\
860 #define SCROLL_POSITION_Y(y) ((y) < SBY_Upper + MIDPOSY ? SBY_Upper :\
861 (y) > SBY_Lower + MIDPOSY ? SBY_Lower :\
864 /* values for other actions */
865 #define MOVE_STEPSIZE_NORMAL (TILEX / MOVE_DELAY_NORMAL_SPEED)
866 #define MOVE_STEPSIZE_MIN (1)
867 #define MOVE_STEPSIZE_MAX (TILEX)
869 #define GET_DX_FROM_DIR(d) ((d) == MV_LEFT ? -1 : (d) == MV_RIGHT ? 1 : 0)
870 #define GET_DY_FROM_DIR(d) ((d) == MV_UP ? -1 : (d) == MV_DOWN ? 1 : 0)
872 #define INIT_GFX_RANDOM() (GetSimpleRandom(1000000))
874 #define GET_NEW_PUSH_DELAY(e) ( (element_info[e].push_delay_fixed) + \
875 RND(element_info[e].push_delay_random))
876 #define GET_NEW_DROP_DELAY(e) ( (element_info[e].drop_delay_fixed) + \
877 RND(element_info[e].drop_delay_random))
878 #define GET_NEW_MOVE_DELAY(e) ( (element_info[e].move_delay_fixed) + \
879 RND(element_info[e].move_delay_random))
880 #define GET_MAX_MOVE_DELAY(e) ( (element_info[e].move_delay_fixed) + \
881 (element_info[e].move_delay_random))
882 #define GET_NEW_CE_VALUE(e) ( (element_info[e].ce_value_fixed_initial) +\
883 RND(element_info[e].ce_value_random_initial))
884 #define GET_CE_SCORE(e) ( (element_info[e].collect_score))
885 #define GET_CHANGE_DELAY(c) ( ((c)->delay_fixed * (c)->delay_frames) + \
886 RND((c)->delay_random * (c)->delay_frames))
887 #define GET_CE_DELAY_VALUE(c) ( ((c)->delay_fixed) + \
888 RND((c)->delay_random))
891 #define GET_VALID_RUNTIME_ELEMENT(e) \
892 ((e) >= NUM_RUNTIME_ELEMENTS ? EL_UNKNOWN : (e))
894 #define RESOLVED_REFERENCE_ELEMENT(be, e) \
895 ((be) + (e) - EL_SELF < EL_CUSTOM_START ? EL_CUSTOM_START : \
896 (be) + (e) - EL_SELF > EL_CUSTOM_END ? EL_CUSTOM_END : \
897 (be) + (e) - EL_SELF)
899 #define GET_PLAYER_FROM_BITS(p) \
900 (EL_PLAYER_1 + ((p) != PLAYER_BITS_ANY ? log_2(p) : 0))
902 #define GET_TARGET_ELEMENT(be, e, ch, cv, cs) \
903 ((e) == EL_TRIGGER_PLAYER ? (ch)->actual_trigger_player : \
904 (e) == EL_TRIGGER_ELEMENT ? (ch)->actual_trigger_element : \
905 (e) == EL_TRIGGER_CE_VALUE ? (ch)->actual_trigger_ce_value : \
906 (e) == EL_TRIGGER_CE_SCORE ? (ch)->actual_trigger_ce_score : \
907 (e) == EL_CURRENT_CE_VALUE ? (cv) : \
908 (e) == EL_CURRENT_CE_SCORE ? (cs) : \
909 (e) >= EL_PREV_CE_8 && (e) <= EL_NEXT_CE_8 ? \
910 RESOLVED_REFERENCE_ELEMENT(be, e) : \
913 #define CAN_GROW_INTO(e) \
914 ((e) == EL_SAND || (IS_DIGGABLE(e) && level.grow_into_diggable))
916 #define ELEMENT_CAN_ENTER_FIELD_BASE_X(x, y, condition) \
917 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
920 #define ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, condition) \
921 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
922 (CAN_MOVE_INTO_ACID(e) && \
923 Feld[x][y] == EL_ACID) || \
926 #define ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, condition) \
927 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) || \
928 (CAN_MOVE_INTO_ACID(e) && \
929 Feld[x][y] == EL_ACID) || \
932 #define ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, condition) \
933 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
935 (CAN_MOVE_INTO_ACID(e) && \
936 Feld[x][y] == EL_ACID) || \
937 (DONT_COLLIDE_WITH(e) && \
939 !PLAYER_ENEMY_PROTECTED(x, y))))
941 #define ELEMENT_CAN_ENTER_FIELD(e, x, y) \
942 ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, 0)
944 #define SATELLITE_CAN_ENTER_FIELD(x, y) \
945 ELEMENT_CAN_ENTER_FIELD_BASE_2(EL_SATELLITE, x, y, 0)
947 #define ANDROID_CAN_ENTER_FIELD(e, x, y) \
948 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, Feld[x][y] == EL_EMC_PLANT)
950 #define ANDROID_CAN_CLONE_FIELD(x, y) \
951 (IN_LEV_FIELD(x, y) && (CAN_BE_CLONED_BY_ANDROID(Feld[x][y]) || \
952 CAN_BE_CLONED_BY_ANDROID(EL_TRIGGER_ELEMENT)))
954 #define ENEMY_CAN_ENTER_FIELD(e, x, y) \
955 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
957 #define YAMYAM_CAN_ENTER_FIELD(e, x, y) \
958 ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, Feld[x][y] == EL_DIAMOND)
960 #define DARK_YAMYAM_CAN_ENTER_FIELD(e, x, y) \
961 ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x,y, IS_FOOD_DARK_YAMYAM(Feld[x][y]))
963 #define PACMAN_CAN_ENTER_FIELD(e, x, y) \
964 ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, IS_AMOEBOID(Feld[x][y]))
966 #define PIG_CAN_ENTER_FIELD(e, x, y) \
967 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, IS_FOOD_PIG(Feld[x][y]))
969 #define PENGUIN_CAN_ENTER_FIELD(e, x, y) \
970 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (Feld[x][y] == EL_EXIT_OPEN || \
971 Feld[x][y] == EL_EM_EXIT_OPEN || \
972 Feld[x][y] == EL_STEEL_EXIT_OPEN || \
973 Feld[x][y] == EL_EM_STEEL_EXIT_OPEN || \
974 IS_FOOD_PENGUIN(Feld[x][y])))
975 #define DRAGON_CAN_ENTER_FIELD(e, x, y) \
976 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
978 #define MOLE_CAN_ENTER_FIELD(e, x, y, condition) \
979 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (condition))
981 #define SPRING_CAN_ENTER_FIELD(e, x, y) \
982 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
984 #define SPRING_CAN_BUMP_FROM_FIELD(x, y) \
985 (IN_LEV_FIELD(x, y) && (Feld[x][y] == EL_EMC_SPRING_BUMPER || \
986 Feld[x][y] == EL_EMC_SPRING_BUMPER_ACTIVE))
988 #define MOVE_ENTER_EL(e) (element_info[e].move_enter_element)
990 #define CE_ENTER_FIELD_COND(e, x, y) \
991 (!IS_PLAYER(x, y) && \
992 IS_EQUAL_OR_IN_GROUP(Feld[x][y], MOVE_ENTER_EL(e)))
994 #define CUSTOM_ELEMENT_CAN_ENTER_FIELD(e, x, y) \
995 ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, CE_ENTER_FIELD_COND(e, x, y))
997 #define IN_LEV_FIELD_AND_IS_FREE(x, y) (IN_LEV_FIELD(x, y) && IS_FREE(x, y))
998 #define IN_LEV_FIELD_AND_NOT_FREE(x, y) (IN_LEV_FIELD(x, y) && !IS_FREE(x, y))
1000 #define ACCESS_FROM(e, d) (element_info[e].access_direction &(d))
1001 #define IS_WALKABLE_FROM(e, d) (IS_WALKABLE(e) && ACCESS_FROM(e, d))
1002 #define IS_PASSABLE_FROM(e, d) (IS_PASSABLE(e) && ACCESS_FROM(e, d))
1003 #define IS_ACCESSIBLE_FROM(e, d) (IS_ACCESSIBLE(e) && ACCESS_FROM(e, d))
1005 #define MM_HEALTH(x) (MIN(MAX(0, MAX_HEALTH - (x)), MAX_HEALTH))
1007 /* game button identifiers */
1008 #define GAME_CTRL_ID_STOP 0
1009 #define GAME_CTRL_ID_PAUSE 1
1010 #define GAME_CTRL_ID_PLAY 2
1011 #define GAME_CTRL_ID_UNDO 3
1012 #define GAME_CTRL_ID_REDO 4
1013 #define GAME_CTRL_ID_SAVE 5
1014 #define GAME_CTRL_ID_PAUSE2 6
1015 #define GAME_CTRL_ID_LOAD 7
1016 #define GAME_CTRL_ID_PANEL_STOP 8
1017 #define GAME_CTRL_ID_PANEL_PAUSE 9
1018 #define GAME_CTRL_ID_PANEL_PLAY 10
1019 #define SOUND_CTRL_ID_MUSIC 11
1020 #define SOUND_CTRL_ID_LOOPS 12
1021 #define SOUND_CTRL_ID_SIMPLE 13
1022 #define SOUND_CTRL_ID_PANEL_MUSIC 14
1023 #define SOUND_CTRL_ID_PANEL_LOOPS 15
1024 #define SOUND_CTRL_ID_PANEL_SIMPLE 16
1026 #define NUM_GAME_BUTTONS 17
1029 /* forward declaration for internal use */
1031 static void CreateField(int, int, int);
1033 static void ResetGfxAnimation(int, int);
1035 static void SetPlayerWaiting(struct PlayerInfo *, boolean);
1036 static void AdvanceFrameAndPlayerCounters(int);
1038 static boolean MovePlayerOneStep(struct PlayerInfo *, int, int, int, int);
1039 static boolean MovePlayer(struct PlayerInfo *, int, int);
1040 static void ScrollPlayer(struct PlayerInfo *, int);
1041 static void ScrollScreen(struct PlayerInfo *, int);
1043 static int DigField(struct PlayerInfo *, int, int, int, int, int, int, int);
1044 static boolean DigFieldByCE(int, int, int);
1045 static boolean SnapField(struct PlayerInfo *, int, int);
1046 static boolean DropElement(struct PlayerInfo *);
1048 static void InitBeltMovement(void);
1049 static void CloseAllOpenTimegates(void);
1050 static void CheckGravityMovement(struct PlayerInfo *);
1051 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *);
1052 static void KillPlayerUnlessEnemyProtected(int, int);
1053 static void KillPlayerUnlessExplosionProtected(int, int);
1055 static void TestIfPlayerTouchesCustomElement(int, int);
1056 static void TestIfElementTouchesCustomElement(int, int);
1057 static void TestIfElementHitsCustomElement(int, int, int);
1059 static void HandleElementChange(int, int, int);
1060 static void ExecuteCustomElementAction(int, int, int, int);
1061 static boolean ChangeElement(int, int, int, int);
1063 static boolean CheckTriggeredElementChangeExt(int, int, int, int, int,int,int);
1064 #define CheckTriggeredElementChange(x, y, e, ev) \
1065 CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY,CH_SIDE_ANY, -1)
1066 #define CheckTriggeredElementChangeByPlayer(x, y, e, ev, p, s) \
1067 CheckTriggeredElementChangeExt(x, y, e, ev, p, s, -1)
1068 #define CheckTriggeredElementChangeBySide(x, y, e, ev, s) \
1069 CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, s, -1)
1070 #define CheckTriggeredElementChangeByPage(x, y, e, ev, p) \
1071 CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY, CH_SIDE_ANY, p)
1073 static boolean CheckElementChangeExt(int, int, int, int, int, int, int);
1074 #define CheckElementChange(x, y, e, te, ev) \
1075 CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, CH_SIDE_ANY)
1076 #define CheckElementChangeByPlayer(x, y, e, ev, p, s) \
1077 CheckElementChangeExt(x, y, e, EL_EMPTY, ev, p, s)
1078 #define CheckElementChangeBySide(x, y, e, te, ev, s) \
1079 CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, s)
1081 static void PlayLevelSound(int, int, int);
1082 static void PlayLevelSoundNearest(int, int, int);
1083 static void PlayLevelSoundAction(int, int, int);
1084 static void PlayLevelSoundElementAction(int, int, int, int);
1085 static void PlayLevelSoundElementActionIfLoop(int, int, int, int);
1086 static void PlayLevelSoundActionIfLoop(int, int, int);
1087 static void StopLevelSoundActionIfLoop(int, int, int);
1088 static void PlayLevelMusic();
1089 static void FadeLevelSoundsAndMusic();
1091 static void HandleGameButtons(struct GadgetInfo *);
1093 int AmoebeNachbarNr(int, int);
1094 void AmoebeUmwandeln(int, int);
1095 void ContinueMoving(int, int);
1096 void Bang(int, int);
1097 void InitMovDir(int, int);
1098 void InitAmoebaNr(int, int);
1099 int NewHiScore(void);
1101 void TestIfGoodThingHitsBadThing(int, int, int);
1102 void TestIfBadThingHitsGoodThing(int, int, int);
1103 void TestIfPlayerTouchesBadThing(int, int);
1104 void TestIfPlayerRunsIntoBadThing(int, int, int);
1105 void TestIfBadThingTouchesPlayer(int, int);
1106 void TestIfBadThingRunsIntoPlayer(int, int, int);
1107 void TestIfFriendTouchesBadThing(int, int);
1108 void TestIfBadThingTouchesFriend(int, int);
1109 void TestIfBadThingTouchesOtherBadThing(int, int);
1110 void TestIfGoodThingGetsHitByBadThing(int, int, int);
1112 void KillPlayer(struct PlayerInfo *);
1113 void BuryPlayer(struct PlayerInfo *);
1114 void RemovePlayer(struct PlayerInfo *);
1116 static int getInvisibleActiveFromInvisibleElement(int);
1117 static int getInvisibleFromInvisibleActiveElement(int);
1119 static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
1121 /* for detection of endless loops, caused by custom element programming */
1122 /* (using maximal playfield width x 10 is just a rough approximation) */
1123 #define MAX_ELEMENT_CHANGE_RECURSION_DEPTH (MAX_PLAYFIELD_WIDTH * 10)
1125 #define RECURSION_LOOP_DETECTION_START(e, rc) \
1127 if (recursion_loop_detected) \
1130 if (recursion_loop_depth > MAX_ELEMENT_CHANGE_RECURSION_DEPTH) \
1132 recursion_loop_detected = TRUE; \
1133 recursion_loop_element = (e); \
1136 recursion_loop_depth++; \
1139 #define RECURSION_LOOP_DETECTION_END() \
1141 recursion_loop_depth--; \
1144 static int recursion_loop_depth;
1145 static boolean recursion_loop_detected;
1146 static boolean recursion_loop_element;
1148 static int map_player_action[MAX_PLAYERS];
1151 /* ------------------------------------------------------------------------- */
1152 /* definition of elements that automatically change to other elements after */
1153 /* a specified time, eventually calling a function when changing */
1154 /* ------------------------------------------------------------------------- */
1156 /* forward declaration for changer functions */
1157 static void InitBuggyBase(int, int);
1158 static void WarnBuggyBase(int, int);
1160 static void InitTrap(int, int);
1161 static void ActivateTrap(int, int);
1162 static void ChangeActiveTrap(int, int);
1164 static void InitRobotWheel(int, int);
1165 static void RunRobotWheel(int, int);
1166 static void StopRobotWheel(int, int);
1168 static void InitTimegateWheel(int, int);
1169 static void RunTimegateWheel(int, int);
1171 static void InitMagicBallDelay(int, int);
1172 static void ActivateMagicBall(int, int);
1174 struct ChangingElementInfo
1179 void (*pre_change_function)(int x, int y);
1180 void (*change_function)(int x, int y);
1181 void (*post_change_function)(int x, int y);
1184 static struct ChangingElementInfo change_delay_list[] =
1219 EL_STEEL_EXIT_OPENING,
1227 EL_STEEL_EXIT_CLOSING,
1228 EL_STEEL_EXIT_CLOSED,
1251 EL_EM_STEEL_EXIT_OPENING,
1252 EL_EM_STEEL_EXIT_OPEN,
1259 EL_EM_STEEL_EXIT_CLOSING,
1283 EL_SWITCHGATE_OPENING,
1291 EL_SWITCHGATE_CLOSING,
1292 EL_SWITCHGATE_CLOSED,
1299 EL_TIMEGATE_OPENING,
1307 EL_TIMEGATE_CLOSING,
1316 EL_ACID_SPLASH_LEFT,
1324 EL_ACID_SPLASH_RIGHT,
1333 EL_SP_BUGGY_BASE_ACTIVATING,
1340 EL_SP_BUGGY_BASE_ACTIVATING,
1341 EL_SP_BUGGY_BASE_ACTIVE,
1348 EL_SP_BUGGY_BASE_ACTIVE,
1372 EL_ROBOT_WHEEL_ACTIVE,
1380 EL_TIMEGATE_SWITCH_ACTIVE,
1388 EL_DC_TIMEGATE_SWITCH_ACTIVE,
1389 EL_DC_TIMEGATE_SWITCH,
1396 EL_EMC_MAGIC_BALL_ACTIVE,
1397 EL_EMC_MAGIC_BALL_ACTIVE,
1404 EL_EMC_SPRING_BUMPER_ACTIVE,
1405 EL_EMC_SPRING_BUMPER,
1412 EL_DIAGONAL_SHRINKING,
1420 EL_DIAGONAL_GROWING,
1441 int push_delay_fixed, push_delay_random;
1445 { EL_SPRING, 0, 0 },
1446 { EL_BALLOON, 0, 0 },
1448 { EL_SOKOBAN_OBJECT, 2, 0 },
1449 { EL_SOKOBAN_FIELD_FULL, 2, 0 },
1450 { EL_SATELLITE, 2, 0 },
1451 { EL_SP_DISK_YELLOW, 2, 0 },
1453 { EL_UNDEFINED, 0, 0 },
1461 move_stepsize_list[] =
1463 { EL_AMOEBA_DROP, 2 },
1464 { EL_AMOEBA_DROPPING, 2 },
1465 { EL_QUICKSAND_FILLING, 1 },
1466 { EL_QUICKSAND_EMPTYING, 1 },
1467 { EL_QUICKSAND_FAST_FILLING, 2 },
1468 { EL_QUICKSAND_FAST_EMPTYING, 2 },
1469 { EL_MAGIC_WALL_FILLING, 2 },
1470 { EL_MAGIC_WALL_EMPTYING, 2 },
1471 { EL_BD_MAGIC_WALL_FILLING, 2 },
1472 { EL_BD_MAGIC_WALL_EMPTYING, 2 },
1473 { EL_DC_MAGIC_WALL_FILLING, 2 },
1474 { EL_DC_MAGIC_WALL_EMPTYING, 2 },
1476 { EL_UNDEFINED, 0 },
1484 collect_count_list[] =
1487 { EL_BD_DIAMOND, 1 },
1488 { EL_EMERALD_YELLOW, 1 },
1489 { EL_EMERALD_RED, 1 },
1490 { EL_EMERALD_PURPLE, 1 },
1492 { EL_SP_INFOTRON, 1 },
1496 { EL_UNDEFINED, 0 },
1504 access_direction_list[] =
1506 { EL_TUBE_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1507 { EL_TUBE_VERTICAL, MV_UP | MV_DOWN },
1508 { EL_TUBE_HORIZONTAL, MV_LEFT | MV_RIGHT },
1509 { EL_TUBE_VERTICAL_LEFT, MV_LEFT | MV_UP | MV_DOWN },
1510 { EL_TUBE_VERTICAL_RIGHT, MV_RIGHT | MV_UP | MV_DOWN },
1511 { EL_TUBE_HORIZONTAL_UP, MV_LEFT | MV_RIGHT | MV_UP },
1512 { EL_TUBE_HORIZONTAL_DOWN, MV_LEFT | MV_RIGHT | MV_DOWN },
1513 { EL_TUBE_LEFT_UP, MV_LEFT | MV_UP },
1514 { EL_TUBE_LEFT_DOWN, MV_LEFT | MV_DOWN },
1515 { EL_TUBE_RIGHT_UP, MV_RIGHT | MV_UP },
1516 { EL_TUBE_RIGHT_DOWN, MV_RIGHT | MV_DOWN },
1518 { EL_SP_PORT_LEFT, MV_RIGHT },
1519 { EL_SP_PORT_RIGHT, MV_LEFT },
1520 { EL_SP_PORT_UP, MV_DOWN },
1521 { EL_SP_PORT_DOWN, MV_UP },
1522 { EL_SP_PORT_HORIZONTAL, MV_LEFT | MV_RIGHT },
1523 { EL_SP_PORT_VERTICAL, MV_UP | MV_DOWN },
1524 { EL_SP_PORT_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1525 { EL_SP_GRAVITY_PORT_LEFT, MV_RIGHT },
1526 { EL_SP_GRAVITY_PORT_RIGHT, MV_LEFT },
1527 { EL_SP_GRAVITY_PORT_UP, MV_DOWN },
1528 { EL_SP_GRAVITY_PORT_DOWN, MV_UP },
1529 { EL_SP_GRAVITY_ON_PORT_LEFT, MV_RIGHT },
1530 { EL_SP_GRAVITY_ON_PORT_RIGHT, MV_LEFT },
1531 { EL_SP_GRAVITY_ON_PORT_UP, MV_DOWN },
1532 { EL_SP_GRAVITY_ON_PORT_DOWN, MV_UP },
1533 { EL_SP_GRAVITY_OFF_PORT_LEFT, MV_RIGHT },
1534 { EL_SP_GRAVITY_OFF_PORT_RIGHT, MV_LEFT },
1535 { EL_SP_GRAVITY_OFF_PORT_UP, MV_DOWN },
1536 { EL_SP_GRAVITY_OFF_PORT_DOWN, MV_UP },
1538 { EL_UNDEFINED, MV_NONE }
1541 static boolean trigger_events[MAX_NUM_ELEMENTS][NUM_CHANGE_EVENTS];
1543 #define IS_AUTO_CHANGING(e) (element_info[e].has_change_event[CE_DELAY])
1544 #define IS_JUST_CHANGING(x, y) (ChangeDelay[x][y] != 0)
1545 #define IS_CHANGING(x, y) (IS_AUTO_CHANGING(Feld[x][y]) || \
1546 IS_JUST_CHANGING(x, y))
1548 #define CE_PAGE(e, ce) (element_info[e].event_page[ce])
1550 /* static variables for playfield scan mode (scanning forward or backward) */
1551 static int playfield_scan_start_x = 0;
1552 static int playfield_scan_start_y = 0;
1553 static int playfield_scan_delta_x = 1;
1554 static int playfield_scan_delta_y = 1;
1556 #define SCAN_PLAYFIELD(x, y) for ((y) = playfield_scan_start_y; \
1557 (y) >= 0 && (y) <= lev_fieldy - 1; \
1558 (y) += playfield_scan_delta_y) \
1559 for ((x) = playfield_scan_start_x; \
1560 (x) >= 0 && (x) <= lev_fieldx - 1; \
1561 (x) += playfield_scan_delta_x)
1564 void DEBUG_SetMaximumDynamite()
1568 for (i = 0; i < MAX_INVENTORY_SIZE; i++)
1569 if (local_player->inventory_size < MAX_INVENTORY_SIZE)
1570 local_player->inventory_element[local_player->inventory_size++] =
1575 static void InitPlayfieldScanModeVars()
1577 if (game.use_reverse_scan_direction)
1579 playfield_scan_start_x = lev_fieldx - 1;
1580 playfield_scan_start_y = lev_fieldy - 1;
1582 playfield_scan_delta_x = -1;
1583 playfield_scan_delta_y = -1;
1587 playfield_scan_start_x = 0;
1588 playfield_scan_start_y = 0;
1590 playfield_scan_delta_x = 1;
1591 playfield_scan_delta_y = 1;
1595 static void InitPlayfieldScanMode(int mode)
1597 game.use_reverse_scan_direction =
1598 (mode == CA_ARG_SCAN_MODE_REVERSE ? TRUE : FALSE);
1600 InitPlayfieldScanModeVars();
1603 static int get_move_delay_from_stepsize(int move_stepsize)
1606 MIN(MAX(MOVE_STEPSIZE_MIN, move_stepsize), MOVE_STEPSIZE_MAX);
1608 /* make sure that stepsize value is always a power of 2 */
1609 move_stepsize = (1 << log_2(move_stepsize));
1611 return TILEX / move_stepsize;
1614 static void SetPlayerMoveSpeed(struct PlayerInfo *player, int move_stepsize,
1617 int player_nr = player->index_nr;
1618 int move_delay = get_move_delay_from_stepsize(move_stepsize);
1619 boolean cannot_move = (move_stepsize == STEPSIZE_NOT_MOVING ? TRUE : FALSE);
1621 /* do no immediately change move delay -- the player might just be moving */
1622 player->move_delay_value_next = move_delay;
1624 /* information if player can move must be set separately */
1625 player->cannot_move = cannot_move;
1629 player->move_delay = game.initial_move_delay[player_nr];
1630 player->move_delay_value = game.initial_move_delay_value[player_nr];
1632 player->move_delay_value_next = -1;
1634 player->move_delay_reset_counter = 0;
1638 void GetPlayerConfig()
1640 GameFrameDelay = setup.game_frame_delay;
1642 if (!audio.sound_available)
1643 setup.sound_simple = FALSE;
1645 if (!audio.loops_available)
1646 setup.sound_loops = FALSE;
1648 if (!audio.music_available)
1649 setup.sound_music = FALSE;
1651 if (!video.fullscreen_available)
1652 setup.fullscreen = FALSE;
1654 setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
1656 SetAudioMode(setup.sound);
1659 int GetElementFromGroupElement(int element)
1661 if (IS_GROUP_ELEMENT(element))
1663 struct ElementGroupInfo *group = element_info[element].group;
1664 int last_anim_random_frame = gfx.anim_random_frame;
1667 if (group->choice_mode == ANIM_RANDOM)
1668 gfx.anim_random_frame = RND(group->num_elements_resolved);
1670 element_pos = getAnimationFrame(group->num_elements_resolved, 1,
1671 group->choice_mode, 0,
1674 if (group->choice_mode == ANIM_RANDOM)
1675 gfx.anim_random_frame = last_anim_random_frame;
1677 group->choice_pos++;
1679 element = group->element_resolved[element_pos];
1685 static void InitPlayerField(int x, int y, int element, boolean init_game)
1687 if (element == EL_SP_MURPHY)
1691 if (stored_player[0].present)
1693 Feld[x][y] = EL_SP_MURPHY_CLONE;
1699 stored_player[0].initial_element = element;
1700 stored_player[0].use_murphy = TRUE;
1702 if (!level.use_artwork_element[0])
1703 stored_player[0].artwork_element = EL_SP_MURPHY;
1706 Feld[x][y] = EL_PLAYER_1;
1712 struct PlayerInfo *player = &stored_player[Feld[x][y] - EL_PLAYER_1];
1713 int jx = player->jx, jy = player->jy;
1715 player->present = TRUE;
1717 player->block_last_field = (element == EL_SP_MURPHY ?
1718 level.sp_block_last_field :
1719 level.block_last_field);
1721 /* ---------- initialize player's last field block delay --------------- */
1723 /* always start with reliable default value (no adjustment needed) */
1724 player->block_delay_adjustment = 0;
1726 /* special case 1: in Supaplex, Murphy blocks last field one more frame */
1727 if (player->block_last_field && element == EL_SP_MURPHY)
1728 player->block_delay_adjustment = 1;
1730 /* special case 2: in game engines before 3.1.1, blocking was different */
1731 if (game.use_block_last_field_bug)
1732 player->block_delay_adjustment = (player->block_last_field ? -1 : 1);
1734 if (!options.network || player->connected)
1736 player->active = TRUE;
1738 /* remove potentially duplicate players */
1739 if (StorePlayer[jx][jy] == Feld[x][y])
1740 StorePlayer[jx][jy] = 0;
1742 StorePlayer[x][y] = Feld[x][y];
1744 #if DEBUG_INIT_PLAYER
1747 printf("- player element %d activated", player->element_nr);
1748 printf(" (local player is %d and currently %s)\n",
1749 local_player->element_nr,
1750 local_player->active ? "active" : "not active");
1755 Feld[x][y] = EL_EMPTY;
1757 player->jx = player->last_jx = x;
1758 player->jy = player->last_jy = y;
1763 int player_nr = GET_PLAYER_NR(element);
1764 struct PlayerInfo *player = &stored_player[player_nr];
1766 if (player->active && player->killed)
1767 player->reanimated = TRUE; /* if player was just killed, reanimate him */
1771 static void InitField(int x, int y, boolean init_game)
1773 int element = Feld[x][y];
1782 InitPlayerField(x, y, element, init_game);
1785 case EL_SOKOBAN_FIELD_PLAYER:
1786 element = Feld[x][y] = EL_PLAYER_1;
1787 InitField(x, y, init_game);
1789 element = Feld[x][y] = EL_SOKOBAN_FIELD_EMPTY;
1790 InitField(x, y, init_game);
1793 case EL_SOKOBAN_FIELD_EMPTY:
1794 local_player->sokobanfields_still_needed++;
1798 if (x < lev_fieldx-1 && Feld[x+1][y] == EL_ACID)
1799 Feld[x][y] = EL_ACID_POOL_TOPLEFT;
1800 else if (x > 0 && Feld[x-1][y] == EL_ACID)
1801 Feld[x][y] = EL_ACID_POOL_TOPRIGHT;
1802 else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPLEFT)
1803 Feld[x][y] = EL_ACID_POOL_BOTTOMLEFT;
1804 else if (y > 0 && Feld[x][y-1] == EL_ACID)
1805 Feld[x][y] = EL_ACID_POOL_BOTTOM;
1806 else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPRIGHT)
1807 Feld[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
1816 case EL_SPACESHIP_RIGHT:
1817 case EL_SPACESHIP_UP:
1818 case EL_SPACESHIP_LEFT:
1819 case EL_SPACESHIP_DOWN:
1820 case EL_BD_BUTTERFLY:
1821 case EL_BD_BUTTERFLY_RIGHT:
1822 case EL_BD_BUTTERFLY_UP:
1823 case EL_BD_BUTTERFLY_LEFT:
1824 case EL_BD_BUTTERFLY_DOWN:
1826 case EL_BD_FIREFLY_RIGHT:
1827 case EL_BD_FIREFLY_UP:
1828 case EL_BD_FIREFLY_LEFT:
1829 case EL_BD_FIREFLY_DOWN:
1830 case EL_PACMAN_RIGHT:
1832 case EL_PACMAN_LEFT:
1833 case EL_PACMAN_DOWN:
1835 case EL_YAMYAM_LEFT:
1836 case EL_YAMYAM_RIGHT:
1838 case EL_YAMYAM_DOWN:
1839 case EL_DARK_YAMYAM:
1842 case EL_SP_SNIKSNAK:
1843 case EL_SP_ELECTRON:
1852 case EL_AMOEBA_FULL:
1857 case EL_AMOEBA_DROP:
1858 if (y == lev_fieldy - 1)
1860 Feld[x][y] = EL_AMOEBA_GROWING;
1861 Store[x][y] = EL_AMOEBA_WET;
1865 case EL_DYNAMITE_ACTIVE:
1866 case EL_SP_DISK_RED_ACTIVE:
1867 case EL_DYNABOMB_PLAYER_1_ACTIVE:
1868 case EL_DYNABOMB_PLAYER_2_ACTIVE:
1869 case EL_DYNABOMB_PLAYER_3_ACTIVE:
1870 case EL_DYNABOMB_PLAYER_4_ACTIVE:
1871 MovDelay[x][y] = 96;
1874 case EL_EM_DYNAMITE_ACTIVE:
1875 MovDelay[x][y] = 32;
1879 local_player->lights_still_needed++;
1883 local_player->friends_still_needed++;
1888 GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
1891 case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
1892 case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
1893 case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
1894 case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
1895 case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
1896 case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
1897 case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
1898 case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
1899 case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
1900 case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
1901 case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
1902 case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
1905 int belt_nr = getBeltNrFromBeltSwitchElement(Feld[x][y]);
1906 int belt_dir = getBeltDirFromBeltSwitchElement(Feld[x][y]);
1907 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Feld[x][y]);
1909 if (game.belt_dir_nr[belt_nr] == 3) /* initial value */
1911 game.belt_dir[belt_nr] = belt_dir;
1912 game.belt_dir_nr[belt_nr] = belt_dir_nr;
1914 else /* more than one switch -- set it like the first switch */
1916 Feld[x][y] = Feld[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
1921 case EL_LIGHT_SWITCH_ACTIVE:
1923 game.light_time_left = level.time_light * FRAMES_PER_SECOND;
1926 case EL_INVISIBLE_STEELWALL:
1927 case EL_INVISIBLE_WALL:
1928 case EL_INVISIBLE_SAND:
1929 if (game.light_time_left > 0 ||
1930 game.lenses_time_left > 0)
1931 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
1934 case EL_EMC_MAGIC_BALL:
1935 if (game.ball_state)
1936 Feld[x][y] = EL_EMC_MAGIC_BALL_ACTIVE;
1939 case EL_EMC_MAGIC_BALL_SWITCH:
1940 if (game.ball_state)
1941 Feld[x][y] = EL_EMC_MAGIC_BALL_SWITCH_ACTIVE;
1944 case EL_TRIGGER_PLAYER:
1945 case EL_TRIGGER_ELEMENT:
1946 case EL_TRIGGER_CE_VALUE:
1947 case EL_TRIGGER_CE_SCORE:
1949 case EL_ANY_ELEMENT:
1950 case EL_CURRENT_CE_VALUE:
1951 case EL_CURRENT_CE_SCORE:
1968 /* reference elements should not be used on the playfield */
1969 Feld[x][y] = EL_EMPTY;
1973 if (IS_CUSTOM_ELEMENT(element))
1975 if (CAN_MOVE(element))
1978 if (!element_info[element].use_last_ce_value || init_game)
1979 CustomValue[x][y] = GET_NEW_CE_VALUE(Feld[x][y]);
1981 else if (IS_GROUP_ELEMENT(element))
1983 Feld[x][y] = GetElementFromGroupElement(element);
1985 InitField(x, y, init_game);
1992 CheckTriggeredElementChange(x, y, element, CE_CREATION_OF_X);
1995 inline static void InitField_WithBug1(int x, int y, boolean init_game)
1997 InitField(x, y, init_game);
1999 /* not needed to call InitMovDir() -- already done by InitField()! */
2000 if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2001 CAN_MOVE(Feld[x][y]))
2005 inline static void InitField_WithBug2(int x, int y, boolean init_game)
2007 int old_element = Feld[x][y];
2009 InitField(x, y, init_game);
2011 /* not needed to call InitMovDir() -- already done by InitField()! */
2012 if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2013 CAN_MOVE(old_element) &&
2014 (old_element < EL_MOLE_LEFT || old_element > EL_MOLE_DOWN))
2017 /* this case is in fact a combination of not less than three bugs:
2018 first, it calls InitMovDir() for elements that can move, although this is
2019 already done by InitField(); then, it checks the element that was at this
2020 field _before_ the call to InitField() (which can change it); lastly, it
2021 was not called for "mole with direction" elements, which were treated as
2022 "cannot move" due to (fixed) wrong element initialization in "src/init.c"
2026 static int get_key_element_from_nr(int key_nr)
2028 int key_base_element = (key_nr >= STD_NUM_KEYS ? EL_EMC_KEY_5 - STD_NUM_KEYS :
2029 level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2030 EL_EM_KEY_1 : EL_KEY_1);
2032 return key_base_element + key_nr;
2035 static int get_next_dropped_element(struct PlayerInfo *player)
2037 return (player->inventory_size > 0 ?
2038 player->inventory_element[player->inventory_size - 1] :
2039 player->inventory_infinite_element != EL_UNDEFINED ?
2040 player->inventory_infinite_element :
2041 player->dynabombs_left > 0 ?
2042 EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
2046 static int get_inventory_element_from_pos(struct PlayerInfo *player, int pos)
2048 /* pos >= 0: get element from bottom of the stack;
2049 pos < 0: get element from top of the stack */
2053 int min_inventory_size = -pos;
2054 int inventory_pos = player->inventory_size - min_inventory_size;
2055 int min_dynabombs_left = min_inventory_size - player->inventory_size;
2057 return (player->inventory_size >= min_inventory_size ?
2058 player->inventory_element[inventory_pos] :
2059 player->inventory_infinite_element != EL_UNDEFINED ?
2060 player->inventory_infinite_element :
2061 player->dynabombs_left >= min_dynabombs_left ?
2062 EL_DYNABOMB_PLAYER_1 + player->index_nr :
2067 int min_dynabombs_left = pos + 1;
2068 int min_inventory_size = pos + 1 - player->dynabombs_left;
2069 int inventory_pos = pos - player->dynabombs_left;
2071 return (player->inventory_infinite_element != EL_UNDEFINED ?
2072 player->inventory_infinite_element :
2073 player->dynabombs_left >= min_dynabombs_left ?
2074 EL_DYNABOMB_PLAYER_1 + player->index_nr :
2075 player->inventory_size >= min_inventory_size ?
2076 player->inventory_element[inventory_pos] :
2081 static int compareGamePanelOrderInfo(const void *object1, const void *object2)
2083 const struct GamePanelOrderInfo *gpo1 = (struct GamePanelOrderInfo *)object1;
2084 const struct GamePanelOrderInfo *gpo2 = (struct GamePanelOrderInfo *)object2;
2087 if (gpo1->sort_priority != gpo2->sort_priority)
2088 compare_result = gpo1->sort_priority - gpo2->sort_priority;
2090 compare_result = gpo1->nr - gpo2->nr;
2092 return compare_result;
2095 int getPlayerInventorySize(int player_nr)
2097 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2098 return level.native_em_level->ply[player_nr]->dynamite;
2099 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
2100 return level.native_sp_level->game_sp->red_disk_count;
2102 return stored_player[player_nr].inventory_size;
2105 void InitGameControlValues()
2109 for (i = 0; game_panel_controls[i].nr != -1; i++)
2111 struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2112 struct GamePanelOrderInfo *gpo = &game_panel_order[i];
2113 struct TextPosInfo *pos = gpc->pos;
2115 int type = gpc->type;
2119 Error(ERR_INFO, "'game_panel_controls' structure corrupted at %d", i);
2120 Error(ERR_EXIT, "this should not happen -- please debug");
2123 /* force update of game controls after initialization */
2124 gpc->value = gpc->last_value = -1;
2125 gpc->frame = gpc->last_frame = -1;
2126 gpc->gfx_frame = -1;
2128 /* determine panel value width for later calculation of alignment */
2129 if (type == TYPE_INTEGER || type == TYPE_STRING)
2131 pos->width = pos->size * getFontWidth(pos->font);
2132 pos->height = getFontHeight(pos->font);
2134 else if (type == TYPE_ELEMENT)
2136 pos->width = pos->size;
2137 pos->height = pos->size;
2140 /* fill structure for game panel draw order */
2142 gpo->sort_priority = pos->sort_priority;
2145 /* sort game panel controls according to sort_priority and control number */
2146 qsort(game_panel_order, NUM_GAME_PANEL_CONTROLS,
2147 sizeof(struct GamePanelOrderInfo), compareGamePanelOrderInfo);
2150 void UpdatePlayfieldElementCount()
2152 boolean use_element_count = FALSE;
2155 /* first check if it is needed at all to calculate playfield element count */
2156 for (i = GAME_PANEL_ELEMENT_COUNT_1; i <= GAME_PANEL_ELEMENT_COUNT_8; i++)
2157 if (!PANEL_DEACTIVATED(game_panel_controls[i].pos))
2158 use_element_count = TRUE;
2160 if (!use_element_count)
2163 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2164 element_info[i].element_count = 0;
2166 SCAN_PLAYFIELD(x, y)
2168 element_info[Feld[x][y]].element_count++;
2171 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
2172 for (j = 0; j < MAX_NUM_ELEMENTS; j++)
2173 if (IS_IN_GROUP(j, i))
2174 element_info[EL_GROUP_START + i].element_count +=
2175 element_info[j].element_count;
2178 void UpdateGameControlValues()
2181 int time = (local_player->LevelSolved ?
2182 local_player->LevelSolved_CountingTime :
2183 level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2184 level.native_em_level->lev->time :
2185 level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2186 level.native_sp_level->game_sp->time_played :
2187 level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2188 game_mm.energy_left :
2189 game.no_time_limit ? TimePlayed : TimeLeft);
2190 int score = (local_player->LevelSolved ?
2191 local_player->LevelSolved_CountingScore :
2192 level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2193 level.native_em_level->lev->score :
2194 level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2195 level.native_sp_level->game_sp->score :
2196 level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2198 local_player->score);
2199 int gems = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2200 level.native_em_level->lev->required :
2201 level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2202 level.native_sp_level->game_sp->infotrons_still_needed :
2203 level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2204 game_mm.kettles_still_needed :
2205 local_player->gems_still_needed);
2206 int exit_closed = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2207 level.native_em_level->lev->required > 0 :
2208 level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2209 level.native_sp_level->game_sp->infotrons_still_needed > 0 :
2210 level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2211 game_mm.kettles_still_needed > 0 ||
2212 game_mm.lights_still_needed > 0 :
2213 local_player->gems_still_needed > 0 ||
2214 local_player->sokobanfields_still_needed > 0 ||
2215 local_player->lights_still_needed > 0);
2216 int health = (local_player->LevelSolved ?
2217 local_player->LevelSolved_CountingHealth :
2218 level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2219 MM_HEALTH(game_mm.laser_overload_value) :
2220 local_player->health);
2222 UpdatePlayfieldElementCount();
2224 /* update game panel control values */
2226 game_panel_controls[GAME_PANEL_LEVEL_NUMBER].value = level_nr;
2227 game_panel_controls[GAME_PANEL_GEMS].value = gems;
2229 game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value = 0;
2230 for (i = 0; i < MAX_NUM_KEYS; i++)
2231 game_panel_controls[GAME_PANEL_KEY_1 + i].value = EL_EMPTY;
2232 game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_EMPTY;
2233 game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value = 0;
2235 if (game.centered_player_nr == -1)
2237 for (i = 0; i < MAX_PLAYERS; i++)
2239 /* only one player in Supaplex game engine */
2240 if (level.game_engine_type == GAME_ENGINE_TYPE_SP && i > 0)
2243 for (k = 0; k < MAX_NUM_KEYS; k++)
2245 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2247 if (level.native_em_level->ply[i]->keys & (1 << k))
2248 game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2249 get_key_element_from_nr(k);
2251 else if (stored_player[i].key[k])
2252 game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2253 get_key_element_from_nr(k);
2256 game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2257 getPlayerInventorySize(i);
2259 if (stored_player[i].num_white_keys > 0)
2260 game_panel_controls[GAME_PANEL_KEY_WHITE].value =
2263 game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2264 stored_player[i].num_white_keys;
2269 int player_nr = game.centered_player_nr;
2271 for (k = 0; k < MAX_NUM_KEYS; k++)
2273 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2275 if (level.native_em_level->ply[player_nr]->keys & (1 << k))
2276 game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2277 get_key_element_from_nr(k);
2279 else if (stored_player[player_nr].key[k])
2280 game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2281 get_key_element_from_nr(k);
2284 game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2285 getPlayerInventorySize(player_nr);
2287 if (stored_player[player_nr].num_white_keys > 0)
2288 game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_DC_KEY_WHITE;
2290 game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2291 stored_player[player_nr].num_white_keys;
2294 for (i = 0; i < NUM_PANEL_INVENTORY; i++)
2296 game_panel_controls[GAME_PANEL_INVENTORY_FIRST_1 + i].value =
2297 get_inventory_element_from_pos(local_player, i);
2298 game_panel_controls[GAME_PANEL_INVENTORY_LAST_1 + i].value =
2299 get_inventory_element_from_pos(local_player, -i - 1);
2302 game_panel_controls[GAME_PANEL_SCORE].value = score;
2303 game_panel_controls[GAME_PANEL_HIGHSCORE].value = highscore[0].Score;
2305 game_panel_controls[GAME_PANEL_TIME].value = time;
2307 game_panel_controls[GAME_PANEL_TIME_HH].value = time / 3600;
2308 game_panel_controls[GAME_PANEL_TIME_MM].value = (time / 60) % 60;
2309 game_panel_controls[GAME_PANEL_TIME_SS].value = time % 60;
2311 if (game.no_time_limit)
2312 game_panel_controls[GAME_PANEL_TIME_ANIM].value = 100;
2314 game_panel_controls[GAME_PANEL_TIME_ANIM].value = time * 100 / level.time;
2316 game_panel_controls[GAME_PANEL_HEALTH].value = health;
2317 game_panel_controls[GAME_PANEL_HEALTH_ANIM].value = health;
2319 game_panel_controls[GAME_PANEL_FRAME].value = FrameCounter;
2321 game_panel_controls[GAME_PANEL_SHIELD_NORMAL].value =
2322 (local_player->shield_normal_time_left > 0 ? EL_SHIELD_NORMAL_ACTIVE :
2324 game_panel_controls[GAME_PANEL_SHIELD_NORMAL_TIME].value =
2325 local_player->shield_normal_time_left;
2326 game_panel_controls[GAME_PANEL_SHIELD_DEADLY].value =
2327 (local_player->shield_deadly_time_left > 0 ? EL_SHIELD_DEADLY_ACTIVE :
2329 game_panel_controls[GAME_PANEL_SHIELD_DEADLY_TIME].value =
2330 local_player->shield_deadly_time_left;
2332 game_panel_controls[GAME_PANEL_EXIT].value =
2333 (exit_closed ? EL_EXIT_CLOSED : EL_EXIT_OPEN);
2335 game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL].value =
2336 (game.ball_state ? EL_EMC_MAGIC_BALL_ACTIVE : EL_EMC_MAGIC_BALL);
2337 game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL_SWITCH].value =
2338 (game.ball_state ? EL_EMC_MAGIC_BALL_SWITCH_ACTIVE :
2339 EL_EMC_MAGIC_BALL_SWITCH);
2341 game_panel_controls[GAME_PANEL_LIGHT_SWITCH].value =
2342 (game.light_time_left > 0 ? EL_LIGHT_SWITCH_ACTIVE : EL_LIGHT_SWITCH);
2343 game_panel_controls[GAME_PANEL_LIGHT_SWITCH_TIME].value =
2344 game.light_time_left;
2346 game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH].value =
2347 (game.timegate_time_left > 0 ? EL_TIMEGATE_OPEN : EL_TIMEGATE_CLOSED);
2348 game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH_TIME].value =
2349 game.timegate_time_left;
2351 game_panel_controls[GAME_PANEL_SWITCHGATE_SWITCH].value =
2352 EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
2354 game_panel_controls[GAME_PANEL_EMC_LENSES].value =
2355 (game.lenses_time_left > 0 ? EL_EMC_LENSES : EL_EMPTY);
2356 game_panel_controls[GAME_PANEL_EMC_LENSES_TIME].value =
2357 game.lenses_time_left;
2359 game_panel_controls[GAME_PANEL_EMC_MAGNIFIER].value =
2360 (game.magnify_time_left > 0 ? EL_EMC_MAGNIFIER : EL_EMPTY);
2361 game_panel_controls[GAME_PANEL_EMC_MAGNIFIER_TIME].value =
2362 game.magnify_time_left;
2364 game_panel_controls[GAME_PANEL_BALLOON_SWITCH].value =
2365 (game.wind_direction == MV_LEFT ? EL_BALLOON_SWITCH_LEFT :
2366 game.wind_direction == MV_RIGHT ? EL_BALLOON_SWITCH_RIGHT :
2367 game.wind_direction == MV_UP ? EL_BALLOON_SWITCH_UP :
2368 game.wind_direction == MV_DOWN ? EL_BALLOON_SWITCH_DOWN :
2369 EL_BALLOON_SWITCH_NONE);
2371 game_panel_controls[GAME_PANEL_DYNABOMB_NUMBER].value =
2372 local_player->dynabomb_count;
2373 game_panel_controls[GAME_PANEL_DYNABOMB_SIZE].value =
2374 local_player->dynabomb_size;
2375 game_panel_controls[GAME_PANEL_DYNABOMB_POWER].value =
2376 (local_player->dynabomb_xl ? EL_DYNABOMB_INCREASE_POWER : EL_EMPTY);
2378 game_panel_controls[GAME_PANEL_PENGUINS].value =
2379 local_player->friends_still_needed;
2381 game_panel_controls[GAME_PANEL_SOKOBAN_OBJECTS].value =
2382 local_player->sokobanfields_still_needed;
2383 game_panel_controls[GAME_PANEL_SOKOBAN_FIELDS].value =
2384 local_player->sokobanfields_still_needed;
2386 game_panel_controls[GAME_PANEL_ROBOT_WHEEL].value =
2387 (game.robot_wheel_active ? EL_ROBOT_WHEEL_ACTIVE : EL_ROBOT_WHEEL);
2389 for (i = 0; i < NUM_BELTS; i++)
2391 game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1 + i].value =
2392 (game.belt_dir[i] != MV_NONE ? EL_CONVEYOR_BELT_1_MIDDLE_ACTIVE :
2393 EL_CONVEYOR_BELT_1_MIDDLE) + i;
2394 game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1_SWITCH + i].value =
2395 getBeltSwitchElementFromBeltNrAndBeltDir(i, game.belt_dir[i]);
2398 game_panel_controls[GAME_PANEL_MAGIC_WALL].value =
2399 (game.magic_wall_active ? EL_MAGIC_WALL_ACTIVE : EL_MAGIC_WALL);
2400 game_panel_controls[GAME_PANEL_MAGIC_WALL_TIME].value =
2401 game.magic_wall_time_left;
2403 game_panel_controls[GAME_PANEL_GRAVITY_STATE].value =
2404 local_player->gravity;
2406 for (i = 0; i < NUM_PANEL_GRAPHICS; i++)
2407 game_panel_controls[GAME_PANEL_GRAPHIC_1 + i].value = EL_GRAPHIC_1 + i;
2409 for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2410 game_panel_controls[GAME_PANEL_ELEMENT_1 + i].value =
2411 (IS_DRAWABLE_ELEMENT(game.panel.element[i].id) ?
2412 game.panel.element[i].id : EL_UNDEFINED);
2414 for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2415 game_panel_controls[GAME_PANEL_ELEMENT_COUNT_1 + i].value =
2416 (IS_VALID_ELEMENT(game.panel.element_count[i].id) ?
2417 element_info[game.panel.element_count[i].id].element_count : 0);
2419 for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2420 game_panel_controls[GAME_PANEL_CE_SCORE_1 + i].value =
2421 (IS_CUSTOM_ELEMENT(game.panel.ce_score[i].id) ?
2422 element_info[game.panel.ce_score[i].id].collect_score : 0);
2424 for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2425 game_panel_controls[GAME_PANEL_CE_SCORE_1_ELEMENT + i].value =
2426 (IS_CUSTOM_ELEMENT(game.panel.ce_score_element[i].id) ?
2427 element_info[game.panel.ce_score_element[i].id].collect_score :
2430 game_panel_controls[GAME_PANEL_PLAYER_NAME].value = 0;
2431 game_panel_controls[GAME_PANEL_LEVEL_NAME].value = 0;
2432 game_panel_controls[GAME_PANEL_LEVEL_AUTHOR].value = 0;
2434 /* update game panel control frames */
2436 for (i = 0; game_panel_controls[i].nr != -1; i++)
2438 struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2440 if (gpc->type == TYPE_ELEMENT)
2442 if (gpc->value != EL_UNDEFINED && gpc->value != EL_EMPTY)
2444 int last_anim_random_frame = gfx.anim_random_frame;
2445 int element = gpc->value;
2446 int graphic = el2panelimg(element);
2448 if (gpc->value != gpc->last_value)
2451 gpc->gfx_random = INIT_GFX_RANDOM();
2457 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2458 IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2459 gpc->gfx_random = INIT_GFX_RANDOM();
2462 if (ANIM_MODE(graphic) == ANIM_RANDOM)
2463 gfx.anim_random_frame = gpc->gfx_random;
2465 if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
2466 gpc->gfx_frame = element_info[element].collect_score;
2468 gpc->frame = getGraphicAnimationFrame(el2panelimg(gpc->value),
2471 if (ANIM_MODE(graphic) == ANIM_RANDOM)
2472 gfx.anim_random_frame = last_anim_random_frame;
2475 else if (gpc->type == TYPE_GRAPHIC)
2477 if (gpc->graphic != IMG_UNDEFINED)
2479 int last_anim_random_frame = gfx.anim_random_frame;
2480 int graphic = gpc->graphic;
2482 if (gpc->value != gpc->last_value)
2485 gpc->gfx_random = INIT_GFX_RANDOM();
2491 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2492 IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2493 gpc->gfx_random = INIT_GFX_RANDOM();
2496 if (ANIM_MODE(graphic) == ANIM_RANDOM)
2497 gfx.anim_random_frame = gpc->gfx_random;
2499 gpc->frame = getGraphicAnimationFrame(graphic, gpc->gfx_frame);
2501 if (ANIM_MODE(graphic) == ANIM_RANDOM)
2502 gfx.anim_random_frame = last_anim_random_frame;
2508 void DisplayGameControlValues()
2510 boolean redraw_panel = FALSE;
2513 for (i = 0; game_panel_controls[i].nr != -1; i++)
2515 struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2517 if (PANEL_DEACTIVATED(gpc->pos))
2520 if (gpc->value == gpc->last_value &&
2521 gpc->frame == gpc->last_frame)
2524 redraw_panel = TRUE;
2530 /* copy default game door content to main double buffer */
2532 /* !!! CHECK AGAIN !!! */
2533 SetPanelBackground();
2534 // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
2535 DrawBackground(DX, DY, DXSIZE, DYSIZE);
2537 /* redraw game control buttons */
2538 RedrawGameButtons();
2540 SetGameStatus(GAME_MODE_PSEUDO_PANEL);
2542 for (i = 0; i < NUM_GAME_PANEL_CONTROLS; i++)
2544 int nr = game_panel_order[i].nr;
2545 struct GamePanelControlInfo *gpc = &game_panel_controls[nr];
2546 struct TextPosInfo *pos = gpc->pos;
2547 int type = gpc->type;
2548 int value = gpc->value;
2549 int frame = gpc->frame;
2550 int size = pos->size;
2551 int font = pos->font;
2552 boolean draw_masked = pos->draw_masked;
2553 int mask_mode = (draw_masked ? BLIT_MASKED : BLIT_OPAQUE);
2555 if (PANEL_DEACTIVATED(pos))
2558 gpc->last_value = value;
2559 gpc->last_frame = frame;
2561 if (type == TYPE_INTEGER)
2563 if (nr == GAME_PANEL_LEVEL_NUMBER ||
2564 nr == GAME_PANEL_TIME)
2566 boolean use_dynamic_size = (size == -1 ? TRUE : FALSE);
2568 if (use_dynamic_size) /* use dynamic number of digits */
2570 int value_change = (nr == GAME_PANEL_LEVEL_NUMBER ? 100 : 1000);
2571 int size1 = (nr == GAME_PANEL_LEVEL_NUMBER ? 2 : 3);
2572 int size2 = size1 + 1;
2573 int font1 = pos->font;
2574 int font2 = pos->font_alt;
2576 size = (value < value_change ? size1 : size2);
2577 font = (value < value_change ? font1 : font2);
2581 /* correct text size if "digits" is zero or less */
2583 size = strlen(int2str(value, size));
2585 /* dynamically correct text alignment */
2586 pos->width = size * getFontWidth(font);
2588 DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2589 int2str(value, size), font, mask_mode);
2591 else if (type == TYPE_ELEMENT)
2593 int element, graphic;
2597 int dst_x = PANEL_XPOS(pos);
2598 int dst_y = PANEL_YPOS(pos);
2600 if (value != EL_UNDEFINED && value != EL_EMPTY)
2603 graphic = el2panelimg(value);
2605 // printf("::: %d, '%s' [%d]\n", element, EL_NAME(element), size);
2607 if (element >= EL_GRAPHIC_1 && element <= EL_GRAPHIC_8 && size == 0)
2610 getSizedGraphicSource(graphic, frame, size, &src_bitmap,
2613 width = graphic_info[graphic].width * size / TILESIZE;
2614 height = graphic_info[graphic].height * size / TILESIZE;
2617 BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2620 BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2624 else if (type == TYPE_GRAPHIC)
2626 int graphic = gpc->graphic;
2627 int graphic_active = gpc->graphic_active;
2631 int dst_x = PANEL_XPOS(pos);
2632 int dst_y = PANEL_YPOS(pos);
2633 boolean skip = (pos->class == get_hash_from_key("mm_engine_only") &&
2634 level.game_engine_type != GAME_ENGINE_TYPE_MM);
2636 if (graphic != IMG_UNDEFINED && !skip)
2638 if (pos->style == STYLE_REVERSE)
2639 value = 100 - value;
2641 getGraphicSource(graphic_active, frame, &src_bitmap, &src_x, &src_y);
2643 if (pos->direction & MV_HORIZONTAL)
2645 width = graphic_info[graphic_active].width * value / 100;
2646 height = graphic_info[graphic_active].height;
2648 if (pos->direction == MV_LEFT)
2650 src_x += graphic_info[graphic_active].width - width;
2651 dst_x += graphic_info[graphic_active].width - width;
2656 width = graphic_info[graphic_active].width;
2657 height = graphic_info[graphic_active].height * value / 100;
2659 if (pos->direction == MV_UP)
2661 src_y += graphic_info[graphic_active].height - height;
2662 dst_y += graphic_info[graphic_active].height - height;
2667 BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2670 BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2673 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
2675 if (pos->direction & MV_HORIZONTAL)
2677 if (pos->direction == MV_RIGHT)
2684 dst_x = PANEL_XPOS(pos);
2687 width = graphic_info[graphic].width - width;
2691 if (pos->direction == MV_DOWN)
2698 dst_y = PANEL_YPOS(pos);
2701 height = graphic_info[graphic].height - height;
2705 BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2708 BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2712 else if (type == TYPE_STRING)
2714 boolean active = (value != 0);
2715 char *state_normal = "off";
2716 char *state_active = "on";
2717 char *state = (active ? state_active : state_normal);
2718 char *s = (nr == GAME_PANEL_GRAVITY_STATE ? state :
2719 nr == GAME_PANEL_PLAYER_NAME ? setup.player_name :
2720 nr == GAME_PANEL_LEVEL_NAME ? level.name :
2721 nr == GAME_PANEL_LEVEL_AUTHOR ? level.author : NULL);
2723 if (nr == GAME_PANEL_GRAVITY_STATE)
2725 int font1 = pos->font; /* (used for normal state) */
2726 int font2 = pos->font_alt; /* (used for active state) */
2728 font = (active ? font2 : font1);
2737 /* don't truncate output if "chars" is zero or less */
2740 /* dynamically correct text alignment */
2741 pos->width = size * getFontWidth(font);
2744 s_cut = getStringCopyN(s, size);
2746 DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2747 s_cut, font, mask_mode);
2753 redraw_mask |= REDRAW_DOOR_1;
2756 SetGameStatus(GAME_MODE_PLAYING);
2759 void UpdateAndDisplayGameControlValues()
2761 if (tape.deactivate_display)
2764 UpdateGameControlValues();
2765 DisplayGameControlValues();
2768 void UpdateGameDoorValues()
2770 UpdateGameControlValues();
2773 void DrawGameDoorValues()
2775 DisplayGameControlValues();
2780 =============================================================================
2782 -----------------------------------------------------------------------------
2783 initialize game engine due to level / tape version number
2784 =============================================================================
2787 static void InitGameEngine()
2789 int i, j, k, l, x, y;
2791 /* set game engine from tape file when re-playing, else from level file */
2792 game.engine_version = (tape.playing ? tape.engine_version :
2793 level.game_version);
2795 /* set single or multi-player game mode (needed for re-playing tapes) */
2796 game.team_mode = setup.team_mode;
2800 int num_players = 0;
2802 for (i = 0; i < MAX_PLAYERS; i++)
2803 if (tape.player_participates[i])
2806 /* multi-player tapes contain input data for more than one player */
2807 game.team_mode = (num_players > 1);
2810 /* ---------------------------------------------------------------------- */
2811 /* set flags for bugs and changes according to active game engine version */
2812 /* ---------------------------------------------------------------------- */
2815 Summary of bugfix/change:
2816 Fixed handling for custom elements that change when pushed by the player.
2818 Fixed/changed in version:
2822 Before 3.1.0, custom elements that "change when pushing" changed directly
2823 after the player started pushing them (until then handled in "DigField()").
2824 Since 3.1.0, these custom elements are not changed until the "pushing"
2825 move of the element is finished (now handled in "ContinueMoving()").
2827 Affected levels/tapes:
2828 The first condition is generally needed for all levels/tapes before version
2829 3.1.0, which might use the old behaviour before it was changed; known tapes
2830 that are affected are some tapes from the level set "Walpurgis Gardens" by
2832 The second condition is an exception from the above case and is needed for
2833 the special case of tapes recorded with game (not engine!) version 3.1.0 or
2834 above (including some development versions of 3.1.0), but before it was
2835 known that this change would break tapes like the above and was fixed in
2836 3.1.1, so that the changed behaviour was active although the engine version
2837 while recording maybe was before 3.1.0. There is at least one tape that is
2838 affected by this exception, which is the tape for the one-level set "Bug
2839 Machine" by Juergen Bonhagen.
2842 game.use_change_when_pushing_bug =
2843 (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2845 tape.game_version >= VERSION_IDENT(3,1,0,0) &&
2846 tape.game_version < VERSION_IDENT(3,1,1,0)));
2849 Summary of bugfix/change:
2850 Fixed handling for blocking the field the player leaves when moving.
2852 Fixed/changed in version:
2856 Before 3.1.1, when "block last field when moving" was enabled, the field
2857 the player is leaving when moving was blocked for the time of the move,
2858 and was directly unblocked afterwards. This resulted in the last field
2859 being blocked for exactly one less than the number of frames of one player
2860 move. Additionally, even when blocking was disabled, the last field was
2861 blocked for exactly one frame.
2862 Since 3.1.1, due to changes in player movement handling, the last field
2863 is not blocked at all when blocking is disabled. When blocking is enabled,
2864 the last field is blocked for exactly the number of frames of one player
2865 move. Additionally, if the player is Murphy, the hero of Supaplex, the
2866 last field is blocked for exactly one more than the number of frames of
2869 Affected levels/tapes:
2870 (!!! yet to be determined -- probably many !!!)
2873 game.use_block_last_field_bug =
2874 (game.engine_version < VERSION_IDENT(3,1,1,0));
2876 game_em.use_single_button =
2877 (game.engine_version > VERSION_IDENT(4,0,0,2));
2879 game_em.use_snap_key_bug =
2880 (game.engine_version < VERSION_IDENT(4,0,1,0));
2882 /* ---------------------------------------------------------------------- */
2884 /* set maximal allowed number of custom element changes per game frame */
2885 game.max_num_changes_per_frame = 1;
2887 /* default scan direction: scan playfield from top/left to bottom/right */
2888 InitPlayfieldScanMode(CA_ARG_SCAN_MODE_NORMAL);
2890 /* dynamically adjust element properties according to game engine version */
2891 InitElementPropertiesEngine(game.engine_version);
2894 printf("level %d: level version == %06d\n", level_nr, level.game_version);
2895 printf(" tape version == %06d [%s] [file: %06d]\n",
2896 tape.engine_version, (tape.playing ? "PLAYING" : "RECORDING"),
2898 printf(" => game.engine_version == %06d\n", game.engine_version);
2901 /* ---------- initialize player's initial move delay --------------------- */
2903 /* dynamically adjust player properties according to level information */
2904 for (i = 0; i < MAX_PLAYERS; i++)
2905 game.initial_move_delay_value[i] =
2906 get_move_delay_from_stepsize(level.initial_player_stepsize[i]);
2908 /* dynamically adjust player properties according to game engine version */
2909 for (i = 0; i < MAX_PLAYERS; i++)
2910 game.initial_move_delay[i] =
2911 (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
2912 game.initial_move_delay_value[i] : 0);
2914 /* ---------- initialize player's initial push delay --------------------- */
2916 /* dynamically adjust player properties according to game engine version */
2917 game.initial_push_delay_value =
2918 (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
2920 /* ---------- initialize changing elements ------------------------------- */
2922 /* initialize changing elements information */
2923 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2925 struct ElementInfo *ei = &element_info[i];
2927 /* this pointer might have been changed in the level editor */
2928 ei->change = &ei->change_page[0];
2930 if (!IS_CUSTOM_ELEMENT(i))
2932 ei->change->target_element = EL_EMPTY_SPACE;
2933 ei->change->delay_fixed = 0;
2934 ei->change->delay_random = 0;
2935 ei->change->delay_frames = 1;
2938 for (j = 0; j < NUM_CHANGE_EVENTS; j++)
2940 ei->has_change_event[j] = FALSE;
2942 ei->event_page_nr[j] = 0;
2943 ei->event_page[j] = &ei->change_page[0];
2947 /* add changing elements from pre-defined list */
2948 for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
2950 struct ChangingElementInfo *ch_delay = &change_delay_list[i];
2951 struct ElementInfo *ei = &element_info[ch_delay->element];
2953 ei->change->target_element = ch_delay->target_element;
2954 ei->change->delay_fixed = ch_delay->change_delay;
2956 ei->change->pre_change_function = ch_delay->pre_change_function;
2957 ei->change->change_function = ch_delay->change_function;
2958 ei->change->post_change_function = ch_delay->post_change_function;
2960 ei->change->can_change = TRUE;
2961 ei->change->can_change_or_has_action = TRUE;
2963 ei->has_change_event[CE_DELAY] = TRUE;
2965 SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
2966 SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE_OR_HAS_ACTION, TRUE);
2969 /* ---------- initialize internal run-time variables --------------------- */
2971 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2973 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
2975 for (j = 0; j < ei->num_change_pages; j++)
2977 ei->change_page[j].can_change_or_has_action =
2978 (ei->change_page[j].can_change |
2979 ei->change_page[j].has_action);
2983 /* add change events from custom element configuration */
2984 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2986 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
2988 for (j = 0; j < ei->num_change_pages; j++)
2990 if (!ei->change_page[j].can_change_or_has_action)
2993 for (k = 0; k < NUM_CHANGE_EVENTS; k++)
2995 /* only add event page for the first page found with this event */
2996 if (ei->change_page[j].has_event[k] && !(ei->has_change_event[k]))
2998 ei->has_change_event[k] = TRUE;
3000 ei->event_page_nr[k] = j;
3001 ei->event_page[k] = &ei->change_page[j];
3007 /* ---------- initialize reference elements in change conditions --------- */
3009 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3011 int element = EL_CUSTOM_START + i;
3012 struct ElementInfo *ei = &element_info[element];
3014 for (j = 0; j < ei->num_change_pages; j++)
3016 int trigger_element = ei->change_page[j].initial_trigger_element;
3018 if (trigger_element >= EL_PREV_CE_8 &&
3019 trigger_element <= EL_NEXT_CE_8)
3020 trigger_element = RESOLVED_REFERENCE_ELEMENT(element, trigger_element);
3022 ei->change_page[j].trigger_element = trigger_element;
3026 /* ---------- initialize run-time trigger player and element ------------- */
3028 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3030 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3032 for (j = 0; j < ei->num_change_pages; j++)
3034 ei->change_page[j].actual_trigger_element = EL_EMPTY;
3035 ei->change_page[j].actual_trigger_player = EL_EMPTY;
3036 ei->change_page[j].actual_trigger_player_bits = CH_PLAYER_NONE;
3037 ei->change_page[j].actual_trigger_side = CH_SIDE_NONE;
3038 ei->change_page[j].actual_trigger_ce_value = 0;
3039 ei->change_page[j].actual_trigger_ce_score = 0;
3043 /* ---------- initialize trigger events ---------------------------------- */
3045 /* initialize trigger events information */
3046 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3047 for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3048 trigger_events[i][j] = FALSE;
3050 /* add trigger events from element change event properties */
3051 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3053 struct ElementInfo *ei = &element_info[i];
3055 for (j = 0; j < ei->num_change_pages; j++)
3057 if (!ei->change_page[j].can_change_or_has_action)
3060 if (ei->change_page[j].has_event[CE_BY_OTHER_ACTION])
3062 int trigger_element = ei->change_page[j].trigger_element;
3064 for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3066 if (ei->change_page[j].has_event[k])
3068 if (IS_GROUP_ELEMENT(trigger_element))
3070 struct ElementGroupInfo *group =
3071 element_info[trigger_element].group;
3073 for (l = 0; l < group->num_elements_resolved; l++)
3074 trigger_events[group->element_resolved[l]][k] = TRUE;
3076 else if (trigger_element == EL_ANY_ELEMENT)
3077 for (l = 0; l < MAX_NUM_ELEMENTS; l++)
3078 trigger_events[l][k] = TRUE;
3080 trigger_events[trigger_element][k] = TRUE;
3087 /* ---------- initialize push delay -------------------------------------- */
3089 /* initialize push delay values to default */
3090 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3092 if (!IS_CUSTOM_ELEMENT(i))
3094 /* set default push delay values (corrected since version 3.0.7-1) */
3095 if (game.engine_version < VERSION_IDENT(3,0,7,1))
3097 element_info[i].push_delay_fixed = 2;
3098 element_info[i].push_delay_random = 8;
3102 element_info[i].push_delay_fixed = 8;
3103 element_info[i].push_delay_random = 8;
3108 /* set push delay value for certain elements from pre-defined list */
3109 for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
3111 int e = push_delay_list[i].element;
3113 element_info[e].push_delay_fixed = push_delay_list[i].push_delay_fixed;
3114 element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
3117 /* set push delay value for Supaplex elements for newer engine versions */
3118 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
3120 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3122 if (IS_SP_ELEMENT(i))
3124 /* set SP push delay to just enough to push under a falling zonk */
3125 int delay = (game.engine_version >= VERSION_IDENT(3,1,1,0) ? 8 : 6);
3127 element_info[i].push_delay_fixed = delay;
3128 element_info[i].push_delay_random = 0;
3133 /* ---------- initialize move stepsize ----------------------------------- */
3135 /* initialize move stepsize values to default */
3136 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3137 if (!IS_CUSTOM_ELEMENT(i))
3138 element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
3140 /* set move stepsize value for certain elements from pre-defined list */
3141 for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
3143 int e = move_stepsize_list[i].element;
3145 element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
3148 /* ---------- initialize collect score ----------------------------------- */
3150 /* initialize collect score values for custom elements from initial value */
3151 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3152 if (IS_CUSTOM_ELEMENT(i))
3153 element_info[i].collect_score = element_info[i].collect_score_initial;
3155 /* ---------- initialize collect count ----------------------------------- */
3157 /* initialize collect count values for non-custom elements */
3158 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3159 if (!IS_CUSTOM_ELEMENT(i))
3160 element_info[i].collect_count_initial = 0;
3162 /* add collect count values for all elements from pre-defined list */
3163 for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
3164 element_info[collect_count_list[i].element].collect_count_initial =
3165 collect_count_list[i].count;
3167 /* ---------- initialize access direction -------------------------------- */
3169 /* initialize access direction values to default (access from every side) */
3170 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3171 if (!IS_CUSTOM_ELEMENT(i))
3172 element_info[i].access_direction = MV_ALL_DIRECTIONS;
3174 /* set access direction value for certain elements from pre-defined list */
3175 for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
3176 element_info[access_direction_list[i].element].access_direction =
3177 access_direction_list[i].direction;
3179 /* ---------- initialize explosion content ------------------------------- */
3180 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3182 if (IS_CUSTOM_ELEMENT(i))
3185 for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
3187 /* (content for EL_YAMYAM set at run-time with game.yamyam_content_nr) */
3189 element_info[i].content.e[x][y] =
3190 (i == EL_PLAYER_1 ? EL_EMERALD_YELLOW :
3191 i == EL_PLAYER_2 ? EL_EMERALD_RED :
3192 i == EL_PLAYER_3 ? EL_EMERALD :
3193 i == EL_PLAYER_4 ? EL_EMERALD_PURPLE :
3194 i == EL_MOLE ? EL_EMERALD_RED :
3195 i == EL_PENGUIN ? EL_EMERALD_PURPLE :
3196 i == EL_BUG ? (x == 1 && y == 1 ? EL_DIAMOND : EL_EMERALD) :
3197 i == EL_BD_BUTTERFLY ? EL_BD_DIAMOND :
3198 i == EL_SP_ELECTRON ? EL_SP_INFOTRON :
3199 i == EL_AMOEBA_TO_DIAMOND ? level.amoeba_content :
3200 i == EL_WALL_EMERALD ? EL_EMERALD :
3201 i == EL_WALL_DIAMOND ? EL_DIAMOND :
3202 i == EL_WALL_BD_DIAMOND ? EL_BD_DIAMOND :
3203 i == EL_WALL_EMERALD_YELLOW ? EL_EMERALD_YELLOW :
3204 i == EL_WALL_EMERALD_RED ? EL_EMERALD_RED :
3205 i == EL_WALL_EMERALD_PURPLE ? EL_EMERALD_PURPLE :
3206 i == EL_WALL_PEARL ? EL_PEARL :
3207 i == EL_WALL_CRYSTAL ? EL_CRYSTAL :
3212 /* ---------- initialize recursion detection ------------------------------ */
3213 recursion_loop_depth = 0;
3214 recursion_loop_detected = FALSE;
3215 recursion_loop_element = EL_UNDEFINED;
3217 /* ---------- initialize graphics engine ---------------------------------- */
3218 game.scroll_delay_value =
3219 (game.forced_scroll_delay_value != -1 ? game.forced_scroll_delay_value :
3220 setup.scroll_delay ? setup.scroll_delay_value : 0);
3221 game.scroll_delay_value =
3222 MIN(MAX(MIN_SCROLL_DELAY, game.scroll_delay_value), MAX_SCROLL_DELAY);
3224 /* ---------- initialize game engine snapshots ---------------------------- */
3225 for (i = 0; i < MAX_PLAYERS; i++)
3226 game.snapshot.last_action[i] = 0;
3227 game.snapshot.changed_action = FALSE;
3228 game.snapshot.collected_item = FALSE;
3229 game.snapshot.mode =
3230 (strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_STEP) ?
3231 SNAPSHOT_MODE_EVERY_STEP :
3232 strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_MOVE) ?
3233 SNAPSHOT_MODE_EVERY_MOVE :
3234 strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_COLLECT) ?
3235 SNAPSHOT_MODE_EVERY_COLLECT : SNAPSHOT_MODE_OFF);
3236 game.snapshot.save_snapshot = FALSE;
3238 /* ---------- initialize level time for Supaplex engine ------------------- */
3239 /* Supaplex levels with time limit currently unsupported -- should be added */
3240 if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
3244 int get_num_special_action(int element, int action_first, int action_last)
3246 int num_special_action = 0;
3249 for (i = action_first; i <= action_last; i++)
3251 boolean found = FALSE;
3253 for (j = 0; j < NUM_DIRECTIONS; j++)
3254 if (el_act_dir2img(element, i, j) !=
3255 el_act_dir2img(element, ACTION_DEFAULT, j))
3259 num_special_action++;
3264 return num_special_action;
3269 =============================================================================
3271 -----------------------------------------------------------------------------
3272 initialize and start new game
3273 =============================================================================
3278 int full_lev_fieldx = lev_fieldx + (BorderElement != EL_EMPTY ? 2 : 0);
3279 int full_lev_fieldy = lev_fieldy + (BorderElement != EL_EMPTY ? 2 : 0);
3280 int fade_mask = REDRAW_FIELD;
3282 boolean emulate_bd = TRUE; /* unless non-BOULDERDASH elements found */
3283 boolean emulate_sb = TRUE; /* unless non-SOKOBAN elements found */
3284 boolean emulate_sp = TRUE; /* unless non-SUPAPLEX elements found */
3285 int initial_move_dir = MV_DOWN;
3288 // required here to update video display before fading (FIX THIS)
3289 DrawMaskedBorder(REDRAW_DOOR_2);
3291 if (!game.restart_level)
3292 CloseDoor(DOOR_CLOSE_1);
3294 SetGameStatus(GAME_MODE_PLAYING);
3296 if (level_editor_test_game)
3297 FadeSkipNextFadeIn();
3299 FadeSetEnterScreen();
3301 if (CheckIfGlobalBorderHasChanged())
3302 fade_mask = REDRAW_ALL;
3304 FadeLevelSoundsAndMusic();
3306 ExpireSoundLoops(TRUE);
3310 /* needed if different viewport properties defined for playing */
3311 ChangeViewportPropertiesIfNeeded();
3315 OpenDoor(GetDoorState() | DOOR_NO_DELAY | DOOR_FORCE_REDRAW);
3317 DrawCompleteVideoDisplay();
3320 InitGameControlValues();
3322 /* don't play tapes over network */
3323 network_playing = (options.network && !tape.playing);
3325 for (i = 0; i < MAX_PLAYERS; i++)
3327 struct PlayerInfo *player = &stored_player[i];
3329 player->index_nr = i;
3330 player->index_bit = (1 << i);
3331 player->element_nr = EL_PLAYER_1 + i;
3333 player->present = FALSE;
3334 player->active = FALSE;
3335 player->mapped = FALSE;
3337 player->killed = FALSE;
3338 player->reanimated = FALSE;
3341 player->effective_action = 0;
3342 player->programmed_action = 0;
3344 player->mouse_action.lx = 0;
3345 player->mouse_action.ly = 0;
3346 player->mouse_action.button = 0;
3348 player->effective_mouse_action.lx = 0;
3349 player->effective_mouse_action.ly = 0;
3350 player->effective_mouse_action.button = 0;
3353 player->score_final = 0;
3355 player->health = MAX_HEALTH;
3356 player->health_final = MAX_HEALTH;
3358 player->gems_still_needed = level.gems_needed;
3359 player->sokobanfields_still_needed = 0;
3360 player->lights_still_needed = 0;
3361 player->friends_still_needed = 0;
3363 for (j = 0; j < MAX_NUM_KEYS; j++)
3364 player->key[j] = FALSE;
3366 player->num_white_keys = 0;
3368 player->dynabomb_count = 0;
3369 player->dynabomb_size = 1;
3370 player->dynabombs_left = 0;
3371 player->dynabomb_xl = FALSE;
3373 player->MovDir = initial_move_dir;
3376 player->GfxDir = initial_move_dir;
3377 player->GfxAction = ACTION_DEFAULT;
3379 player->StepFrame = 0;
3381 player->initial_element = player->element_nr;
3382 player->artwork_element =
3383 (level.use_artwork_element[i] ? level.artwork_element[i] :
3384 player->element_nr);
3385 player->use_murphy = FALSE;
3387 player->block_last_field = FALSE; /* initialized in InitPlayerField() */
3388 player->block_delay_adjustment = 0; /* initialized in InitPlayerField() */
3390 player->gravity = level.initial_player_gravity[i];
3392 player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
3394 player->actual_frame_counter = 0;
3396 player->step_counter = 0;
3398 player->last_move_dir = initial_move_dir;
3400 player->is_active = FALSE;
3402 player->is_waiting = FALSE;
3403 player->is_moving = FALSE;
3404 player->is_auto_moving = FALSE;
3405 player->is_digging = FALSE;
3406 player->is_snapping = FALSE;
3407 player->is_collecting = FALSE;
3408 player->is_pushing = FALSE;
3409 player->is_switching = FALSE;
3410 player->is_dropping = FALSE;
3411 player->is_dropping_pressed = FALSE;
3413 player->is_bored = FALSE;
3414 player->is_sleeping = FALSE;
3416 player->was_waiting = TRUE;
3417 player->was_moving = FALSE;
3418 player->was_snapping = FALSE;
3419 player->was_dropping = FALSE;
3421 player->force_dropping = FALSE;
3423 player->frame_counter_bored = -1;
3424 player->frame_counter_sleeping = -1;
3426 player->anim_delay_counter = 0;
3427 player->post_delay_counter = 0;
3429 player->dir_waiting = initial_move_dir;
3430 player->action_waiting = ACTION_DEFAULT;
3431 player->last_action_waiting = ACTION_DEFAULT;
3432 player->special_action_bored = ACTION_DEFAULT;
3433 player->special_action_sleeping = ACTION_DEFAULT;
3435 player->switch_x = -1;
3436 player->switch_y = -1;
3438 player->drop_x = -1;
3439 player->drop_y = -1;
3441 player->show_envelope = 0;
3443 SetPlayerMoveSpeed(player, level.initial_player_stepsize[i], TRUE);
3445 player->push_delay = -1; /* initialized when pushing starts */
3446 player->push_delay_value = game.initial_push_delay_value;
3448 player->drop_delay = 0;
3449 player->drop_pressed_delay = 0;
3451 player->last_jx = -1;
3452 player->last_jy = -1;
3456 player->shield_normal_time_left = 0;
3457 player->shield_deadly_time_left = 0;
3459 player->inventory_infinite_element = EL_UNDEFINED;
3460 player->inventory_size = 0;
3462 if (level.use_initial_inventory[i])
3464 for (j = 0; j < level.initial_inventory_size[i]; j++)
3466 int element = level.initial_inventory_content[i][j];
3467 int collect_count = element_info[element].collect_count_initial;
3470 if (!IS_CUSTOM_ELEMENT(element))
3473 if (collect_count == 0)
3474 player->inventory_infinite_element = element;
3476 for (k = 0; k < collect_count; k++)
3477 if (player->inventory_size < MAX_INVENTORY_SIZE)
3478 player->inventory_element[player->inventory_size++] = element;
3482 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
3483 SnapField(player, 0, 0);
3485 player->LevelSolved = FALSE;
3486 player->GameOver = FALSE;
3488 player->LevelSolved_GameWon = FALSE;
3489 player->LevelSolved_GameEnd = FALSE;
3490 player->LevelSolved_PanelOff = FALSE;
3491 player->LevelSolved_SaveTape = FALSE;
3492 player->LevelSolved_SaveScore = FALSE;
3494 player->LevelSolved_CountingTime = 0;
3495 player->LevelSolved_CountingScore = 0;
3496 player->LevelSolved_CountingHealth = 0;
3498 map_player_action[i] = i;
3501 network_player_action_received = FALSE;
3503 #if defined(NETWORK_AVALIABLE)
3504 /* initial null action */
3505 if (network_playing)
3506 SendToServer_MovePlayer(MV_NONE);
3515 TimeLeft = level.time;
3518 ScreenMovDir = MV_NONE;
3522 ScrollStepSize = 0; /* will be correctly initialized by ScrollScreen() */
3524 AllPlayersGone = FALSE;
3526 game.no_time_limit = (level.time == 0);
3528 game.yamyam_content_nr = 0;
3529 game.robot_wheel_active = FALSE;
3530 game.magic_wall_active = FALSE;
3531 game.magic_wall_time_left = 0;
3532 game.light_time_left = 0;
3533 game.timegate_time_left = 0;
3534 game.switchgate_pos = 0;
3535 game.wind_direction = level.wind_direction_initial;
3537 game.lenses_time_left = 0;
3538 game.magnify_time_left = 0;
3540 game.ball_state = level.ball_state_initial;
3541 game.ball_content_nr = 0;
3543 game.envelope_active = FALSE;
3545 /* set focus to local player for network games, else to all players */
3546 game.centered_player_nr = (network_playing ? local_player->index_nr : -1);
3547 game.centered_player_nr_next = game.centered_player_nr;
3548 game.set_centered_player = FALSE;
3550 if (network_playing && tape.recording)
3552 /* store client dependent player focus when recording network games */
3553 tape.centered_player_nr_next = game.centered_player_nr_next;
3554 tape.set_centered_player = TRUE;
3557 for (i = 0; i < NUM_BELTS; i++)
3559 game.belt_dir[i] = MV_NONE;
3560 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
3563 for (i = 0; i < MAX_NUM_AMOEBA; i++)
3564 AmoebaCnt[i] = AmoebaCnt2[i] = 0;
3566 #if DEBUG_INIT_PLAYER
3569 printf("Player status at level initialization:\n");
3573 SCAN_PLAYFIELD(x, y)
3575 Feld[x][y] = level.field[x][y];
3576 MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
3577 ChangeDelay[x][y] = 0;
3578 ChangePage[x][y] = -1;
3579 CustomValue[x][y] = 0; /* initialized in InitField() */
3580 Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
3582 WasJustMoving[x][y] = 0;
3583 WasJustFalling[x][y] = 0;
3584 CheckCollision[x][y] = 0;
3585 CheckImpact[x][y] = 0;
3587 Pushed[x][y] = FALSE;
3589 ChangeCount[x][y] = 0;
3590 ChangeEvent[x][y] = -1;
3592 ExplodePhase[x][y] = 0;
3593 ExplodeDelay[x][y] = 0;
3594 ExplodeField[x][y] = EX_TYPE_NONE;
3596 RunnerVisit[x][y] = 0;
3597 PlayerVisit[x][y] = 0;
3600 GfxRandom[x][y] = INIT_GFX_RANDOM();
3601 GfxElement[x][y] = EL_UNDEFINED;
3602 GfxAction[x][y] = ACTION_DEFAULT;
3603 GfxDir[x][y] = MV_NONE;
3604 GfxRedraw[x][y] = GFX_REDRAW_NONE;
3607 SCAN_PLAYFIELD(x, y)
3609 if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
3611 if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
3613 if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
3616 InitField(x, y, TRUE);
3618 ResetGfxAnimation(x, y);
3623 for (i = 0; i < MAX_PLAYERS; i++)
3625 struct PlayerInfo *player = &stored_player[i];
3627 /* set number of special actions for bored and sleeping animation */
3628 player->num_special_action_bored =
3629 get_num_special_action(player->artwork_element,
3630 ACTION_BORING_1, ACTION_BORING_LAST);
3631 player->num_special_action_sleeping =
3632 get_num_special_action(player->artwork_element,
3633 ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
3636 game.emulation = (emulate_bd ? EMU_BOULDERDASH :
3637 emulate_sb ? EMU_SOKOBAN :
3638 emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
3640 /* initialize type of slippery elements */
3641 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3643 if (!IS_CUSTOM_ELEMENT(i))
3645 /* default: elements slip down either to the left or right randomly */
3646 element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
3648 /* SP style elements prefer to slip down on the left side */
3649 if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
3650 element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3652 /* BD style elements prefer to slip down on the left side */
3653 if (game.emulation == EMU_BOULDERDASH)
3654 element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3658 /* initialize explosion and ignition delay */
3659 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3661 if (!IS_CUSTOM_ELEMENT(i))
3664 int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
3665 game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
3666 game.emulation == EMU_SUPAPLEX ? 3 : 2);
3667 int last_phase = (num_phase + 1) * delay;
3668 int half_phase = (num_phase / 2) * delay;
3670 element_info[i].explosion_delay = last_phase - 1;
3671 element_info[i].ignition_delay = half_phase;
3673 if (i == EL_BLACK_ORB)
3674 element_info[i].ignition_delay = 1;
3678 /* correct non-moving belts to start moving left */
3679 for (i = 0; i < NUM_BELTS; i++)
3680 if (game.belt_dir[i] == MV_NONE)
3681 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
3683 #if USE_NEW_PLAYER_ASSIGNMENTS
3684 /* !!! SAME AS init.c:InitPlayerInfo() -- FIX THIS !!! */
3685 /* choose default local player */
3686 local_player = &stored_player[0];
3688 for (i = 0; i < MAX_PLAYERS; i++)
3689 stored_player[i].connected = FALSE;
3691 local_player->connected = TRUE;
3692 /* !!! SAME AS init.c:InitPlayerInfo() -- FIX THIS !!! */
3696 for (i = 0; i < MAX_PLAYERS; i++)
3697 stored_player[i].connected = tape.player_participates[i];
3699 else if (game.team_mode && !options.network)
3701 /* try to guess locally connected team mode players (needed for correct
3702 assignment of player figures from level to locally playing players) */
3704 for (i = 0; i < MAX_PLAYERS; i++)
3705 if (setup.input[i].use_joystick ||
3706 setup.input[i].key.left != KSYM_UNDEFINED)
3707 stored_player[i].connected = TRUE;
3710 #if DEBUG_INIT_PLAYER
3713 printf("Player status after level initialization:\n");
3715 for (i = 0; i < MAX_PLAYERS; i++)
3717 struct PlayerInfo *player = &stored_player[i];
3719 printf("- player %d: present == %d, connected == %d, active == %d",
3725 if (local_player == player)
3726 printf(" (local player)");
3733 #if DEBUG_INIT_PLAYER
3735 printf("Reassigning players ...\n");
3738 /* check if any connected player was not found in playfield */
3739 for (i = 0; i < MAX_PLAYERS; i++)
3741 struct PlayerInfo *player = &stored_player[i];
3743 if (player->connected && !player->present)
3745 struct PlayerInfo *field_player = NULL;
3747 #if DEBUG_INIT_PLAYER
3749 printf("- looking for field player for player %d ...\n", i + 1);
3752 /* assign first free player found that is present in the playfield */
3754 /* first try: look for unmapped playfield player that is not connected */
3755 for (j = 0; j < MAX_PLAYERS; j++)
3756 if (field_player == NULL &&
3757 stored_player[j].present &&
3758 !stored_player[j].mapped &&
3759 !stored_player[j].connected)
3760 field_player = &stored_player[j];
3762 /* second try: look for *any* unmapped playfield player */
3763 for (j = 0; j < MAX_PLAYERS; j++)
3764 if (field_player == NULL &&
3765 stored_player[j].present &&
3766 !stored_player[j].mapped)
3767 field_player = &stored_player[j];
3769 if (field_player != NULL)
3771 int jx = field_player->jx, jy = field_player->jy;
3773 #if DEBUG_INIT_PLAYER
3775 printf("- found player %d\n", field_player->index_nr + 1);
3778 player->present = FALSE;
3779 player->active = FALSE;
3781 field_player->present = TRUE;
3782 field_player->active = TRUE;
3785 player->initial_element = field_player->initial_element;
3786 player->artwork_element = field_player->artwork_element;
3788 player->block_last_field = field_player->block_last_field;
3789 player->block_delay_adjustment = field_player->block_delay_adjustment;
3792 StorePlayer[jx][jy] = field_player->element_nr;
3794 field_player->jx = field_player->last_jx = jx;
3795 field_player->jy = field_player->last_jy = jy;
3797 if (local_player == player)
3798 local_player = field_player;
3800 map_player_action[field_player->index_nr] = i;
3802 field_player->mapped = TRUE;
3804 #if DEBUG_INIT_PLAYER
3806 printf("- map_player_action[%d] == %d\n",
3807 field_player->index_nr + 1, i + 1);
3812 if (player->connected && player->present)
3813 player->mapped = TRUE;
3816 #if DEBUG_INIT_PLAYER
3819 printf("Player status after player assignment (first stage):\n");
3821 for (i = 0; i < MAX_PLAYERS; i++)
3823 struct PlayerInfo *player = &stored_player[i];
3825 printf("- player %d: present == %d, connected == %d, active == %d",
3831 if (local_player == player)
3832 printf(" (local player)");
3841 /* check if any connected player was not found in playfield */
3842 for (i = 0; i < MAX_PLAYERS; i++)
3844 struct PlayerInfo *player = &stored_player[i];
3846 if (player->connected && !player->present)
3848 for (j = 0; j < MAX_PLAYERS; j++)
3850 struct PlayerInfo *field_player = &stored_player[j];
3851 int jx = field_player->jx, jy = field_player->jy;
3853 /* assign first free player found that is present in the playfield */
3854 if (field_player->present && !field_player->connected)
3856 player->present = TRUE;
3857 player->active = TRUE;
3859 field_player->present = FALSE;
3860 field_player->active = FALSE;
3862 player->initial_element = field_player->initial_element;
3863 player->artwork_element = field_player->artwork_element;
3865 player->block_last_field = field_player->block_last_field;
3866 player->block_delay_adjustment = field_player->block_delay_adjustment;
3868 StorePlayer[jx][jy] = player->element_nr;
3870 player->jx = player->last_jx = jx;
3871 player->jy = player->last_jy = jy;
3881 printf("::: local_player->present == %d\n", local_player->present);
3886 /* when playing a tape, eliminate all players who do not participate */
3888 #if USE_NEW_PLAYER_ASSIGNMENTS
3890 if (!game.team_mode)
3892 for (i = 0; i < MAX_PLAYERS; i++)
3894 if (stored_player[i].active &&
3895 !tape.player_participates[map_player_action[i]])
3897 struct PlayerInfo *player = &stored_player[i];
3898 int jx = player->jx, jy = player->jy;
3900 #if DEBUG_INIT_PLAYER
3902 printf("Removing player %d at (%d, %d)\n", i + 1, jx, jy);
3905 player->active = FALSE;
3906 StorePlayer[jx][jy] = 0;
3907 Feld[jx][jy] = EL_EMPTY;
3914 for (i = 0; i < MAX_PLAYERS; i++)
3916 if (stored_player[i].active &&
3917 !tape.player_participates[i])
3919 struct PlayerInfo *player = &stored_player[i];
3920 int jx = player->jx, jy = player->jy;
3922 player->active = FALSE;
3923 StorePlayer[jx][jy] = 0;
3924 Feld[jx][jy] = EL_EMPTY;
3929 else if (!options.network && !game.team_mode) /* && !tape.playing */
3931 /* when in single player mode, eliminate all but the first active player */
3933 for (i = 0; i < MAX_PLAYERS; i++)
3935 if (stored_player[i].active)
3937 for (j = i + 1; j < MAX_PLAYERS; j++)
3939 if (stored_player[j].active)
3941 struct PlayerInfo *player = &stored_player[j];
3942 int jx = player->jx, jy = player->jy;
3944 player->active = FALSE;
3945 player->present = FALSE;
3947 StorePlayer[jx][jy] = 0;
3948 Feld[jx][jy] = EL_EMPTY;
3955 /* when recording the game, store which players take part in the game */
3958 #if USE_NEW_PLAYER_ASSIGNMENTS
3959 for (i = 0; i < MAX_PLAYERS; i++)
3960 if (stored_player[i].connected)
3961 tape.player_participates[i] = TRUE;
3963 for (i = 0; i < MAX_PLAYERS; i++)
3964 if (stored_player[i].active)
3965 tape.player_participates[i] = TRUE;
3969 #if DEBUG_INIT_PLAYER
3972 printf("Player status after player assignment (final stage):\n");
3974 for (i = 0; i < MAX_PLAYERS; i++)
3976 struct PlayerInfo *player = &stored_player[i];
3978 printf("- player %d: present == %d, connected == %d, active == %d",
3984 if (local_player == player)
3985 printf(" (local player)");
3992 if (BorderElement == EL_EMPTY)
3995 SBX_Right = lev_fieldx - SCR_FIELDX;
3997 SBY_Lower = lev_fieldy - SCR_FIELDY;
4002 SBX_Right = lev_fieldx - SCR_FIELDX + 1;
4004 SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
4007 if (full_lev_fieldx <= SCR_FIELDX)
4008 SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
4009 if (full_lev_fieldy <= SCR_FIELDY)
4010 SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
4012 if (EVEN(SCR_FIELDX) && full_lev_fieldx > SCR_FIELDX)
4014 if (EVEN(SCR_FIELDY) && full_lev_fieldy > SCR_FIELDY)
4017 /* if local player not found, look for custom element that might create
4018 the player (make some assumptions about the right custom element) */
4019 if (!local_player->present)
4021 int start_x = 0, start_y = 0;
4022 int found_rating = 0;
4023 int found_element = EL_UNDEFINED;
4024 int player_nr = local_player->index_nr;
4026 SCAN_PLAYFIELD(x, y)
4028 int element = Feld[x][y];
4033 if (level.use_start_element[player_nr] &&
4034 level.start_element[player_nr] == element &&
4041 found_element = element;
4044 if (!IS_CUSTOM_ELEMENT(element))
4047 if (CAN_CHANGE(element))
4049 for (i = 0; i < element_info[element].num_change_pages; i++)
4051 /* check for player created from custom element as single target */
4052 content = element_info[element].change_page[i].target_element;
4053 is_player = ELEM_IS_PLAYER(content);
4055 if (is_player && (found_rating < 3 ||
4056 (found_rating == 3 && element < found_element)))
4062 found_element = element;
4067 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
4069 /* check for player created from custom element as explosion content */
4070 content = element_info[element].content.e[xx][yy];
4071 is_player = ELEM_IS_PLAYER(content);
4073 if (is_player && (found_rating < 2 ||
4074 (found_rating == 2 && element < found_element)))
4076 start_x = x + xx - 1;
4077 start_y = y + yy - 1;
4080 found_element = element;
4083 if (!CAN_CHANGE(element))
4086 for (i = 0; i < element_info[element].num_change_pages; i++)
4088 /* check for player created from custom element as extended target */
4090 element_info[element].change_page[i].target_content.e[xx][yy];
4092 is_player = ELEM_IS_PLAYER(content);
4094 if (is_player && (found_rating < 1 ||
4095 (found_rating == 1 && element < found_element)))
4097 start_x = x + xx - 1;
4098 start_y = y + yy - 1;
4101 found_element = element;
4107 scroll_x = SCROLL_POSITION_X(start_x);
4108 scroll_y = SCROLL_POSITION_Y(start_y);
4112 scroll_x = SCROLL_POSITION_X(local_player->jx);
4113 scroll_y = SCROLL_POSITION_Y(local_player->jy);
4116 /* !!! FIX THIS (START) !!! */
4117 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
4119 InitGameEngine_EM();
4121 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
4123 InitGameEngine_SP();
4125 else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4127 InitGameEngine_MM();
4131 DrawLevel(REDRAW_FIELD);
4134 /* after drawing the level, correct some elements */
4135 if (game.timegate_time_left == 0)
4136 CloseAllOpenTimegates();
4139 /* blit playfield from scroll buffer to normal back buffer for fading in */
4140 BlitScreenToBitmap(backbuffer);
4141 /* !!! FIX THIS (END) !!! */
4143 DrawMaskedBorder(fade_mask);
4148 // full screen redraw is required at this point in the following cases:
4149 // - special editor door undrawn when game was started from level editor
4150 // - drawing area (playfield) was changed and has to be removed completely
4151 redraw_mask = REDRAW_ALL;
4155 if (!game.restart_level)
4157 /* copy default game door content to main double buffer */
4159 /* !!! CHECK AGAIN !!! */
4160 SetPanelBackground();
4161 // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
4162 DrawBackground(DX, DY, DXSIZE, DYSIZE);
4165 SetPanelBackground();
4166 SetDrawBackgroundMask(REDRAW_DOOR_1);
4168 UpdateAndDisplayGameControlValues();
4170 if (!game.restart_level)
4176 CreateGameButtons();
4181 /* copy actual game door content to door double buffer for OpenDoor() */
4182 BlitBitmap(drawto, bitmap_db_door_1, DX, DY, DXSIZE, DYSIZE, 0, 0);
4184 OpenDoor(DOOR_OPEN_ALL);
4186 PlaySound(SND_GAME_STARTING);
4188 if (setup.sound_music)
4191 KeyboardAutoRepeatOffUnlessAutoplay();
4193 #if DEBUG_INIT_PLAYER
4196 printf("Player status (final):\n");
4198 for (i = 0; i < MAX_PLAYERS; i++)
4200 struct PlayerInfo *player = &stored_player[i];
4202 printf("- player %d: present == %d, connected == %d, active == %d",
4208 if (local_player == player)
4209 printf(" (local player)");
4222 if (!game.restart_level && !tape.playing)
4224 LevelStats_incPlayed(level_nr);
4226 SaveLevelSetup_SeriesInfo();
4229 game.restart_level = FALSE;
4231 if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4232 InitGameActions_MM();
4234 SaveEngineSnapshotToListInitial();
4237 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y,
4238 int actual_player_x, int actual_player_y)
4240 /* this is used for non-R'n'D game engines to update certain engine values */
4242 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
4244 actual_player_x = correctLevelPosX_EM(actual_player_x);
4245 actual_player_y = correctLevelPosY_EM(actual_player_y);
4248 /* needed to determine if sounds are played within the visible screen area */
4249 scroll_x = actual_scroll_x;
4250 scroll_y = actual_scroll_y;
4252 /* needed to get player position for "follow finger" playing input method */
4253 local_player->jx = actual_player_x;
4254 local_player->jy = actual_player_y;
4257 void InitMovDir(int x, int y)
4259 int i, element = Feld[x][y];
4260 static int xy[4][2] =
4267 static int direction[3][4] =
4269 { MV_RIGHT, MV_UP, MV_LEFT, MV_DOWN },
4270 { MV_LEFT, MV_DOWN, MV_RIGHT, MV_UP },
4271 { MV_LEFT, MV_RIGHT, MV_UP, MV_DOWN }
4280 Feld[x][y] = EL_BUG;
4281 MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
4284 case EL_SPACESHIP_RIGHT:
4285 case EL_SPACESHIP_UP:
4286 case EL_SPACESHIP_LEFT:
4287 case EL_SPACESHIP_DOWN:
4288 Feld[x][y] = EL_SPACESHIP;
4289 MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
4292 case EL_BD_BUTTERFLY_RIGHT:
4293 case EL_BD_BUTTERFLY_UP:
4294 case EL_BD_BUTTERFLY_LEFT:
4295 case EL_BD_BUTTERFLY_DOWN:
4296 Feld[x][y] = EL_BD_BUTTERFLY;
4297 MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
4300 case EL_BD_FIREFLY_RIGHT:
4301 case EL_BD_FIREFLY_UP:
4302 case EL_BD_FIREFLY_LEFT:
4303 case EL_BD_FIREFLY_DOWN:
4304 Feld[x][y] = EL_BD_FIREFLY;
4305 MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
4308 case EL_PACMAN_RIGHT:
4310 case EL_PACMAN_LEFT:
4311 case EL_PACMAN_DOWN:
4312 Feld[x][y] = EL_PACMAN;
4313 MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
4316 case EL_YAMYAM_LEFT:
4317 case EL_YAMYAM_RIGHT:
4319 case EL_YAMYAM_DOWN:
4320 Feld[x][y] = EL_YAMYAM;
4321 MovDir[x][y] = direction[2][element - EL_YAMYAM_LEFT];
4324 case EL_SP_SNIKSNAK:
4325 MovDir[x][y] = MV_UP;
4328 case EL_SP_ELECTRON:
4329 MovDir[x][y] = MV_LEFT;
4336 Feld[x][y] = EL_MOLE;
4337 MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
4341 if (IS_CUSTOM_ELEMENT(element))
4343 struct ElementInfo *ei = &element_info[element];
4344 int move_direction_initial = ei->move_direction_initial;
4345 int move_pattern = ei->move_pattern;
4347 if (move_direction_initial == MV_START_PREVIOUS)
4349 if (MovDir[x][y] != MV_NONE)
4352 move_direction_initial = MV_START_AUTOMATIC;
4355 if (move_direction_initial == MV_START_RANDOM)
4356 MovDir[x][y] = 1 << RND(4);
4357 else if (move_direction_initial & MV_ANY_DIRECTION)
4358 MovDir[x][y] = move_direction_initial;
4359 else if (move_pattern == MV_ALL_DIRECTIONS ||
4360 move_pattern == MV_TURNING_LEFT ||
4361 move_pattern == MV_TURNING_RIGHT ||
4362 move_pattern == MV_TURNING_LEFT_RIGHT ||
4363 move_pattern == MV_TURNING_RIGHT_LEFT ||
4364 move_pattern == MV_TURNING_RANDOM)
4365 MovDir[x][y] = 1 << RND(4);
4366 else if (move_pattern == MV_HORIZONTAL)
4367 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
4368 else if (move_pattern == MV_VERTICAL)
4369 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
4370 else if (move_pattern & MV_ANY_DIRECTION)
4371 MovDir[x][y] = element_info[element].move_pattern;
4372 else if (move_pattern == MV_ALONG_LEFT_SIDE ||
4373 move_pattern == MV_ALONG_RIGHT_SIDE)
4375 /* use random direction as default start direction */
4376 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
4377 MovDir[x][y] = 1 << RND(4);
4379 for (i = 0; i < NUM_DIRECTIONS; i++)
4381 int x1 = x + xy[i][0];
4382 int y1 = y + xy[i][1];
4384 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4386 if (move_pattern == MV_ALONG_RIGHT_SIDE)
4387 MovDir[x][y] = direction[0][i];
4389 MovDir[x][y] = direction[1][i];
4398 MovDir[x][y] = 1 << RND(4);
4400 if (element != EL_BUG &&
4401 element != EL_SPACESHIP &&
4402 element != EL_BD_BUTTERFLY &&
4403 element != EL_BD_FIREFLY)
4406 for (i = 0; i < NUM_DIRECTIONS; i++)
4408 int x1 = x + xy[i][0];
4409 int y1 = y + xy[i][1];
4411 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4413 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
4415 MovDir[x][y] = direction[0][i];
4418 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
4419 element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4421 MovDir[x][y] = direction[1][i];
4430 GfxDir[x][y] = MovDir[x][y];
4433 void InitAmoebaNr(int x, int y)
4436 int group_nr = AmoebeNachbarNr(x, y);
4440 for (i = 1; i < MAX_NUM_AMOEBA; i++)
4442 if (AmoebaCnt[i] == 0)
4450 AmoebaNr[x][y] = group_nr;
4451 AmoebaCnt[group_nr]++;
4452 AmoebaCnt2[group_nr]++;
4455 static void PlayerWins(struct PlayerInfo *player)
4457 player->LevelSolved = TRUE;
4458 player->GameOver = TRUE;
4460 player->score_final = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
4461 level.native_em_level->lev->score :
4462 level.game_engine_type == GAME_ENGINE_TYPE_MM ?
4465 player->health_final = (level.game_engine_type == GAME_ENGINE_TYPE_MM ?
4466 MM_HEALTH(game_mm.laser_overload_value) :
4469 player->LevelSolved_CountingTime = (game.no_time_limit ? TimePlayed :
4471 player->LevelSolved_CountingScore = player->score_final;
4472 player->LevelSolved_CountingHealth = player->health_final;
4477 static int time_count_steps;
4478 static int time, time_final;
4479 static int score, score_final;
4480 static int health, health_final;
4481 static int game_over_delay_1 = 0;
4482 static int game_over_delay_2 = 0;
4483 static int game_over_delay_3 = 0;
4484 int game_over_delay_value_1 = 50;
4485 int game_over_delay_value_2 = 25;
4486 int game_over_delay_value_3 = 50;
4488 if (!local_player->LevelSolved_GameWon)
4492 /* do not start end game actions before the player stops moving (to exit) */
4493 if (local_player->MovPos)
4496 local_player->LevelSolved_GameWon = TRUE;
4497 local_player->LevelSolved_SaveTape = tape.recording;
4498 local_player->LevelSolved_SaveScore = !tape.playing;
4502 LevelStats_incSolved(level_nr);
4504 SaveLevelSetup_SeriesInfo();
4507 if (tape.auto_play) /* tape might already be stopped here */
4508 tape.auto_play_level_solved = TRUE;
4512 game_over_delay_1 = 0;
4513 game_over_delay_2 = 0;
4514 game_over_delay_3 = game_over_delay_value_3;
4516 time = time_final = (game.no_time_limit ? TimePlayed : TimeLeft);
4517 score = score_final = local_player->score_final;
4518 health = health_final = local_player->health_final;
4520 if (level.score[SC_TIME_BONUS] > 0)
4525 score_final += TimeLeft * level.score[SC_TIME_BONUS];
4527 else if (game.no_time_limit && TimePlayed < 999)
4530 score_final += (999 - TimePlayed) * level.score[SC_TIME_BONUS];
4533 time_count_steps = MAX(1, ABS(time_final - time) / 100);
4535 game_over_delay_1 = game_over_delay_value_1;
4537 if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4540 score_final += health * level.score[SC_TIME_BONUS];
4542 game_over_delay_2 = game_over_delay_value_2;
4545 local_player->score_final = score_final;
4546 local_player->health_final = health_final;
4549 if (level_editor_test_game)
4552 score = score_final;
4554 local_player->LevelSolved_CountingTime = time;
4555 local_player->LevelSolved_CountingScore = score;
4557 game_panel_controls[GAME_PANEL_TIME].value = time;
4558 game_panel_controls[GAME_PANEL_SCORE].value = score;
4560 DisplayGameControlValues();
4563 if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
4565 if (ExitX >= 0 && ExitY >= 0) /* local player has left the level */
4567 /* close exit door after last player */
4568 if ((AllPlayersGone &&
4569 (Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
4570 Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN ||
4571 Feld[ExitX][ExitY] == EL_STEEL_EXIT_OPEN)) ||
4572 Feld[ExitX][ExitY] == EL_EM_EXIT_OPEN ||
4573 Feld[ExitX][ExitY] == EL_EM_STEEL_EXIT_OPEN)
4575 int element = Feld[ExitX][ExitY];
4577 Feld[ExitX][ExitY] =
4578 (element == EL_EXIT_OPEN ? EL_EXIT_CLOSING :
4579 element == EL_EM_EXIT_OPEN ? EL_EM_EXIT_CLOSING :
4580 element == EL_SP_EXIT_OPEN ? EL_SP_EXIT_CLOSING:
4581 element == EL_STEEL_EXIT_OPEN ? EL_STEEL_EXIT_CLOSING:
4582 EL_EM_STEEL_EXIT_CLOSING);
4584 PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING);
4587 /* player disappears */
4588 DrawLevelField(ExitX, ExitY);
4591 for (i = 0; i < MAX_PLAYERS; i++)
4593 struct PlayerInfo *player = &stored_player[i];
4595 if (player->present)
4597 RemovePlayer(player);
4599 /* player disappears */
4600 DrawLevelField(player->jx, player->jy);
4605 PlaySound(SND_GAME_WINNING);
4608 if (game_over_delay_1 > 0)
4610 game_over_delay_1--;
4615 if (time != time_final)
4617 int time_to_go = ABS(time_final - time);
4618 int time_count_dir = (time < time_final ? +1 : -1);
4620 if (time_to_go < time_count_steps)
4621 time_count_steps = 1;
4623 time += time_count_steps * time_count_dir;
4624 score += time_count_steps * level.score[SC_TIME_BONUS];
4626 local_player->LevelSolved_CountingTime = time;
4627 local_player->LevelSolved_CountingScore = score;
4629 game_panel_controls[GAME_PANEL_TIME].value = time;
4630 game_panel_controls[GAME_PANEL_SCORE].value = score;
4632 DisplayGameControlValues();
4634 if (time == time_final)
4635 StopSound(SND_GAME_LEVELTIME_BONUS);
4636 else if (setup.sound_loops)
4637 PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4639 PlaySound(SND_GAME_LEVELTIME_BONUS);
4644 if (game_over_delay_2 > 0)
4646 game_over_delay_2--;
4651 if (health != health_final)
4653 int health_count_dir = (health < health_final ? +1 : -1);
4655 health += health_count_dir;
4656 score += level.score[SC_TIME_BONUS];
4658 local_player->LevelSolved_CountingHealth = health;
4659 local_player->LevelSolved_CountingScore = score;
4661 game_panel_controls[GAME_PANEL_HEALTH].value = health;
4662 game_panel_controls[GAME_PANEL_SCORE].value = score;
4664 DisplayGameControlValues();
4666 if (health == health_final)
4667 StopSound(SND_GAME_LEVELTIME_BONUS);
4668 else if (setup.sound_loops)
4669 PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4671 PlaySound(SND_GAME_LEVELTIME_BONUS);
4676 local_player->LevelSolved_PanelOff = TRUE;
4678 if (game_over_delay_3 > 0)
4680 game_over_delay_3--;
4691 boolean raise_level = FALSE;
4693 local_player->LevelSolved_GameEnd = TRUE;
4695 if (!global.use_envelope_request)
4696 CloseDoor(DOOR_CLOSE_1);
4698 if (local_player->LevelSolved_SaveTape)
4700 SaveTapeChecked(tape.level_nr); /* ask to save tape */
4703 CloseDoor(DOOR_CLOSE_ALL);
4705 if (level_editor_test_game)
4707 SetGameStatus(GAME_MODE_MAIN);
4714 if (!local_player->LevelSolved_SaveScore)
4716 SetGameStatus(GAME_MODE_MAIN);
4723 if (level_nr == leveldir_current->handicap_level)
4725 leveldir_current->handicap_level++;
4727 SaveLevelSetup_SeriesInfo();
4730 if (setup.increment_levels &&
4731 level_nr < leveldir_current->last_level)
4732 raise_level = TRUE; /* advance to next level */
4734 if ((hi_pos = NewHiScore()) >= 0)
4736 SetGameStatus(GAME_MODE_SCORES);
4738 DrawHallOfFame(hi_pos);
4748 SetGameStatus(GAME_MODE_MAIN);
4764 boolean one_score_entry_per_name = !program.many_scores_per_name;
4766 LoadScore(level_nr);
4768 if (strEqual(setup.player_name, EMPTY_PLAYER_NAME) ||
4769 local_player->score_final < highscore[MAX_SCORE_ENTRIES - 1].Score)
4772 for (k = 0; k < MAX_SCORE_ENTRIES; k++)
4774 if (local_player->score_final > highscore[k].Score)
4776 /* player has made it to the hall of fame */
4778 if (k < MAX_SCORE_ENTRIES - 1)
4780 int m = MAX_SCORE_ENTRIES - 1;
4782 if (one_score_entry_per_name)
4784 for (l = k; l < MAX_SCORE_ENTRIES; l++)
4785 if (strEqual(setup.player_name, highscore[l].Name))
4788 if (m == k) /* player's new highscore overwrites his old one */
4792 for (l = m; l > k; l--)
4794 strcpy(highscore[l].Name, highscore[l - 1].Name);
4795 highscore[l].Score = highscore[l - 1].Score;
4801 strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
4802 highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
4803 highscore[k].Score = local_player->score_final;
4808 else if (one_score_entry_per_name &&
4809 !strncmp(setup.player_name, highscore[k].Name,
4810 MAX_PLAYER_NAME_LEN))
4811 break; /* player already there with a higher score */
4815 SaveScore(level_nr);
4820 inline static int getElementMoveStepsizeExt(int x, int y, int direction)
4822 int element = Feld[x][y];
4823 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
4824 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
4825 int horiz_move = (dx != 0);
4826 int sign = (horiz_move ? dx : dy);
4827 int step = sign * element_info[element].move_stepsize;
4829 /* special values for move stepsize for spring and things on conveyor belt */
4832 if (CAN_FALL(element) &&
4833 y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
4834 step = sign * MOVE_STEPSIZE_NORMAL / 2;
4835 else if (element == EL_SPRING)
4836 step = sign * MOVE_STEPSIZE_NORMAL * 2;
4842 inline static int getElementMoveStepsize(int x, int y)
4844 return getElementMoveStepsizeExt(x, y, MovDir[x][y]);
4847 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
4849 if (player->GfxAction != action || player->GfxDir != dir)
4851 player->GfxAction = action;
4852 player->GfxDir = dir;
4854 player->StepFrame = 0;
4858 static void ResetGfxFrame(int x, int y)
4860 // profiling showed that "autotest" spends 10~20% of its time in this function
4861 if (DrawingDeactivatedField())
4864 int element = Feld[x][y];
4865 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
4867 if (graphic_info[graphic].anim_global_sync)
4868 GfxFrame[x][y] = FrameCounter;
4869 else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
4870 GfxFrame[x][y] = CustomValue[x][y];
4871 else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
4872 GfxFrame[x][y] = element_info[element].collect_score;
4873 else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
4874 GfxFrame[x][y] = ChangeDelay[x][y];
4877 static void ResetGfxAnimation(int x, int y)
4879 GfxAction[x][y] = ACTION_DEFAULT;
4880 GfxDir[x][y] = MovDir[x][y];
4883 ResetGfxFrame(x, y);
4886 static void ResetRandomAnimationValue(int x, int y)
4888 GfxRandom[x][y] = INIT_GFX_RANDOM();
4891 void InitMovingField(int x, int y, int direction)
4893 int element = Feld[x][y];
4894 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
4895 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
4898 boolean is_moving_before, is_moving_after;
4900 /* check if element was/is moving or being moved before/after mode change */
4901 is_moving_before = (WasJustMoving[x][y] != 0);
4902 is_moving_after = (getElementMoveStepsizeExt(x, y, direction) != 0);
4904 /* reset animation only for moving elements which change direction of moving
4905 or which just started or stopped moving
4906 (else CEs with property "can move" / "not moving" are reset each frame) */
4907 if (is_moving_before != is_moving_after ||
4908 direction != MovDir[x][y])
4909 ResetGfxAnimation(x, y);
4911 MovDir[x][y] = direction;
4912 GfxDir[x][y] = direction;
4914 GfxAction[x][y] = (!is_moving_after ? ACTION_WAITING :
4915 direction == MV_DOWN && CAN_FALL(element) ?
4916 ACTION_FALLING : ACTION_MOVING);
4918 /* this is needed for CEs with property "can move" / "not moving" */
4920 if (is_moving_after)
4922 if (Feld[newx][newy] == EL_EMPTY)
4923 Feld[newx][newy] = EL_BLOCKED;
4925 MovDir[newx][newy] = MovDir[x][y];
4927 CustomValue[newx][newy] = CustomValue[x][y];
4929 GfxFrame[newx][newy] = GfxFrame[x][y];
4930 GfxRandom[newx][newy] = GfxRandom[x][y];
4931 GfxAction[newx][newy] = GfxAction[x][y];
4932 GfxDir[newx][newy] = GfxDir[x][y];
4936 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
4938 int direction = MovDir[x][y];
4939 int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0);
4940 int newy = y + (direction & MV_UP ? -1 : direction & MV_DOWN ? +1 : 0);
4946 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
4948 int oldx = x, oldy = y;
4949 int direction = MovDir[x][y];
4951 if (direction == MV_LEFT)
4953 else if (direction == MV_RIGHT)
4955 else if (direction == MV_UP)
4957 else if (direction == MV_DOWN)
4960 *comes_from_x = oldx;
4961 *comes_from_y = oldy;
4964 int MovingOrBlocked2Element(int x, int y)
4966 int element = Feld[x][y];
4968 if (element == EL_BLOCKED)
4972 Blocked2Moving(x, y, &oldx, &oldy);
4973 return Feld[oldx][oldy];
4979 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
4981 /* like MovingOrBlocked2Element(), but if element is moving
4982 and (x,y) is the field the moving element is just leaving,
4983 return EL_BLOCKED instead of the element value */
4984 int element = Feld[x][y];
4986 if (IS_MOVING(x, y))
4988 if (element == EL_BLOCKED)
4992 Blocked2Moving(x, y, &oldx, &oldy);
4993 return Feld[oldx][oldy];
5002 static void RemoveField(int x, int y)
5004 Feld[x][y] = EL_EMPTY;
5010 CustomValue[x][y] = 0;
5013 ChangeDelay[x][y] = 0;
5014 ChangePage[x][y] = -1;
5015 Pushed[x][y] = FALSE;
5017 GfxElement[x][y] = EL_UNDEFINED;
5018 GfxAction[x][y] = ACTION_DEFAULT;
5019 GfxDir[x][y] = MV_NONE;
5022 void RemoveMovingField(int x, int y)
5024 int oldx = x, oldy = y, newx = x, newy = y;
5025 int element = Feld[x][y];
5026 int next_element = EL_UNDEFINED;
5028 if (element != EL_BLOCKED && !IS_MOVING(x, y))
5031 if (IS_MOVING(x, y))
5033 Moving2Blocked(x, y, &newx, &newy);
5035 if (Feld[newx][newy] != EL_BLOCKED)
5037 /* element is moving, but target field is not free (blocked), but
5038 already occupied by something different (example: acid pool);
5039 in this case, only remove the moving field, but not the target */
5041 RemoveField(oldx, oldy);
5043 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5045 TEST_DrawLevelField(oldx, oldy);
5050 else if (element == EL_BLOCKED)
5052 Blocked2Moving(x, y, &oldx, &oldy);
5053 if (!IS_MOVING(oldx, oldy))
5057 if (element == EL_BLOCKED &&
5058 (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
5059 Feld[oldx][oldy] == EL_QUICKSAND_FAST_EMPTYING ||
5060 Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
5061 Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
5062 Feld[oldx][oldy] == EL_DC_MAGIC_WALL_EMPTYING ||
5063 Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
5064 next_element = get_next_element(Feld[oldx][oldy]);
5066 RemoveField(oldx, oldy);
5067 RemoveField(newx, newy);
5069 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5071 if (next_element != EL_UNDEFINED)
5072 Feld[oldx][oldy] = next_element;
5074 TEST_DrawLevelField(oldx, oldy);
5075 TEST_DrawLevelField(newx, newy);
5078 void DrawDynamite(int x, int y)
5080 int sx = SCREENX(x), sy = SCREENY(y);
5081 int graphic = el2img(Feld[x][y]);
5084 if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
5087 if (IS_WALKABLE_INSIDE(Back[x][y]))
5091 DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
5092 else if (Store[x][y])
5093 DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
5095 frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5097 if (Back[x][y] || Store[x][y])
5098 DrawGraphicThruMask(sx, sy, graphic, frame);
5100 DrawGraphic(sx, sy, graphic, frame);
5103 void CheckDynamite(int x, int y)
5105 if (MovDelay[x][y] != 0) /* dynamite is still waiting to explode */
5109 if (MovDelay[x][y] != 0)
5112 PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5118 StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5123 static void setMinimalPlayerBoundaries(int *sx1, int *sy1, int *sx2, int *sy2)
5125 boolean num_checked_players = 0;
5128 for (i = 0; i < MAX_PLAYERS; i++)
5130 if (stored_player[i].active)
5132 int sx = stored_player[i].jx;
5133 int sy = stored_player[i].jy;
5135 if (num_checked_players == 0)
5142 *sx1 = MIN(*sx1, sx);
5143 *sy1 = MIN(*sy1, sy);
5144 *sx2 = MAX(*sx2, sx);
5145 *sy2 = MAX(*sy2, sy);
5148 num_checked_players++;
5153 static boolean checkIfAllPlayersFitToScreen_RND()
5155 int sx1 = 0, sy1 = 0, sx2 = 0, sy2 = 0;
5157 setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5159 return (sx2 - sx1 < SCR_FIELDX &&
5160 sy2 - sy1 < SCR_FIELDY);
5163 static void setScreenCenteredToAllPlayers(int *sx, int *sy)
5165 int sx1 = scroll_x, sy1 = scroll_y, sx2 = scroll_x, sy2 = scroll_y;
5167 setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5169 *sx = (sx1 + sx2) / 2;
5170 *sy = (sy1 + sy2) / 2;
5173 void DrawRelocateScreen(int old_x, int old_y, int x, int y, int move_dir,
5174 boolean center_screen, boolean quick_relocation)
5176 unsigned int frame_delay_value_old = GetVideoFrameDelay();
5177 boolean ffwd_delay = (tape.playing && tape.fast_forward);
5178 boolean no_delay = (tape.warp_forward);
5179 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5180 int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5181 int new_scroll_x, new_scroll_y;
5183 if (level.lazy_relocation && IN_VIS_FIELD(SCREENX(x), SCREENY(y)))
5185 /* case 1: quick relocation inside visible screen (without scrolling) */
5192 if (!level.shifted_relocation || center_screen)
5194 /* relocation _with_ centering of screen */
5196 new_scroll_x = SCROLL_POSITION_X(x);
5197 new_scroll_y = SCROLL_POSITION_Y(y);
5201 /* relocation _without_ centering of screen */
5203 int center_scroll_x = SCROLL_POSITION_X(old_x);
5204 int center_scroll_y = SCROLL_POSITION_Y(old_y);
5205 int offset_x = x + (scroll_x - center_scroll_x);
5206 int offset_y = y + (scroll_y - center_scroll_y);
5208 /* for new screen position, apply previous offset to center position */
5209 new_scroll_x = SCROLL_POSITION_X(offset_x);
5210 new_scroll_y = SCROLL_POSITION_Y(offset_y);
5213 if (quick_relocation)
5215 /* case 2: quick relocation (redraw without visible scrolling) */
5217 scroll_x = new_scroll_x;
5218 scroll_y = new_scroll_y;
5225 /* case 3: visible relocation (with scrolling to new position) */
5227 ScrollScreen(NULL, SCROLL_GO_ON); /* scroll last frame to full tile */
5229 SetVideoFrameDelay(wait_delay_value);
5231 while (scroll_x != new_scroll_x || scroll_y != new_scroll_y)
5234 int fx = FX, fy = FY;
5236 dx = (new_scroll_x < scroll_x ? +1 : new_scroll_x > scroll_x ? -1 : 0);
5237 dy = (new_scroll_y < scroll_y ? +1 : new_scroll_y > scroll_y ? -1 : 0);
5239 if (dx == 0 && dy == 0) /* no scrolling needed at all */
5245 fx += dx * TILEX / 2;
5246 fy += dy * TILEY / 2;
5248 ScrollLevel(dx, dy);
5251 /* scroll in two steps of half tile size to make things smoother */
5252 BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
5254 /* scroll second step to align at full tile size */
5255 BlitScreenToBitmap(window);
5261 SetVideoFrameDelay(frame_delay_value_old);
5264 void RelocatePlayer(int jx, int jy, int el_player_raw)
5266 int el_player = GET_PLAYER_ELEMENT(el_player_raw);
5267 int player_nr = GET_PLAYER_NR(el_player);
5268 struct PlayerInfo *player = &stored_player[player_nr];
5269 boolean ffwd_delay = (tape.playing && tape.fast_forward);
5270 boolean no_delay = (tape.warp_forward);
5271 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5272 int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5273 int old_jx = player->jx;
5274 int old_jy = player->jy;
5275 int old_element = Feld[old_jx][old_jy];
5276 int element = Feld[jx][jy];
5277 boolean player_relocated = (old_jx != jx || old_jy != jy);
5279 int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
5280 int move_dir_vert = (jy < old_jy ? MV_UP : jy > old_jy ? MV_DOWN : 0);
5281 int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
5282 int enter_side_vert = MV_DIR_OPPOSITE(move_dir_vert);
5283 int leave_side_horiz = move_dir_horiz;
5284 int leave_side_vert = move_dir_vert;
5285 int enter_side = enter_side_horiz | enter_side_vert;
5286 int leave_side = leave_side_horiz | leave_side_vert;
5288 if (player->GameOver) /* do not reanimate dead player */
5291 if (!player_relocated) /* no need to relocate the player */
5294 if (IS_PLAYER(jx, jy)) /* player already placed at new position */
5296 RemoveField(jx, jy); /* temporarily remove newly placed player */
5297 DrawLevelField(jx, jy);
5300 if (player->present)
5302 while (player->MovPos)
5304 ScrollPlayer(player, SCROLL_GO_ON);
5305 ScrollScreen(NULL, SCROLL_GO_ON);
5307 AdvanceFrameAndPlayerCounters(player->index_nr);
5311 BackToFront_WithFrameDelay(wait_delay_value);
5314 DrawPlayer(player); /* needed here only to cleanup last field */
5315 DrawLevelField(player->jx, player->jy); /* remove player graphic */
5317 player->is_moving = FALSE;
5320 if (IS_CUSTOM_ELEMENT(old_element))
5321 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
5323 player->index_bit, leave_side);
5325 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
5327 player->index_bit, leave_side);
5329 Feld[jx][jy] = el_player;
5330 InitPlayerField(jx, jy, el_player, TRUE);
5332 /* "InitPlayerField()" above sets Feld[jx][jy] to EL_EMPTY, but it may be
5333 possible that the relocation target field did not contain a player element,
5334 but a walkable element, to which the new player was relocated -- in this
5335 case, restore that (already initialized!) element on the player field */
5336 if (!ELEM_IS_PLAYER(element)) /* player may be set on walkable element */
5338 Feld[jx][jy] = element; /* restore previously existing element */
5341 /* only visually relocate centered player */
5342 DrawRelocateScreen(old_jx, old_jy, player->jx, player->jy, player->MovDir,
5343 FALSE, level.instant_relocation);
5345 TestIfPlayerTouchesBadThing(jx, jy);
5346 TestIfPlayerTouchesCustomElement(jx, jy);
5348 if (IS_CUSTOM_ELEMENT(element))
5349 CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
5350 player->index_bit, enter_side);
5352 CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
5353 player->index_bit, enter_side);
5355 if (player->is_switching)
5357 /* ensure that relocation while still switching an element does not cause
5358 a new element to be treated as also switched directly after relocation
5359 (this is important for teleporter switches that teleport the player to
5360 a place where another teleporter switch is in the same direction, which
5361 would then incorrectly be treated as immediately switched before the
5362 direction key that caused the switch was released) */
5364 player->switch_x += jx - old_jx;
5365 player->switch_y += jy - old_jy;
5369 void Explode(int ex, int ey, int phase, int mode)
5375 /* !!! eliminate this variable !!! */
5376 int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
5378 if (game.explosions_delayed)
5380 ExplodeField[ex][ey] = mode;
5384 if (phase == EX_PHASE_START) /* initialize 'Store[][]' field */
5386 int center_element = Feld[ex][ey];
5387 int artwork_element, explosion_element; /* set these values later */
5389 /* remove things displayed in background while burning dynamite */
5390 if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
5393 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
5395 /* put moving element to center field (and let it explode there) */
5396 center_element = MovingOrBlocked2Element(ex, ey);
5397 RemoveMovingField(ex, ey);
5398 Feld[ex][ey] = center_element;
5401 /* now "center_element" is finally determined -- set related values now */
5402 artwork_element = center_element; /* for custom player artwork */
5403 explosion_element = center_element; /* for custom player artwork */
5405 if (IS_PLAYER(ex, ey))
5407 int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
5409 artwork_element = stored_player[player_nr].artwork_element;
5411 if (level.use_explosion_element[player_nr])
5413 explosion_element = level.explosion_element[player_nr];
5414 artwork_element = explosion_element;
5418 if (mode == EX_TYPE_NORMAL ||
5419 mode == EX_TYPE_CENTER ||
5420 mode == EX_TYPE_CROSS)
5421 PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
5423 last_phase = element_info[explosion_element].explosion_delay + 1;
5425 for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
5427 int xx = x - ex + 1;
5428 int yy = y - ey + 1;
5431 if (!IN_LEV_FIELD(x, y) ||
5432 (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
5433 (mode == EX_TYPE_CROSS && (x != ex && y != ey)))
5436 element = Feld[x][y];
5438 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
5440 element = MovingOrBlocked2Element(x, y);
5442 if (!IS_EXPLOSION_PROOF(element))
5443 RemoveMovingField(x, y);
5446 /* indestructible elements can only explode in center (but not flames) */
5447 if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
5448 mode == EX_TYPE_BORDER)) ||
5449 element == EL_FLAMES)
5452 /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
5453 behaviour, for example when touching a yamyam that explodes to rocks
5454 with active deadly shield, a rock is created under the player !!! */
5455 /* (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8) */
5457 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
5458 (game.engine_version < VERSION_IDENT(3,1,0,0) ||
5459 (x == ex && y == ey && mode != EX_TYPE_BORDER)))
5461 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
5464 if (IS_ACTIVE_BOMB(element))
5466 /* re-activate things under the bomb like gate or penguin */
5467 Feld[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
5474 /* save walkable background elements while explosion on same tile */
5475 if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
5476 (x != ex || y != ey || mode == EX_TYPE_BORDER))
5477 Back[x][y] = element;
5479 /* ignite explodable elements reached by other explosion */
5480 if (element == EL_EXPLOSION)
5481 element = Store2[x][y];
5483 if (AmoebaNr[x][y] &&
5484 (element == EL_AMOEBA_FULL ||
5485 element == EL_BD_AMOEBA ||
5486 element == EL_AMOEBA_GROWING))
5488 AmoebaCnt[AmoebaNr[x][y]]--;
5489 AmoebaCnt2[AmoebaNr[x][y]]--;
5494 if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
5496 int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
5498 Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
5500 if (PLAYERINFO(ex, ey)->use_murphy)
5501 Store[x][y] = EL_EMPTY;
5504 /* !!! check this case -- currently needed for rnd_rado_negundo_v,
5505 !!! levels 015 018 019 020 021 022 023 026 027 028 !!! */
5506 else if (ELEM_IS_PLAYER(center_element))
5507 Store[x][y] = EL_EMPTY;
5508 else if (center_element == EL_YAMYAM)
5509 Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
5510 else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
5511 Store[x][y] = element_info[center_element].content.e[xx][yy];
5513 /* needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
5514 (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
5515 otherwise) -- FIX THIS !!! */
5516 else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
5517 Store[x][y] = element_info[element].content.e[1][1];
5519 else if (!CAN_EXPLODE(element))
5520 Store[x][y] = element_info[element].content.e[1][1];
5523 Store[x][y] = EL_EMPTY;
5525 if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
5526 center_element == EL_AMOEBA_TO_DIAMOND)
5527 Store2[x][y] = element;
5529 Feld[x][y] = EL_EXPLOSION;
5530 GfxElement[x][y] = artwork_element;
5532 ExplodePhase[x][y] = 1;
5533 ExplodeDelay[x][y] = last_phase;
5538 if (center_element == EL_YAMYAM)
5539 game.yamyam_content_nr =
5540 (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
5552 GfxFrame[x][y] = 0; /* restart explosion animation */
5554 last_phase = ExplodeDelay[x][y];
5556 ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
5558 /* this can happen if the player leaves an explosion just in time */
5559 if (GfxElement[x][y] == EL_UNDEFINED)
5560 GfxElement[x][y] = EL_EMPTY;
5562 border_element = Store2[x][y];
5563 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5564 border_element = StorePlayer[x][y];
5566 if (phase == element_info[border_element].ignition_delay ||
5567 phase == last_phase)
5569 boolean border_explosion = FALSE;
5571 if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
5572 !PLAYER_EXPLOSION_PROTECTED(x, y))
5574 KillPlayerUnlessExplosionProtected(x, y);
5575 border_explosion = TRUE;
5577 else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
5579 Feld[x][y] = Store2[x][y];
5582 border_explosion = TRUE;
5584 else if (border_element == EL_AMOEBA_TO_DIAMOND)
5586 AmoebeUmwandeln(x, y);
5588 border_explosion = TRUE;
5591 /* if an element just explodes due to another explosion (chain-reaction),
5592 do not immediately end the new explosion when it was the last frame of
5593 the explosion (as it would be done in the following "if"-statement!) */
5594 if (border_explosion && phase == last_phase)
5598 if (phase == last_phase)
5602 element = Feld[x][y] = Store[x][y];
5603 Store[x][y] = Store2[x][y] = 0;
5604 GfxElement[x][y] = EL_UNDEFINED;
5606 /* player can escape from explosions and might therefore be still alive */
5607 if (element >= EL_PLAYER_IS_EXPLODING_1 &&
5608 element <= EL_PLAYER_IS_EXPLODING_4)
5610 int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
5611 int explosion_element = EL_PLAYER_1 + player_nr;
5612 int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
5613 int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
5615 if (level.use_explosion_element[player_nr])
5616 explosion_element = level.explosion_element[player_nr];
5618 Feld[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
5619 element_info[explosion_element].content.e[xx][yy]);
5622 /* restore probably existing indestructible background element */
5623 if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
5624 element = Feld[x][y] = Back[x][y];
5627 MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
5628 GfxDir[x][y] = MV_NONE;
5629 ChangeDelay[x][y] = 0;
5630 ChangePage[x][y] = -1;
5632 CustomValue[x][y] = 0;
5634 InitField_WithBug2(x, y, FALSE);
5636 TEST_DrawLevelField(x, y);
5638 TestIfElementTouchesCustomElement(x, y);
5640 if (GFX_CRUMBLED(element))
5641 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
5643 if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
5644 StorePlayer[x][y] = 0;
5646 if (ELEM_IS_PLAYER(element))
5647 RelocatePlayer(x, y, element);
5649 else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
5651 int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
5652 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5655 TEST_DrawLevelFieldCrumbled(x, y);
5657 if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
5659 DrawLevelElement(x, y, Back[x][y]);
5660 DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
5662 else if (IS_WALKABLE_UNDER(Back[x][y]))
5664 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5665 DrawLevelElementThruMask(x, y, Back[x][y]);
5667 else if (!IS_WALKABLE_INSIDE(Back[x][y]))
5668 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5672 void DynaExplode(int ex, int ey)
5675 int dynabomb_element = Feld[ex][ey];
5676 int dynabomb_size = 1;
5677 boolean dynabomb_xl = FALSE;
5678 struct PlayerInfo *player;
5679 static int xy[4][2] =
5687 if (IS_ACTIVE_BOMB(dynabomb_element))
5689 player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
5690 dynabomb_size = player->dynabomb_size;
5691 dynabomb_xl = player->dynabomb_xl;
5692 player->dynabombs_left++;
5695 Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
5697 for (i = 0; i < NUM_DIRECTIONS; i++)
5699 for (j = 1; j <= dynabomb_size; j++)
5701 int x = ex + j * xy[i][0];
5702 int y = ey + j * xy[i][1];
5705 if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
5708 element = Feld[x][y];
5710 /* do not restart explosions of fields with active bombs */
5711 if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
5714 Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
5716 if (element != EL_EMPTY && element != EL_EXPLOSION &&
5717 !IS_DIGGABLE(element) && !dynabomb_xl)
5723 void Bang(int x, int y)
5725 int element = MovingOrBlocked2Element(x, y);
5726 int explosion_type = EX_TYPE_NORMAL;
5728 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5730 struct PlayerInfo *player = PLAYERINFO(x, y);
5732 element = Feld[x][y] = player->initial_element;
5734 if (level.use_explosion_element[player->index_nr])
5736 int explosion_element = level.explosion_element[player->index_nr];
5738 if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
5739 explosion_type = EX_TYPE_CROSS;
5740 else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
5741 explosion_type = EX_TYPE_CENTER;
5749 case EL_BD_BUTTERFLY:
5752 case EL_DARK_YAMYAM:
5756 RaiseScoreElement(element);
5759 case EL_DYNABOMB_PLAYER_1_ACTIVE:
5760 case EL_DYNABOMB_PLAYER_2_ACTIVE:
5761 case EL_DYNABOMB_PLAYER_3_ACTIVE:
5762 case EL_DYNABOMB_PLAYER_4_ACTIVE:
5763 case EL_DYNABOMB_INCREASE_NUMBER:
5764 case EL_DYNABOMB_INCREASE_SIZE:
5765 case EL_DYNABOMB_INCREASE_POWER:
5766 explosion_type = EX_TYPE_DYNA;
5769 case EL_DC_LANDMINE:
5770 explosion_type = EX_TYPE_CENTER;
5775 case EL_LAMP_ACTIVE:
5776 case EL_AMOEBA_TO_DIAMOND:
5777 if (!IS_PLAYER(x, y)) /* penguin and player may be at same field */
5778 explosion_type = EX_TYPE_CENTER;
5782 if (element_info[element].explosion_type == EXPLODES_CROSS)
5783 explosion_type = EX_TYPE_CROSS;
5784 else if (element_info[element].explosion_type == EXPLODES_1X1)
5785 explosion_type = EX_TYPE_CENTER;
5789 if (explosion_type == EX_TYPE_DYNA)
5792 Explode(x, y, EX_PHASE_START, explosion_type);
5794 CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
5797 void SplashAcid(int x, int y)
5799 if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
5800 (!IN_LEV_FIELD(x - 1, y - 2) ||
5801 !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
5802 Feld[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
5804 if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
5805 (!IN_LEV_FIELD(x + 1, y - 2) ||
5806 !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
5807 Feld[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
5809 PlayLevelSound(x, y, SND_ACID_SPLASHING);
5812 static void InitBeltMovement()
5814 static int belt_base_element[4] =
5816 EL_CONVEYOR_BELT_1_LEFT,
5817 EL_CONVEYOR_BELT_2_LEFT,
5818 EL_CONVEYOR_BELT_3_LEFT,
5819 EL_CONVEYOR_BELT_4_LEFT
5821 static int belt_base_active_element[4] =
5823 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
5824 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
5825 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
5826 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
5831 /* set frame order for belt animation graphic according to belt direction */
5832 for (i = 0; i < NUM_BELTS; i++)
5836 for (j = 0; j < NUM_BELT_PARTS; j++)
5838 int element = belt_base_active_element[belt_nr] + j;
5839 int graphic_1 = el2img(element);
5840 int graphic_2 = el2panelimg(element);
5842 if (game.belt_dir[i] == MV_LEFT)
5844 graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
5845 graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
5849 graphic_info[graphic_1].anim_mode |= ANIM_REVERSE;
5850 graphic_info[graphic_2].anim_mode |= ANIM_REVERSE;
5855 SCAN_PLAYFIELD(x, y)
5857 int element = Feld[x][y];
5859 for (i = 0; i < NUM_BELTS; i++)
5861 if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
5863 int e_belt_nr = getBeltNrFromBeltElement(element);
5866 if (e_belt_nr == belt_nr)
5868 int belt_part = Feld[x][y] - belt_base_element[belt_nr];
5870 Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
5877 static void ToggleBeltSwitch(int x, int y)
5879 static int belt_base_element[4] =
5881 EL_CONVEYOR_BELT_1_LEFT,
5882 EL_CONVEYOR_BELT_2_LEFT,
5883 EL_CONVEYOR_BELT_3_LEFT,
5884 EL_CONVEYOR_BELT_4_LEFT
5886 static int belt_base_active_element[4] =
5888 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
5889 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
5890 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
5891 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
5893 static int belt_base_switch_element[4] =
5895 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
5896 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
5897 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
5898 EL_CONVEYOR_BELT_4_SWITCH_LEFT
5900 static int belt_move_dir[4] =
5908 int element = Feld[x][y];
5909 int belt_nr = getBeltNrFromBeltSwitchElement(element);
5910 int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
5911 int belt_dir = belt_move_dir[belt_dir_nr];
5914 if (!IS_BELT_SWITCH(element))
5917 game.belt_dir_nr[belt_nr] = belt_dir_nr;
5918 game.belt_dir[belt_nr] = belt_dir;
5920 if (belt_dir_nr == 3)
5923 /* set frame order for belt animation graphic according to belt direction */
5924 for (i = 0; i < NUM_BELT_PARTS; i++)
5926 int element = belt_base_active_element[belt_nr] + i;
5927 int graphic_1 = el2img(element);
5928 int graphic_2 = el2panelimg(element);
5930 if (belt_dir == MV_LEFT)
5932 graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
5933 graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
5937 graphic_info[graphic_1].anim_mode |= ANIM_REVERSE;
5938 graphic_info[graphic_2].anim_mode |= ANIM_REVERSE;
5942 SCAN_PLAYFIELD(xx, yy)
5944 int element = Feld[xx][yy];
5946 if (IS_BELT_SWITCH(element))
5948 int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
5950 if (e_belt_nr == belt_nr)
5952 Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
5953 TEST_DrawLevelField(xx, yy);
5956 else if (IS_BELT(element) && belt_dir != MV_NONE)
5958 int e_belt_nr = getBeltNrFromBeltElement(element);
5960 if (e_belt_nr == belt_nr)
5962 int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
5964 Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
5965 TEST_DrawLevelField(xx, yy);
5968 else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
5970 int e_belt_nr = getBeltNrFromBeltActiveElement(element);
5972 if (e_belt_nr == belt_nr)
5974 int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
5976 Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
5977 TEST_DrawLevelField(xx, yy);
5983 static void ToggleSwitchgateSwitch(int x, int y)
5987 game.switchgate_pos = !game.switchgate_pos;
5989 SCAN_PLAYFIELD(xx, yy)
5991 int element = Feld[xx][yy];
5993 if (element == EL_SWITCHGATE_SWITCH_UP)
5995 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_DOWN;
5996 TEST_DrawLevelField(xx, yy);
5998 else if (element == EL_SWITCHGATE_SWITCH_DOWN)
6000 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP;
6001 TEST_DrawLevelField(xx, yy);
6003 else if (element == EL_DC_SWITCHGATE_SWITCH_UP)
6005 Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_DOWN;
6006 TEST_DrawLevelField(xx, yy);
6008 else if (element == EL_DC_SWITCHGATE_SWITCH_DOWN)
6010 Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP;
6011 TEST_DrawLevelField(xx, yy);
6013 else if (element == EL_SWITCHGATE_OPEN ||
6014 element == EL_SWITCHGATE_OPENING)
6016 Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
6018 PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
6020 else if (element == EL_SWITCHGATE_CLOSED ||
6021 element == EL_SWITCHGATE_CLOSING)
6023 Feld[xx][yy] = EL_SWITCHGATE_OPENING;
6025 PlayLevelSoundAction(xx, yy, ACTION_OPENING);
6030 static int getInvisibleActiveFromInvisibleElement(int element)
6032 return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
6033 element == EL_INVISIBLE_WALL ? EL_INVISIBLE_WALL_ACTIVE :
6034 element == EL_INVISIBLE_SAND ? EL_INVISIBLE_SAND_ACTIVE :
6038 static int getInvisibleFromInvisibleActiveElement(int element)
6040 return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
6041 element == EL_INVISIBLE_WALL_ACTIVE ? EL_INVISIBLE_WALL :
6042 element == EL_INVISIBLE_SAND_ACTIVE ? EL_INVISIBLE_SAND :
6046 static void RedrawAllLightSwitchesAndInvisibleElements()
6050 SCAN_PLAYFIELD(x, y)
6052 int element = Feld[x][y];
6054 if (element == EL_LIGHT_SWITCH &&
6055 game.light_time_left > 0)
6057 Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
6058 TEST_DrawLevelField(x, y);
6060 else if (element == EL_LIGHT_SWITCH_ACTIVE &&
6061 game.light_time_left == 0)
6063 Feld[x][y] = EL_LIGHT_SWITCH;
6064 TEST_DrawLevelField(x, y);
6066 else if (element == EL_EMC_DRIPPER &&
6067 game.light_time_left > 0)
6069 Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
6070 TEST_DrawLevelField(x, y);
6072 else if (element == EL_EMC_DRIPPER_ACTIVE &&
6073 game.light_time_left == 0)
6075 Feld[x][y] = EL_EMC_DRIPPER;
6076 TEST_DrawLevelField(x, y);
6078 else if (element == EL_INVISIBLE_STEELWALL ||
6079 element == EL_INVISIBLE_WALL ||
6080 element == EL_INVISIBLE_SAND)
6082 if (game.light_time_left > 0)
6083 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
6085 TEST_DrawLevelField(x, y);
6087 /* uncrumble neighbour fields, if needed */
6088 if (element == EL_INVISIBLE_SAND)
6089 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6091 else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6092 element == EL_INVISIBLE_WALL_ACTIVE ||
6093 element == EL_INVISIBLE_SAND_ACTIVE)
6095 if (game.light_time_left == 0)
6096 Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
6098 TEST_DrawLevelField(x, y);
6100 /* re-crumble neighbour fields, if needed */
6101 if (element == EL_INVISIBLE_SAND)
6102 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6107 static void RedrawAllInvisibleElementsForLenses()
6111 SCAN_PLAYFIELD(x, y)
6113 int element = Feld[x][y];
6115 if (element == EL_EMC_DRIPPER &&
6116 game.lenses_time_left > 0)
6118 Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
6119 TEST_DrawLevelField(x, y);
6121 else if (element == EL_EMC_DRIPPER_ACTIVE &&
6122 game.lenses_time_left == 0)
6124 Feld[x][y] = EL_EMC_DRIPPER;
6125 TEST_DrawLevelField(x, y);
6127 else if (element == EL_INVISIBLE_STEELWALL ||
6128 element == EL_INVISIBLE_WALL ||
6129 element == EL_INVISIBLE_SAND)
6131 if (game.lenses_time_left > 0)
6132 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
6134 TEST_DrawLevelField(x, y);
6136 /* uncrumble neighbour fields, if needed */
6137 if (element == EL_INVISIBLE_SAND)
6138 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6140 else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6141 element == EL_INVISIBLE_WALL_ACTIVE ||
6142 element == EL_INVISIBLE_SAND_ACTIVE)
6144 if (game.lenses_time_left == 0)
6145 Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
6147 TEST_DrawLevelField(x, y);
6149 /* re-crumble neighbour fields, if needed */
6150 if (element == EL_INVISIBLE_SAND)
6151 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6156 static void RedrawAllInvisibleElementsForMagnifier()
6160 SCAN_PLAYFIELD(x, y)
6162 int element = Feld[x][y];
6164 if (element == EL_EMC_FAKE_GRASS &&
6165 game.magnify_time_left > 0)
6167 Feld[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
6168 TEST_DrawLevelField(x, y);
6170 else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
6171 game.magnify_time_left == 0)
6173 Feld[x][y] = EL_EMC_FAKE_GRASS;
6174 TEST_DrawLevelField(x, y);
6176 else if (IS_GATE_GRAY(element) &&
6177 game.magnify_time_left > 0)
6179 Feld[x][y] = (IS_RND_GATE_GRAY(element) ?
6180 element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
6181 IS_EM_GATE_GRAY(element) ?
6182 element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
6183 IS_EMC_GATE_GRAY(element) ?
6184 element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
6185 IS_DC_GATE_GRAY(element) ?
6186 EL_DC_GATE_WHITE_GRAY_ACTIVE :
6188 TEST_DrawLevelField(x, y);
6190 else if (IS_GATE_GRAY_ACTIVE(element) &&
6191 game.magnify_time_left == 0)
6193 Feld[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
6194 element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
6195 IS_EM_GATE_GRAY_ACTIVE(element) ?
6196 element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
6197 IS_EMC_GATE_GRAY_ACTIVE(element) ?
6198 element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
6199 IS_DC_GATE_GRAY_ACTIVE(element) ?
6200 EL_DC_GATE_WHITE_GRAY :
6202 TEST_DrawLevelField(x, y);
6207 static void ToggleLightSwitch(int x, int y)
6209 int element = Feld[x][y];
6211 game.light_time_left =
6212 (element == EL_LIGHT_SWITCH ?
6213 level.time_light * FRAMES_PER_SECOND : 0);
6215 RedrawAllLightSwitchesAndInvisibleElements();
6218 static void ActivateTimegateSwitch(int x, int y)
6222 game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
6224 SCAN_PLAYFIELD(xx, yy)
6226 int element = Feld[xx][yy];
6228 if (element == EL_TIMEGATE_CLOSED ||
6229 element == EL_TIMEGATE_CLOSING)
6231 Feld[xx][yy] = EL_TIMEGATE_OPENING;
6232 PlayLevelSound(xx, yy, SND_CLASS_TIMEGATE_OPENING);
6236 else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
6238 Feld[xx][yy] = EL_TIMEGATE_SWITCH;
6239 TEST_DrawLevelField(xx, yy);
6245 Feld[x][y] = (Feld[x][y] == EL_TIMEGATE_SWITCH ? EL_TIMEGATE_SWITCH_ACTIVE :
6246 EL_DC_TIMEGATE_SWITCH_ACTIVE);
6249 void Impact(int x, int y)
6251 boolean last_line = (y == lev_fieldy - 1);
6252 boolean object_hit = FALSE;
6253 boolean impact = (last_line || object_hit);
6254 int element = Feld[x][y];
6255 int smashed = EL_STEELWALL;
6257 if (!last_line) /* check if element below was hit */
6259 if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
6262 object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
6263 MovDir[x][y + 1] != MV_DOWN ||
6264 MovPos[x][y + 1] <= TILEY / 2));
6266 /* do not smash moving elements that left the smashed field in time */
6267 if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
6268 ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
6271 #if USE_QUICKSAND_IMPACT_BUGFIX
6272 if (Feld[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
6274 RemoveMovingField(x, y + 1);
6275 Feld[x][y + 1] = EL_QUICKSAND_EMPTY;
6276 Feld[x][y + 2] = EL_ROCK;
6277 TEST_DrawLevelField(x, y + 2);
6282 if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTYING && object_hit == FALSE)
6284 RemoveMovingField(x, y + 1);
6285 Feld[x][y + 1] = EL_QUICKSAND_FAST_EMPTY;
6286 Feld[x][y + 2] = EL_ROCK;
6287 TEST_DrawLevelField(x, y + 2);
6294 smashed = MovingOrBlocked2Element(x, y + 1);
6296 impact = (last_line || object_hit);
6299 if (!last_line && smashed == EL_ACID) /* element falls into acid */
6301 SplashAcid(x, y + 1);
6305 /* !!! not sufficient for all cases -- see EL_PEARL below !!! */
6306 /* only reset graphic animation if graphic really changes after impact */
6308 el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
6310 ResetGfxAnimation(x, y);
6311 TEST_DrawLevelField(x, y);
6314 if (impact && CAN_EXPLODE_IMPACT(element))
6319 else if (impact && element == EL_PEARL &&
6320 smashed != EL_DC_MAGIC_WALL && smashed != EL_DC_MAGIC_WALL_ACTIVE)
6322 ResetGfxAnimation(x, y);
6324 Feld[x][y] = EL_PEARL_BREAKING;
6325 PlayLevelSound(x, y, SND_PEARL_BREAKING);
6328 else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
6330 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6335 if (impact && element == EL_AMOEBA_DROP)
6337 if (object_hit && IS_PLAYER(x, y + 1))
6338 KillPlayerUnlessEnemyProtected(x, y + 1);
6339 else if (object_hit && smashed == EL_PENGUIN)
6343 Feld[x][y] = EL_AMOEBA_GROWING;
6344 Store[x][y] = EL_AMOEBA_WET;
6346 ResetRandomAnimationValue(x, y);
6351 if (object_hit) /* check which object was hit */
6353 if ((CAN_PASS_MAGIC_WALL(element) &&
6354 (smashed == EL_MAGIC_WALL ||
6355 smashed == EL_BD_MAGIC_WALL)) ||
6356 (CAN_PASS_DC_MAGIC_WALL(element) &&
6357 smashed == EL_DC_MAGIC_WALL))
6360 int activated_magic_wall =
6361 (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
6362 smashed == EL_BD_MAGIC_WALL ? EL_BD_MAGIC_WALL_ACTIVE :
6363 EL_DC_MAGIC_WALL_ACTIVE);
6365 /* activate magic wall / mill */
6366 SCAN_PLAYFIELD(xx, yy)
6368 if (Feld[xx][yy] == smashed)
6369 Feld[xx][yy] = activated_magic_wall;
6372 game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
6373 game.magic_wall_active = TRUE;
6375 PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
6376 SND_MAGIC_WALL_ACTIVATING :
6377 smashed == EL_BD_MAGIC_WALL ?
6378 SND_BD_MAGIC_WALL_ACTIVATING :
6379 SND_DC_MAGIC_WALL_ACTIVATING));
6382 if (IS_PLAYER(x, y + 1))
6384 if (CAN_SMASH_PLAYER(element))
6386 KillPlayerUnlessEnemyProtected(x, y + 1);
6390 else if (smashed == EL_PENGUIN)
6392 if (CAN_SMASH_PLAYER(element))
6398 else if (element == EL_BD_DIAMOND)
6400 if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
6406 else if (((element == EL_SP_INFOTRON ||
6407 element == EL_SP_ZONK) &&
6408 (smashed == EL_SP_SNIKSNAK ||
6409 smashed == EL_SP_ELECTRON ||
6410 smashed == EL_SP_DISK_ORANGE)) ||
6411 (element == EL_SP_INFOTRON &&
6412 smashed == EL_SP_DISK_YELLOW))
6417 else if (CAN_SMASH_EVERYTHING(element))
6419 if (IS_CLASSIC_ENEMY(smashed) ||
6420 CAN_EXPLODE_SMASHED(smashed))
6425 else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
6427 if (smashed == EL_LAMP ||
6428 smashed == EL_LAMP_ACTIVE)
6433 else if (smashed == EL_NUT)
6435 Feld[x][y + 1] = EL_NUT_BREAKING;
6436 PlayLevelSound(x, y, SND_NUT_BREAKING);
6437 RaiseScoreElement(EL_NUT);
6440 else if (smashed == EL_PEARL)
6442 ResetGfxAnimation(x, y);
6444 Feld[x][y + 1] = EL_PEARL_BREAKING;
6445 PlayLevelSound(x, y, SND_PEARL_BREAKING);
6448 else if (smashed == EL_DIAMOND)
6450 Feld[x][y + 1] = EL_DIAMOND_BREAKING;
6451 PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
6454 else if (IS_BELT_SWITCH(smashed))
6456 ToggleBeltSwitch(x, y + 1);
6458 else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
6459 smashed == EL_SWITCHGATE_SWITCH_DOWN ||
6460 smashed == EL_DC_SWITCHGATE_SWITCH_UP ||
6461 smashed == EL_DC_SWITCHGATE_SWITCH_DOWN)
6463 ToggleSwitchgateSwitch(x, y + 1);
6465 else if (smashed == EL_LIGHT_SWITCH ||
6466 smashed == EL_LIGHT_SWITCH_ACTIVE)
6468 ToggleLightSwitch(x, y + 1);
6472 CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6474 CheckElementChangeBySide(x, y + 1, smashed, element,
6475 CE_SWITCHED, CH_SIDE_TOP);
6476 CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
6482 CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6487 /* play sound of magic wall / mill */
6489 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
6490 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ||
6491 Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE))
6493 if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
6494 PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
6495 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
6496 PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
6497 else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
6498 PlayLevelSound(x, y, SND_DC_MAGIC_WALL_FILLING);
6503 /* play sound of object that hits the ground */
6504 if (last_line || object_hit)
6505 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6508 inline static void TurnRoundExt(int x, int y)
6520 { 0, 0 }, { 0, 0 }, { 0, 0 },
6525 int left, right, back;
6529 { MV_DOWN, MV_UP, MV_RIGHT },
6530 { MV_UP, MV_DOWN, MV_LEFT },
6532 { MV_LEFT, MV_RIGHT, MV_DOWN },
6536 { MV_RIGHT, MV_LEFT, MV_UP }
6539 int element = Feld[x][y];
6540 int move_pattern = element_info[element].move_pattern;
6542 int old_move_dir = MovDir[x][y];
6543 int left_dir = turn[old_move_dir].left;
6544 int right_dir = turn[old_move_dir].right;
6545 int back_dir = turn[old_move_dir].back;
6547 int left_dx = move_xy[left_dir].dx, left_dy = move_xy[left_dir].dy;
6548 int right_dx = move_xy[right_dir].dx, right_dy = move_xy[right_dir].dy;
6549 int move_dx = move_xy[old_move_dir].dx, move_dy = move_xy[old_move_dir].dy;
6550 int back_dx = move_xy[back_dir].dx, back_dy = move_xy[back_dir].dy;
6552 int left_x = x + left_dx, left_y = y + left_dy;
6553 int right_x = x + right_dx, right_y = y + right_dy;
6554 int move_x = x + move_dx, move_y = y + move_dy;
6558 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
6560 TestIfBadThingTouchesOtherBadThing(x, y);
6562 if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
6563 MovDir[x][y] = right_dir;
6564 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6565 MovDir[x][y] = left_dir;
6567 if (element == EL_BUG && MovDir[x][y] != old_move_dir)
6569 else if (element == EL_BD_BUTTERFLY) /* && MovDir[x][y] == left_dir) */
6572 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
6574 TestIfBadThingTouchesOtherBadThing(x, y);
6576 if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
6577 MovDir[x][y] = left_dir;
6578 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6579 MovDir[x][y] = right_dir;
6581 if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
6583 else if (element == EL_BD_FIREFLY) /* && MovDir[x][y] == right_dir) */
6586 else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
6588 TestIfBadThingTouchesOtherBadThing(x, y);
6590 if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
6591 MovDir[x][y] = left_dir;
6592 else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
6593 MovDir[x][y] = right_dir;
6595 if (MovDir[x][y] != old_move_dir)
6598 else if (element == EL_YAMYAM)
6600 boolean can_turn_left = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
6601 boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
6603 if (can_turn_left && can_turn_right)
6604 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6605 else if (can_turn_left)
6606 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6607 else if (can_turn_right)
6608 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6610 MovDir[x][y] = back_dir;
6612 MovDelay[x][y] = 16 + 16 * RND(3);
6614 else if (element == EL_DARK_YAMYAM)
6616 boolean can_turn_left = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6618 boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6621 if (can_turn_left && can_turn_right)
6622 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6623 else if (can_turn_left)
6624 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6625 else if (can_turn_right)
6626 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6628 MovDir[x][y] = back_dir;
6630 MovDelay[x][y] = 16 + 16 * RND(3);
6632 else if (element == EL_PACMAN)
6634 boolean can_turn_left = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
6635 boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
6637 if (can_turn_left && can_turn_right)
6638 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6639 else if (can_turn_left)
6640 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6641 else if (can_turn_right)
6642 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6644 MovDir[x][y] = back_dir;
6646 MovDelay[x][y] = 6 + RND(40);
6648 else if (element == EL_PIG)
6650 boolean can_turn_left = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
6651 boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
6652 boolean can_move_on = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
6653 boolean should_turn_left, should_turn_right, should_move_on;
6655 int rnd = RND(rnd_value);
6657 should_turn_left = (can_turn_left &&
6659 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
6660 y + back_dy + left_dy)));
6661 should_turn_right = (can_turn_right &&
6663 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
6664 y + back_dy + right_dy)));
6665 should_move_on = (can_move_on &&
6668 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
6669 y + move_dy + left_dy) ||
6670 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
6671 y + move_dy + right_dy)));
6673 if (should_turn_left || should_turn_right || should_move_on)
6675 if (should_turn_left && should_turn_right && should_move_on)
6676 MovDir[x][y] = (rnd < rnd_value / 3 ? left_dir :
6677 rnd < 2 * rnd_value / 3 ? right_dir :
6679 else if (should_turn_left && should_turn_right)
6680 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6681 else if (should_turn_left && should_move_on)
6682 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
6683 else if (should_turn_right && should_move_on)
6684 MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
6685 else if (should_turn_left)
6686 MovDir[x][y] = left_dir;
6687 else if (should_turn_right)
6688 MovDir[x][y] = right_dir;
6689 else if (should_move_on)
6690 MovDir[x][y] = old_move_dir;
6692 else if (can_move_on && rnd > rnd_value / 8)
6693 MovDir[x][y] = old_move_dir;
6694 else if (can_turn_left && can_turn_right)
6695 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6696 else if (can_turn_left && rnd > rnd_value / 8)
6697 MovDir[x][y] = left_dir;
6698 else if (can_turn_right && rnd > rnd_value/8)
6699 MovDir[x][y] = right_dir;
6701 MovDir[x][y] = back_dir;
6703 xx = x + move_xy[MovDir[x][y]].dx;
6704 yy = y + move_xy[MovDir[x][y]].dy;
6706 if (!IN_LEV_FIELD(xx, yy) ||
6707 (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy])))
6708 MovDir[x][y] = old_move_dir;
6712 else if (element == EL_DRAGON)
6714 boolean can_turn_left = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
6715 boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
6716 boolean can_move_on = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
6718 int rnd = RND(rnd_value);
6720 if (can_move_on && rnd > rnd_value / 8)
6721 MovDir[x][y] = old_move_dir;
6722 else if (can_turn_left && can_turn_right)
6723 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6724 else if (can_turn_left && rnd > rnd_value / 8)
6725 MovDir[x][y] = left_dir;
6726 else if (can_turn_right && rnd > rnd_value / 8)
6727 MovDir[x][y] = right_dir;
6729 MovDir[x][y] = back_dir;
6731 xx = x + move_xy[MovDir[x][y]].dx;
6732 yy = y + move_xy[MovDir[x][y]].dy;
6734 if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
6735 MovDir[x][y] = old_move_dir;
6739 else if (element == EL_MOLE)
6741 boolean can_move_on =
6742 (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
6743 IS_AMOEBOID(Feld[move_x][move_y]) ||
6744 Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
6747 boolean can_turn_left =
6748 (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
6749 IS_AMOEBOID(Feld[left_x][left_y])));
6751 boolean can_turn_right =
6752 (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
6753 IS_AMOEBOID(Feld[right_x][right_y])));
6755 if (can_turn_left && can_turn_right)
6756 MovDir[x][y] = (RND(2) ? left_dir : right_dir);
6757 else if (can_turn_left)
6758 MovDir[x][y] = left_dir;
6760 MovDir[x][y] = right_dir;
6763 if (MovDir[x][y] != old_move_dir)
6766 else if (element == EL_BALLOON)
6768 MovDir[x][y] = game.wind_direction;
6771 else if (element == EL_SPRING)
6773 if (MovDir[x][y] & MV_HORIZONTAL)
6775 if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
6776 !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
6778 Feld[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
6779 ResetGfxAnimation(move_x, move_y);
6780 TEST_DrawLevelField(move_x, move_y);
6782 MovDir[x][y] = back_dir;
6784 else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
6785 SPRING_CAN_ENTER_FIELD(element, x, y + 1))
6786 MovDir[x][y] = MV_NONE;
6791 else if (element == EL_ROBOT ||
6792 element == EL_SATELLITE ||
6793 element == EL_PENGUIN ||
6794 element == EL_EMC_ANDROID)
6796 int attr_x = -1, attr_y = -1;
6807 for (i = 0; i < MAX_PLAYERS; i++)
6809 struct PlayerInfo *player = &stored_player[i];
6810 int jx = player->jx, jy = player->jy;
6812 if (!player->active)
6816 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
6824 if (element == EL_ROBOT && ZX >= 0 && ZY >= 0 &&
6825 (Feld[ZX][ZY] == EL_ROBOT_WHEEL_ACTIVE ||
6826 game.engine_version < VERSION_IDENT(3,1,0,0)))
6832 if (element == EL_PENGUIN)
6835 static int xy[4][2] =
6843 for (i = 0; i < NUM_DIRECTIONS; i++)
6845 int ex = x + xy[i][0];
6846 int ey = y + xy[i][1];
6848 if (IN_LEV_FIELD(ex, ey) && (Feld[ex][ey] == EL_EXIT_OPEN ||
6849 Feld[ex][ey] == EL_EM_EXIT_OPEN ||
6850 Feld[ex][ey] == EL_STEEL_EXIT_OPEN ||
6851 Feld[ex][ey] == EL_EM_STEEL_EXIT_OPEN))
6860 MovDir[x][y] = MV_NONE;
6862 MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
6863 else if (attr_x > x)
6864 MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
6866 MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
6867 else if (attr_y > y)
6868 MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
6870 if (element == EL_ROBOT)
6874 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6875 MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
6876 Moving2Blocked(x, y, &newx, &newy);
6878 if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
6879 MovDelay[x][y] = 8 + 8 * !RND(3);
6881 MovDelay[x][y] = 16;
6883 else if (element == EL_PENGUIN)
6889 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6891 boolean first_horiz = RND(2);
6892 int new_move_dir = MovDir[x][y];
6895 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6896 Moving2Blocked(x, y, &newx, &newy);
6898 if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
6902 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6903 Moving2Blocked(x, y, &newx, &newy);
6905 if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
6908 MovDir[x][y] = old_move_dir;
6912 else if (element == EL_SATELLITE)
6918 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6920 boolean first_horiz = RND(2);
6921 int new_move_dir = MovDir[x][y];
6924 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6925 Moving2Blocked(x, y, &newx, &newy);
6927 if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
6931 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6932 Moving2Blocked(x, y, &newx, &newy);
6934 if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
6937 MovDir[x][y] = old_move_dir;
6941 else if (element == EL_EMC_ANDROID)
6943 static int check_pos[16] =
6945 -1, /* 0 => (invalid) */
6946 7, /* 1 => MV_LEFT */
6947 3, /* 2 => MV_RIGHT */
6948 -1, /* 3 => (invalid) */
6950 0, /* 5 => MV_LEFT | MV_UP */
6951 2, /* 6 => MV_RIGHT | MV_UP */
6952 -1, /* 7 => (invalid) */
6953 5, /* 8 => MV_DOWN */
6954 6, /* 9 => MV_LEFT | MV_DOWN */
6955 4, /* 10 => MV_RIGHT | MV_DOWN */
6956 -1, /* 11 => (invalid) */
6957 -1, /* 12 => (invalid) */
6958 -1, /* 13 => (invalid) */
6959 -1, /* 14 => (invalid) */
6960 -1, /* 15 => (invalid) */
6968 { -1, -1, MV_LEFT | MV_UP },
6970 { +1, -1, MV_RIGHT | MV_UP },
6971 { +1, 0, MV_RIGHT },
6972 { +1, +1, MV_RIGHT | MV_DOWN },
6974 { -1, +1, MV_LEFT | MV_DOWN },
6977 int start_pos, check_order;
6978 boolean can_clone = FALSE;
6981 /* check if there is any free field around current position */
6982 for (i = 0; i < 8; i++)
6984 int newx = x + check_xy[i].dx;
6985 int newy = y + check_xy[i].dy;
6987 if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
6995 if (can_clone) /* randomly find an element to clone */
6999 start_pos = check_pos[RND(8)];
7000 check_order = (RND(2) ? -1 : +1);
7002 for (i = 0; i < 8; i++)
7004 int pos_raw = start_pos + i * check_order;
7005 int pos = (pos_raw + 8) % 8;
7006 int newx = x + check_xy[pos].dx;
7007 int newy = y + check_xy[pos].dy;
7009 if (ANDROID_CAN_CLONE_FIELD(newx, newy))
7011 element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
7012 element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
7014 Store[x][y] = Feld[newx][newy];
7023 if (can_clone) /* randomly find a direction to move */
7027 start_pos = check_pos[RND(8)];
7028 check_order = (RND(2) ? -1 : +1);
7030 for (i = 0; i < 8; i++)
7032 int pos_raw = start_pos + i * check_order;
7033 int pos = (pos_raw + 8) % 8;
7034 int newx = x + check_xy[pos].dx;
7035 int newy = y + check_xy[pos].dy;
7036 int new_move_dir = check_xy[pos].dir;
7038 if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7040 MovDir[x][y] = new_move_dir;
7041 MovDelay[x][y] = level.android_clone_time * 8 + 1;
7050 if (can_clone) /* cloning and moving successful */
7053 /* cannot clone -- try to move towards player */
7055 start_pos = check_pos[MovDir[x][y] & 0x0f];
7056 check_order = (RND(2) ? -1 : +1);
7058 for (i = 0; i < 3; i++)
7060 /* first check start_pos, then previous/next or (next/previous) pos */
7061 int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
7062 int pos = (pos_raw + 8) % 8;
7063 int newx = x + check_xy[pos].dx;
7064 int newy = y + check_xy[pos].dy;
7065 int new_move_dir = check_xy[pos].dir;
7067 if (IS_PLAYER(newx, newy))
7070 if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7072 MovDir[x][y] = new_move_dir;
7073 MovDelay[x][y] = level.android_move_time * 8 + 1;
7080 else if (move_pattern == MV_TURNING_LEFT ||
7081 move_pattern == MV_TURNING_RIGHT ||
7082 move_pattern == MV_TURNING_LEFT_RIGHT ||
7083 move_pattern == MV_TURNING_RIGHT_LEFT ||
7084 move_pattern == MV_TURNING_RANDOM ||
7085 move_pattern == MV_ALL_DIRECTIONS)
7087 boolean can_turn_left =
7088 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
7089 boolean can_turn_right =
7090 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
7092 if (element_info[element].move_stepsize == 0) /* "not moving" */
7095 if (move_pattern == MV_TURNING_LEFT)
7096 MovDir[x][y] = left_dir;
7097 else if (move_pattern == MV_TURNING_RIGHT)
7098 MovDir[x][y] = right_dir;
7099 else if (move_pattern == MV_TURNING_LEFT_RIGHT)
7100 MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
7101 else if (move_pattern == MV_TURNING_RIGHT_LEFT)
7102 MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
7103 else if (move_pattern == MV_TURNING_RANDOM)
7104 MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
7105 can_turn_right && !can_turn_left ? right_dir :
7106 RND(2) ? left_dir : right_dir);
7107 else if (can_turn_left && can_turn_right)
7108 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7109 else if (can_turn_left)
7110 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7111 else if (can_turn_right)
7112 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7114 MovDir[x][y] = back_dir;
7116 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7118 else if (move_pattern == MV_HORIZONTAL ||
7119 move_pattern == MV_VERTICAL)
7121 if (move_pattern & old_move_dir)
7122 MovDir[x][y] = back_dir;
7123 else if (move_pattern == MV_HORIZONTAL)
7124 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
7125 else if (move_pattern == MV_VERTICAL)
7126 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
7128 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7130 else if (move_pattern & MV_ANY_DIRECTION)
7132 MovDir[x][y] = move_pattern;
7133 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7135 else if (move_pattern & MV_WIND_DIRECTION)
7137 MovDir[x][y] = game.wind_direction;
7138 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7140 else if (move_pattern == MV_ALONG_LEFT_SIDE)
7142 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
7143 MovDir[x][y] = left_dir;
7144 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7145 MovDir[x][y] = right_dir;
7147 if (MovDir[x][y] != old_move_dir)
7148 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7150 else if (move_pattern == MV_ALONG_RIGHT_SIDE)
7152 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
7153 MovDir[x][y] = right_dir;
7154 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7155 MovDir[x][y] = left_dir;
7157 if (MovDir[x][y] != old_move_dir)
7158 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7160 else if (move_pattern == MV_TOWARDS_PLAYER ||
7161 move_pattern == MV_AWAY_FROM_PLAYER)
7163 int attr_x = -1, attr_y = -1;
7165 boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
7176 for (i = 0; i < MAX_PLAYERS; i++)
7178 struct PlayerInfo *player = &stored_player[i];
7179 int jx = player->jx, jy = player->jy;
7181 if (!player->active)
7185 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7193 MovDir[x][y] = MV_NONE;
7195 MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
7196 else if (attr_x > x)
7197 MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
7199 MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
7200 else if (attr_y > y)
7201 MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
7203 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7205 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7207 boolean first_horiz = RND(2);
7208 int new_move_dir = MovDir[x][y];
7210 if (element_info[element].move_stepsize == 0) /* "not moving" */
7212 first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
7213 MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7219 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7220 Moving2Blocked(x, y, &newx, &newy);
7222 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7226 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7227 Moving2Blocked(x, y, &newx, &newy);
7229 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7232 MovDir[x][y] = old_move_dir;
7235 else if (move_pattern == MV_WHEN_PUSHED ||
7236 move_pattern == MV_WHEN_DROPPED)
7238 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7239 MovDir[x][y] = MV_NONE;
7243 else if (move_pattern & MV_MAZE_RUNNER_STYLE)
7245 static int test_xy[7][2] =
7255 static int test_dir[7] =
7265 boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
7266 int move_preference = -1000000; /* start with very low preference */
7267 int new_move_dir = MV_NONE;
7268 int start_test = RND(4);
7271 for (i = 0; i < NUM_DIRECTIONS; i++)
7273 int move_dir = test_dir[start_test + i];
7274 int move_dir_preference;
7276 xx = x + test_xy[start_test + i][0];
7277 yy = y + test_xy[start_test + i][1];
7279 if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
7280 (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
7282 new_move_dir = move_dir;
7287 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
7290 move_dir_preference = -1 * RunnerVisit[xx][yy];
7291 if (hunter_mode && PlayerVisit[xx][yy] > 0)
7292 move_dir_preference = PlayerVisit[xx][yy];
7294 if (move_dir_preference > move_preference)
7296 /* prefer field that has not been visited for the longest time */
7297 move_preference = move_dir_preference;
7298 new_move_dir = move_dir;
7300 else if (move_dir_preference == move_preference &&
7301 move_dir == old_move_dir)
7303 /* prefer last direction when all directions are preferred equally */
7304 move_preference = move_dir_preference;
7305 new_move_dir = move_dir;
7309 MovDir[x][y] = new_move_dir;
7310 if (old_move_dir != new_move_dir)
7311 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7315 static void TurnRound(int x, int y)
7317 int direction = MovDir[x][y];
7321 GfxDir[x][y] = MovDir[x][y];
7323 if (direction != MovDir[x][y])
7327 GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
7329 ResetGfxFrame(x, y);
7332 static boolean JustBeingPushed(int x, int y)
7336 for (i = 0; i < MAX_PLAYERS; i++)
7338 struct PlayerInfo *player = &stored_player[i];
7340 if (player->active && player->is_pushing && player->MovPos)
7342 int next_jx = player->jx + (player->jx - player->last_jx);
7343 int next_jy = player->jy + (player->jy - player->last_jy);
7345 if (x == next_jx && y == next_jy)
7353 void StartMoving(int x, int y)
7355 boolean started_moving = FALSE; /* some elements can fall _and_ move */
7356 int element = Feld[x][y];
7361 if (MovDelay[x][y] == 0)
7362 GfxAction[x][y] = ACTION_DEFAULT;
7364 if (CAN_FALL(element) && y < lev_fieldy - 1)
7366 if ((x > 0 && IS_PLAYER(x - 1, y)) ||
7367 (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
7368 if (JustBeingPushed(x, y))
7371 if (element == EL_QUICKSAND_FULL)
7373 if (IS_FREE(x, y + 1))
7375 InitMovingField(x, y, MV_DOWN);
7376 started_moving = TRUE;
7378 Feld[x][y] = EL_QUICKSAND_EMPTYING;
7379 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7380 if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7381 Store[x][y] = EL_ROCK;
7383 Store[x][y] = EL_ROCK;
7386 PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7388 else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7390 if (!MovDelay[x][y])
7392 MovDelay[x][y] = TILEY + 1;
7394 ResetGfxAnimation(x, y);
7395 ResetGfxAnimation(x, y + 1);
7400 DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7401 DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7408 Feld[x][y] = EL_QUICKSAND_EMPTY;
7409 Feld[x][y + 1] = EL_QUICKSAND_FULL;
7410 Store[x][y + 1] = Store[x][y];
7413 PlayLevelSoundAction(x, y, ACTION_FILLING);
7415 else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7417 if (!MovDelay[x][y])
7419 MovDelay[x][y] = TILEY + 1;
7421 ResetGfxAnimation(x, y);
7422 ResetGfxAnimation(x, y + 1);
7427 DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7428 DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7435 Feld[x][y] = EL_QUICKSAND_EMPTY;
7436 Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7437 Store[x][y + 1] = Store[x][y];
7440 PlayLevelSoundAction(x, y, ACTION_FILLING);
7443 else if (element == EL_QUICKSAND_FAST_FULL)
7445 if (IS_FREE(x, y + 1))
7447 InitMovingField(x, y, MV_DOWN);
7448 started_moving = TRUE;
7450 Feld[x][y] = EL_QUICKSAND_FAST_EMPTYING;
7451 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7452 if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7453 Store[x][y] = EL_ROCK;
7455 Store[x][y] = EL_ROCK;
7458 PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7460 else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7462 if (!MovDelay[x][y])
7464 MovDelay[x][y] = TILEY + 1;
7466 ResetGfxAnimation(x, y);
7467 ResetGfxAnimation(x, y + 1);
7472 DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7473 DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7480 Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
7481 Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7482 Store[x][y + 1] = Store[x][y];
7485 PlayLevelSoundAction(x, y, ACTION_FILLING);
7487 else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7489 if (!MovDelay[x][y])
7491 MovDelay[x][y] = TILEY + 1;
7493 ResetGfxAnimation(x, y);
7494 ResetGfxAnimation(x, y + 1);
7499 DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7500 DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7507 Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
7508 Feld[x][y + 1] = EL_QUICKSAND_FULL;
7509 Store[x][y + 1] = Store[x][y];
7512 PlayLevelSoundAction(x, y, ACTION_FILLING);
7515 else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7516 Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7518 InitMovingField(x, y, MV_DOWN);
7519 started_moving = TRUE;
7521 Feld[x][y] = EL_QUICKSAND_FILLING;
7522 Store[x][y] = element;
7524 PlayLevelSoundAction(x, y, ACTION_FILLING);
7526 else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7527 Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7529 InitMovingField(x, y, MV_DOWN);
7530 started_moving = TRUE;
7532 Feld[x][y] = EL_QUICKSAND_FAST_FILLING;
7533 Store[x][y] = element;
7535 PlayLevelSoundAction(x, y, ACTION_FILLING);
7537 else if (element == EL_MAGIC_WALL_FULL)
7539 if (IS_FREE(x, y + 1))
7541 InitMovingField(x, y, MV_DOWN);
7542 started_moving = TRUE;
7544 Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
7545 Store[x][y] = EL_CHANGED(Store[x][y]);
7547 else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
7549 if (!MovDelay[x][y])
7550 MovDelay[x][y] = TILEY / 4 + 1;
7559 Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
7560 Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
7561 Store[x][y + 1] = EL_CHANGED(Store[x][y]);
7565 else if (element == EL_BD_MAGIC_WALL_FULL)
7567 if (IS_FREE(x, y + 1))
7569 InitMovingField(x, y, MV_DOWN);
7570 started_moving = TRUE;
7572 Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
7573 Store[x][y] = EL_CHANGED_BD(Store[x][y]);
7575 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
7577 if (!MovDelay[x][y])
7578 MovDelay[x][y] = TILEY / 4 + 1;
7587 Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
7588 Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
7589 Store[x][y + 1] = EL_CHANGED_BD(Store[x][y]);
7593 else if (element == EL_DC_MAGIC_WALL_FULL)
7595 if (IS_FREE(x, y + 1))
7597 InitMovingField(x, y, MV_DOWN);
7598 started_moving = TRUE;
7600 Feld[x][y] = EL_DC_MAGIC_WALL_EMPTYING;
7601 Store[x][y] = EL_CHANGED_DC(Store[x][y]);
7603 else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
7605 if (!MovDelay[x][y])
7606 MovDelay[x][y] = TILEY / 4 + 1;
7615 Feld[x][y] = EL_DC_MAGIC_WALL_ACTIVE;
7616 Feld[x][y + 1] = EL_DC_MAGIC_WALL_FULL;
7617 Store[x][y + 1] = EL_CHANGED_DC(Store[x][y]);
7621 else if ((CAN_PASS_MAGIC_WALL(element) &&
7622 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
7623 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)) ||
7624 (CAN_PASS_DC_MAGIC_WALL(element) &&
7625 (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)))
7628 InitMovingField(x, y, MV_DOWN);
7629 started_moving = TRUE;
7632 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
7633 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ? EL_BD_MAGIC_WALL_FILLING :
7634 EL_DC_MAGIC_WALL_FILLING);
7635 Store[x][y] = element;
7637 else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
7639 SplashAcid(x, y + 1);
7641 InitMovingField(x, y, MV_DOWN);
7642 started_moving = TRUE;
7644 Store[x][y] = EL_ACID;
7647 (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7648 CheckImpact[x][y] && !IS_FREE(x, y + 1)) ||
7649 (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
7650 CAN_FALL(element) && WasJustFalling[x][y] &&
7651 (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
7653 (game.engine_version < VERSION_IDENT(2,2,0,7) &&
7654 CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
7655 (Feld[x][y + 1] == EL_BLOCKED)))
7657 /* this is needed for a special case not covered by calling "Impact()"
7658 from "ContinueMoving()": if an element moves to a tile directly below
7659 another element which was just falling on that tile (which was empty
7660 in the previous frame), the falling element above would just stop
7661 instead of smashing the element below (in previous version, the above
7662 element was just checked for "moving" instead of "falling", resulting
7663 in incorrect smashes caused by horizontal movement of the above
7664 element; also, the case of the player being the element to smash was
7665 simply not covered here... :-/ ) */
7667 CheckCollision[x][y] = 0;
7668 CheckImpact[x][y] = 0;
7672 else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
7674 if (MovDir[x][y] == MV_NONE)
7676 InitMovingField(x, y, MV_DOWN);
7677 started_moving = TRUE;
7680 else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
7682 if (WasJustFalling[x][y]) /* prevent animation from being restarted */
7683 MovDir[x][y] = MV_DOWN;
7685 InitMovingField(x, y, MV_DOWN);
7686 started_moving = TRUE;
7688 else if (element == EL_AMOEBA_DROP)
7690 Feld[x][y] = EL_AMOEBA_GROWING;
7691 Store[x][y] = EL_AMOEBA_WET;
7693 else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
7694 (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
7695 !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
7696 element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
7698 boolean can_fall_left = (x > 0 && IS_FREE(x - 1, y) &&
7699 (IS_FREE(x - 1, y + 1) ||
7700 Feld[x - 1][y + 1] == EL_ACID));
7701 boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
7702 (IS_FREE(x + 1, y + 1) ||
7703 Feld[x + 1][y + 1] == EL_ACID));
7704 boolean can_fall_any = (can_fall_left || can_fall_right);
7705 boolean can_fall_both = (can_fall_left && can_fall_right);
7706 int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
7708 if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
7710 if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
7711 can_fall_right = FALSE;
7712 else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
7713 can_fall_left = FALSE;
7714 else if (slippery_type == SLIPPERY_ONLY_LEFT)
7715 can_fall_right = FALSE;
7716 else if (slippery_type == SLIPPERY_ONLY_RIGHT)
7717 can_fall_left = FALSE;
7719 can_fall_any = (can_fall_left || can_fall_right);
7720 can_fall_both = FALSE;
7725 if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
7726 can_fall_right = FALSE; /* slip down on left side */
7728 can_fall_left = !(can_fall_right = RND(2));
7730 can_fall_both = FALSE;
7735 /* if not determined otherwise, prefer left side for slipping down */
7736 InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
7737 started_moving = TRUE;
7740 else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
7742 boolean left_is_free = (x > 0 && IS_FREE(x - 1, y));
7743 boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
7744 int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
7745 int belt_dir = game.belt_dir[belt_nr];
7747 if ((belt_dir == MV_LEFT && left_is_free) ||
7748 (belt_dir == MV_RIGHT && right_is_free))
7750 int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
7752 InitMovingField(x, y, belt_dir);
7753 started_moving = TRUE;
7755 Pushed[x][y] = TRUE;
7756 Pushed[nextx][y] = TRUE;
7758 GfxAction[x][y] = ACTION_DEFAULT;
7762 MovDir[x][y] = 0; /* if element was moving, stop it */
7767 /* not "else if" because of elements that can fall and move (EL_SPRING) */
7768 if (CAN_MOVE(element) && !started_moving)
7770 int move_pattern = element_info[element].move_pattern;
7773 Moving2Blocked(x, y, &newx, &newy);
7775 if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
7778 if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7779 CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7781 WasJustMoving[x][y] = 0;
7782 CheckCollision[x][y] = 0;
7784 TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
7786 if (Feld[x][y] != element) /* element has changed */
7790 if (!MovDelay[x][y]) /* start new movement phase */
7792 /* all objects that can change their move direction after each step
7793 (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
7795 if (element != EL_YAMYAM &&
7796 element != EL_DARK_YAMYAM &&
7797 element != EL_PACMAN &&
7798 !(move_pattern & MV_ANY_DIRECTION) &&
7799 move_pattern != MV_TURNING_LEFT &&
7800 move_pattern != MV_TURNING_RIGHT &&
7801 move_pattern != MV_TURNING_LEFT_RIGHT &&
7802 move_pattern != MV_TURNING_RIGHT_LEFT &&
7803 move_pattern != MV_TURNING_RANDOM)
7807 if (MovDelay[x][y] && (element == EL_BUG ||
7808 element == EL_SPACESHIP ||
7809 element == EL_SP_SNIKSNAK ||
7810 element == EL_SP_ELECTRON ||
7811 element == EL_MOLE))
7812 TEST_DrawLevelField(x, y);
7816 if (MovDelay[x][y]) /* wait some time before next movement */
7820 if (element == EL_ROBOT ||
7821 element == EL_YAMYAM ||
7822 element == EL_DARK_YAMYAM)
7824 DrawLevelElementAnimationIfNeeded(x, y, element);
7825 PlayLevelSoundAction(x, y, ACTION_WAITING);
7827 else if (element == EL_SP_ELECTRON)
7828 DrawLevelElementAnimationIfNeeded(x, y, element);
7829 else if (element == EL_DRAGON)
7832 int dir = MovDir[x][y];
7833 int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
7834 int dy = (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
7835 int graphic = (dir == MV_LEFT ? IMG_FLAMES_1_LEFT :
7836 dir == MV_RIGHT ? IMG_FLAMES_1_RIGHT :
7837 dir == MV_UP ? IMG_FLAMES_1_UP :
7838 dir == MV_DOWN ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
7839 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
7841 GfxAction[x][y] = ACTION_ATTACKING;
7843 if (IS_PLAYER(x, y))
7844 DrawPlayerField(x, y);
7846 TEST_DrawLevelField(x, y);
7848 PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
7850 for (i = 1; i <= 3; i++)
7852 int xx = x + i * dx;
7853 int yy = y + i * dy;
7854 int sx = SCREENX(xx);
7855 int sy = SCREENY(yy);
7856 int flame_graphic = graphic + (i - 1);
7858 if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
7863 int flamed = MovingOrBlocked2Element(xx, yy);
7865 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
7868 RemoveMovingField(xx, yy);
7870 ChangeDelay[xx][yy] = 0;
7872 Feld[xx][yy] = EL_FLAMES;
7874 if (IN_SCR_FIELD(sx, sy))
7876 TEST_DrawLevelFieldCrumbled(xx, yy);
7877 DrawGraphic(sx, sy, flame_graphic, frame);
7882 if (Feld[xx][yy] == EL_FLAMES)
7883 Feld[xx][yy] = EL_EMPTY;
7884 TEST_DrawLevelField(xx, yy);
7889 if (MovDelay[x][y]) /* element still has to wait some time */
7891 PlayLevelSoundAction(x, y, ACTION_WAITING);
7897 /* now make next step */
7899 Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
7901 if (DONT_COLLIDE_WITH(element) &&
7902 IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
7903 !PLAYER_ENEMY_PROTECTED(newx, newy))
7905 TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
7910 else if (CAN_MOVE_INTO_ACID(element) &&
7911 IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID &&
7912 !IS_MV_DIAGONAL(MovDir[x][y]) &&
7913 (MovDir[x][y] == MV_DOWN ||
7914 game.engine_version >= VERSION_IDENT(3,1,0,0)))
7916 SplashAcid(newx, newy);
7917 Store[x][y] = EL_ACID;
7919 else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
7921 if (Feld[newx][newy] == EL_EXIT_OPEN ||
7922 Feld[newx][newy] == EL_EM_EXIT_OPEN ||
7923 Feld[newx][newy] == EL_STEEL_EXIT_OPEN ||
7924 Feld[newx][newy] == EL_EM_STEEL_EXIT_OPEN)
7927 TEST_DrawLevelField(x, y);
7929 PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
7930 if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
7931 DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
7933 local_player->friends_still_needed--;
7934 if (!local_player->friends_still_needed &&
7935 !local_player->GameOver && AllPlayersGone)
7936 PlayerWins(local_player);
7940 else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
7942 if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MP_MOVING)
7943 TEST_DrawLevelField(newx, newy);
7945 GfxDir[x][y] = MovDir[x][y] = MV_NONE;
7947 else if (!IS_FREE(newx, newy))
7949 GfxAction[x][y] = ACTION_WAITING;
7951 if (IS_PLAYER(x, y))
7952 DrawPlayerField(x, y);
7954 TEST_DrawLevelField(x, y);
7959 else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
7961 if (IS_FOOD_PIG(Feld[newx][newy]))
7963 if (IS_MOVING(newx, newy))
7964 RemoveMovingField(newx, newy);
7967 Feld[newx][newy] = EL_EMPTY;
7968 TEST_DrawLevelField(newx, newy);
7971 PlayLevelSound(x, y, SND_PIG_DIGGING);
7973 else if (!IS_FREE(newx, newy))
7975 if (IS_PLAYER(x, y))
7976 DrawPlayerField(x, y);
7978 TEST_DrawLevelField(x, y);
7983 else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
7985 if (Store[x][y] != EL_EMPTY)
7987 boolean can_clone = FALSE;
7990 /* check if element to clone is still there */
7991 for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
7993 if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == Store[x][y])
8001 /* cannot clone or target field not free anymore -- do not clone */
8002 if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8003 Store[x][y] = EL_EMPTY;
8006 if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8008 if (IS_MV_DIAGONAL(MovDir[x][y]))
8010 int diagonal_move_dir = MovDir[x][y];
8011 int stored = Store[x][y];
8012 int change_delay = 8;
8015 /* android is moving diagonally */
8017 CreateField(x, y, EL_DIAGONAL_SHRINKING);
8019 Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
8020 GfxElement[x][y] = EL_EMC_ANDROID;
8021 GfxAction[x][y] = ACTION_SHRINKING;
8022 GfxDir[x][y] = diagonal_move_dir;
8023 ChangeDelay[x][y] = change_delay;
8025 graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
8028 DrawLevelGraphicAnimation(x, y, graphic);
8029 PlayLevelSoundAction(x, y, ACTION_SHRINKING);
8031 if (Feld[newx][newy] == EL_ACID)
8033 SplashAcid(newx, newy);
8038 CreateField(newx, newy, EL_DIAGONAL_GROWING);
8040 Store[newx][newy] = EL_EMC_ANDROID;
8041 GfxElement[newx][newy] = EL_EMC_ANDROID;
8042 GfxAction[newx][newy] = ACTION_GROWING;
8043 GfxDir[newx][newy] = diagonal_move_dir;
8044 ChangeDelay[newx][newy] = change_delay;
8046 graphic = el_act_dir2img(GfxElement[newx][newy],
8047 GfxAction[newx][newy], GfxDir[newx][newy]);
8049 DrawLevelGraphicAnimation(newx, newy, graphic);
8050 PlayLevelSoundAction(newx, newy, ACTION_GROWING);
8056 Feld[newx][newy] = EL_EMPTY;
8057 TEST_DrawLevelField(newx, newy);
8059 PlayLevelSoundAction(x, y, ACTION_DIGGING);
8062 else if (!IS_FREE(newx, newy))
8067 else if (IS_CUSTOM_ELEMENT(element) &&
8068 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
8070 if (!DigFieldByCE(newx, newy, element))
8073 if (move_pattern & MV_MAZE_RUNNER_STYLE)
8075 RunnerVisit[x][y] = FrameCounter;
8076 PlayerVisit[x][y] /= 8; /* expire player visit path */
8079 else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
8081 if (!IS_FREE(newx, newy))
8083 if (IS_PLAYER(x, y))
8084 DrawPlayerField(x, y);
8086 TEST_DrawLevelField(x, y);
8092 boolean wanna_flame = !RND(10);
8093 int dx = newx - x, dy = newy - y;
8094 int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
8095 int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
8096 int element1 = (IN_LEV_FIELD(newx1, newy1) ?
8097 MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
8098 int element2 = (IN_LEV_FIELD(newx2, newy2) ?
8099 MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
8102 IS_CLASSIC_ENEMY(element1) ||
8103 IS_CLASSIC_ENEMY(element2)) &&
8104 element1 != EL_DRAGON && element2 != EL_DRAGON &&
8105 element1 != EL_FLAMES && element2 != EL_FLAMES)
8107 ResetGfxAnimation(x, y);
8108 GfxAction[x][y] = ACTION_ATTACKING;
8110 if (IS_PLAYER(x, y))
8111 DrawPlayerField(x, y);
8113 TEST_DrawLevelField(x, y);
8115 PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
8117 MovDelay[x][y] = 50;
8119 Feld[newx][newy] = EL_FLAMES;
8120 if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
8121 Feld[newx1][newy1] = EL_FLAMES;
8122 if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
8123 Feld[newx2][newy2] = EL_FLAMES;
8129 else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8130 Feld[newx][newy] == EL_DIAMOND)
8132 if (IS_MOVING(newx, newy))
8133 RemoveMovingField(newx, newy);
8136 Feld[newx][newy] = EL_EMPTY;
8137 TEST_DrawLevelField(newx, newy);
8140 PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
8142 else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8143 IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
8145 if (AmoebaNr[newx][newy])
8147 AmoebaCnt2[AmoebaNr[newx][newy]]--;
8148 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
8149 Feld[newx][newy] == EL_BD_AMOEBA)
8150 AmoebaCnt[AmoebaNr[newx][newy]]--;
8153 if (IS_MOVING(newx, newy))
8155 RemoveMovingField(newx, newy);
8159 Feld[newx][newy] = EL_EMPTY;
8160 TEST_DrawLevelField(newx, newy);
8163 PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
8165 else if ((element == EL_PACMAN || element == EL_MOLE)
8166 && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
8168 if (AmoebaNr[newx][newy])
8170 AmoebaCnt2[AmoebaNr[newx][newy]]--;
8171 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
8172 Feld[newx][newy] == EL_BD_AMOEBA)
8173 AmoebaCnt[AmoebaNr[newx][newy]]--;
8176 if (element == EL_MOLE)
8178 Feld[newx][newy] = EL_AMOEBA_SHRINKING;
8179 PlayLevelSound(x, y, SND_MOLE_DIGGING);
8181 ResetGfxAnimation(x, y);
8182 GfxAction[x][y] = ACTION_DIGGING;
8183 TEST_DrawLevelField(x, y);
8185 MovDelay[newx][newy] = 0; /* start amoeba shrinking delay */
8187 return; /* wait for shrinking amoeba */
8189 else /* element == EL_PACMAN */
8191 Feld[newx][newy] = EL_EMPTY;
8192 TEST_DrawLevelField(newx, newy);
8193 PlayLevelSound(x, y, SND_PACMAN_DIGGING);
8196 else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
8197 (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
8198 (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
8200 /* wait for shrinking amoeba to completely disappear */
8203 else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
8205 /* object was running against a wall */
8209 if (GFX_ELEMENT(element) != EL_SAND) /* !!! FIX THIS (crumble) !!! */
8210 DrawLevelElementAnimation(x, y, element);
8212 if (DONT_TOUCH(element))
8213 TestIfBadThingTouchesPlayer(x, y);
8218 InitMovingField(x, y, MovDir[x][y]);
8220 PlayLevelSoundAction(x, y, ACTION_MOVING);
8224 ContinueMoving(x, y);
8227 void ContinueMoving(int x, int y)
8229 int element = Feld[x][y];
8230 struct ElementInfo *ei = &element_info[element];
8231 int direction = MovDir[x][y];
8232 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
8233 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
8234 int newx = x + dx, newy = y + dy;
8235 int stored = Store[x][y];
8236 int stored_new = Store[newx][newy];
8237 boolean pushed_by_player = (Pushed[x][y] && IS_PLAYER(x, y));
8238 boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
8239 boolean last_line = (newy == lev_fieldy - 1);
8241 MovPos[x][y] += getElementMoveStepsize(x, y);
8243 if (pushed_by_player) /* special case: moving object pushed by player */
8244 MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
8246 if (ABS(MovPos[x][y]) < TILEX)
8248 TEST_DrawLevelField(x, y);
8250 return; /* element is still moving */
8253 /* element reached destination field */
8255 Feld[x][y] = EL_EMPTY;
8256 Feld[newx][newy] = element;
8257 MovPos[x][y] = 0; /* force "not moving" for "crumbled sand" */
8259 if (Store[x][y] == EL_ACID) /* element is moving into acid pool */
8261 element = Feld[newx][newy] = EL_ACID;
8263 else if (element == EL_MOLE)
8265 Feld[x][y] = EL_SAND;
8267 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8269 else if (element == EL_QUICKSAND_FILLING)
8271 element = Feld[newx][newy] = get_next_element(element);
8272 Store[newx][newy] = Store[x][y];
8274 else if (element == EL_QUICKSAND_EMPTYING)
8276 Feld[x][y] = get_next_element(element);
8277 element = Feld[newx][newy] = Store[x][y];
8279 else if (element == EL_QUICKSAND_FAST_FILLING)
8281 element = Feld[newx][newy] = get_next_element(element);
8282 Store[newx][newy] = Store[x][y];
8284 else if (element == EL_QUICKSAND_FAST_EMPTYING)
8286 Feld[x][y] = get_next_element(element);
8287 element = Feld[newx][newy] = Store[x][y];
8289 else if (element == EL_MAGIC_WALL_FILLING)
8291 element = Feld[newx][newy] = get_next_element(element);
8292 if (!game.magic_wall_active)
8293 element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
8294 Store[newx][newy] = Store[x][y];
8296 else if (element == EL_MAGIC_WALL_EMPTYING)
8298 Feld[x][y] = get_next_element(element);
8299 if (!game.magic_wall_active)
8300 Feld[x][y] = EL_MAGIC_WALL_DEAD;
8301 element = Feld[newx][newy] = Store[x][y];
8303 InitField(newx, newy, FALSE);
8305 else if (element == EL_BD_MAGIC_WALL_FILLING)
8307 element = Feld[newx][newy] = get_next_element(element);
8308 if (!game.magic_wall_active)
8309 element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
8310 Store[newx][newy] = Store[x][y];
8312 else if (element == EL_BD_MAGIC_WALL_EMPTYING)
8314 Feld[x][y] = get_next_element(element);
8315 if (!game.magic_wall_active)
8316 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
8317 element = Feld[newx][newy] = Store[x][y];
8319 InitField(newx, newy, FALSE);
8321 else if (element == EL_DC_MAGIC_WALL_FILLING)
8323 element = Feld[newx][newy] = get_next_element(element);
8324 if (!game.magic_wall_active)
8325 element = Feld[newx][newy] = EL_DC_MAGIC_WALL_DEAD;
8326 Store[newx][newy] = Store[x][y];
8328 else if (element == EL_DC_MAGIC_WALL_EMPTYING)
8330 Feld[x][y] = get_next_element(element);
8331 if (!game.magic_wall_active)
8332 Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
8333 element = Feld[newx][newy] = Store[x][y];
8335 InitField(newx, newy, FALSE);
8337 else if (element == EL_AMOEBA_DROPPING)
8339 Feld[x][y] = get_next_element(element);
8340 element = Feld[newx][newy] = Store[x][y];
8342 else if (element == EL_SOKOBAN_OBJECT)
8345 Feld[x][y] = Back[x][y];
8347 if (Back[newx][newy])
8348 Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
8350 Back[x][y] = Back[newx][newy] = 0;
8353 Store[x][y] = EL_EMPTY;
8358 MovDelay[newx][newy] = 0;
8360 if (CAN_CHANGE_OR_HAS_ACTION(element))
8362 /* copy element change control values to new field */
8363 ChangeDelay[newx][newy] = ChangeDelay[x][y];
8364 ChangePage[newx][newy] = ChangePage[x][y];
8365 ChangeCount[newx][newy] = ChangeCount[x][y];
8366 ChangeEvent[newx][newy] = ChangeEvent[x][y];
8369 CustomValue[newx][newy] = CustomValue[x][y];
8371 ChangeDelay[x][y] = 0;
8372 ChangePage[x][y] = -1;
8373 ChangeCount[x][y] = 0;
8374 ChangeEvent[x][y] = -1;
8376 CustomValue[x][y] = 0;
8378 /* copy animation control values to new field */
8379 GfxFrame[newx][newy] = GfxFrame[x][y];
8380 GfxRandom[newx][newy] = GfxRandom[x][y]; /* keep same random value */
8381 GfxAction[newx][newy] = GfxAction[x][y]; /* keep action one frame */
8382 GfxDir[newx][newy] = GfxDir[x][y]; /* keep element direction */
8384 Pushed[x][y] = Pushed[newx][newy] = FALSE;
8386 /* some elements can leave other elements behind after moving */
8387 if (ei->move_leave_element != EL_EMPTY &&
8388 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
8389 (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
8391 int move_leave_element = ei->move_leave_element;
8393 /* this makes it possible to leave the removed element again */
8394 if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
8395 move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
8397 Feld[x][y] = move_leave_element;
8399 if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
8400 MovDir[x][y] = direction;
8402 InitField(x, y, FALSE);
8404 if (GFX_CRUMBLED(Feld[x][y]))
8405 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8407 if (ELEM_IS_PLAYER(move_leave_element))
8408 RelocatePlayer(x, y, move_leave_element);
8411 /* do this after checking for left-behind element */
8412 ResetGfxAnimation(x, y); /* reset animation values for old field */
8414 if (!CAN_MOVE(element) ||
8415 (CAN_FALL(element) && direction == MV_DOWN &&
8416 (element == EL_SPRING ||
8417 element_info[element].move_pattern == MV_WHEN_PUSHED ||
8418 element_info[element].move_pattern == MV_WHEN_DROPPED)))
8419 GfxDir[x][y] = MovDir[newx][newy] = 0;
8421 TEST_DrawLevelField(x, y);
8422 TEST_DrawLevelField(newx, newy);
8424 Stop[newx][newy] = TRUE; /* ignore this element until the next frame */
8426 /* prevent pushed element from moving on in pushed direction */
8427 if (pushed_by_player && CAN_MOVE(element) &&
8428 element_info[element].move_pattern & MV_ANY_DIRECTION &&
8429 !(element_info[element].move_pattern & direction))
8430 TurnRound(newx, newy);
8432 /* prevent elements on conveyor belt from moving on in last direction */
8433 if (pushed_by_conveyor && CAN_FALL(element) &&
8434 direction & MV_HORIZONTAL)
8435 MovDir[newx][newy] = 0;
8437 if (!pushed_by_player)
8439 int nextx = newx + dx, nexty = newy + dy;
8440 boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
8442 WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
8444 if (CAN_FALL(element) && direction == MV_DOWN)
8445 WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
8447 if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
8448 CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
8450 if (CAN_FALL(element) && direction == MV_DOWN && check_collision_again)
8451 CheckImpact[newx][newy] = CHECK_DELAY_IMPACT;
8454 if (DONT_TOUCH(element)) /* object may be nasty to player or others */
8456 TestIfBadThingTouchesPlayer(newx, newy);
8457 TestIfBadThingTouchesFriend(newx, newy);
8459 if (!IS_CUSTOM_ELEMENT(element))
8460 TestIfBadThingTouchesOtherBadThing(newx, newy);
8462 else if (element == EL_PENGUIN)
8463 TestIfFriendTouchesBadThing(newx, newy);
8465 if (DONT_GET_HIT_BY(element))
8467 TestIfGoodThingGetsHitByBadThing(newx, newy, direction);
8470 /* give the player one last chance (one more frame) to move away */
8471 if (CAN_FALL(element) && direction == MV_DOWN &&
8472 (last_line || (!IS_FREE(x, newy + 1) &&
8473 (!IS_PLAYER(x, newy + 1) ||
8474 game.engine_version < VERSION_IDENT(3,1,1,0)))))
8477 if (pushed_by_player && !game.use_change_when_pushing_bug)
8479 int push_side = MV_DIR_OPPOSITE(direction);
8480 struct PlayerInfo *player = PLAYERINFO(x, y);
8482 CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
8483 player->index_bit, push_side);
8484 CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
8485 player->index_bit, push_side);
8488 if (element == EL_EMC_ANDROID && pushed_by_player) /* make another move */
8489 MovDelay[newx][newy] = 1;
8491 CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
8493 TestIfElementTouchesCustomElement(x, y); /* empty or new element */
8494 TestIfElementHitsCustomElement(newx, newy, direction);
8495 TestIfPlayerTouchesCustomElement(newx, newy);
8496 TestIfElementTouchesCustomElement(newx, newy);
8498 if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
8499 IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
8500 CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
8501 MV_DIR_OPPOSITE(direction));
8504 int AmoebeNachbarNr(int ax, int ay)
8507 int element = Feld[ax][ay];
8509 static int xy[4][2] =
8517 for (i = 0; i < NUM_DIRECTIONS; i++)
8519 int x = ax + xy[i][0];
8520 int y = ay + xy[i][1];
8522 if (!IN_LEV_FIELD(x, y))
8525 if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
8526 group_nr = AmoebaNr[x][y];
8532 void AmoebenVereinigen(int ax, int ay)
8534 int i, x, y, xx, yy;
8535 int new_group_nr = AmoebaNr[ax][ay];
8536 static int xy[4][2] =
8544 if (new_group_nr == 0)
8547 for (i = 0; i < NUM_DIRECTIONS; i++)
8552 if (!IN_LEV_FIELD(x, y))
8555 if ((Feld[x][y] == EL_AMOEBA_FULL ||
8556 Feld[x][y] == EL_BD_AMOEBA ||
8557 Feld[x][y] == EL_AMOEBA_DEAD) &&
8558 AmoebaNr[x][y] != new_group_nr)
8560 int old_group_nr = AmoebaNr[x][y];
8562 if (old_group_nr == 0)
8565 AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
8566 AmoebaCnt[old_group_nr] = 0;
8567 AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
8568 AmoebaCnt2[old_group_nr] = 0;
8570 SCAN_PLAYFIELD(xx, yy)
8572 if (AmoebaNr[xx][yy] == old_group_nr)
8573 AmoebaNr[xx][yy] = new_group_nr;
8579 void AmoebeUmwandeln(int ax, int ay)
8583 if (Feld[ax][ay] == EL_AMOEBA_DEAD)
8585 int group_nr = AmoebaNr[ax][ay];
8590 printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
8591 printf("AmoebeUmwandeln(): This should never happen!\n");
8596 SCAN_PLAYFIELD(x, y)
8598 if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
8601 Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
8605 PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
8606 SND_AMOEBA_TURNING_TO_GEM :
8607 SND_AMOEBA_TURNING_TO_ROCK));
8612 static int xy[4][2] =
8620 for (i = 0; i < NUM_DIRECTIONS; i++)
8625 if (!IN_LEV_FIELD(x, y))
8628 if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
8630 PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
8631 SND_AMOEBA_TURNING_TO_GEM :
8632 SND_AMOEBA_TURNING_TO_ROCK));
8639 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
8642 int group_nr = AmoebaNr[ax][ay];
8643 boolean done = FALSE;
8648 printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
8649 printf("AmoebeUmwandelnBD(): This should never happen!\n");
8654 SCAN_PLAYFIELD(x, y)
8656 if (AmoebaNr[x][y] == group_nr &&
8657 (Feld[x][y] == EL_AMOEBA_DEAD ||
8658 Feld[x][y] == EL_BD_AMOEBA ||
8659 Feld[x][y] == EL_AMOEBA_GROWING))
8662 Feld[x][y] = new_element;
8663 InitField(x, y, FALSE);
8664 TEST_DrawLevelField(x, y);
8670 PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
8671 SND_BD_AMOEBA_TURNING_TO_ROCK :
8672 SND_BD_AMOEBA_TURNING_TO_GEM));
8675 void AmoebeWaechst(int x, int y)
8677 static unsigned int sound_delay = 0;
8678 static unsigned int sound_delay_value = 0;
8680 if (!MovDelay[x][y]) /* start new growing cycle */
8684 if (DelayReached(&sound_delay, sound_delay_value))
8686 PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
8687 sound_delay_value = 30;
8691 if (MovDelay[x][y]) /* wait some time before growing bigger */
8694 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8696 int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
8697 6 - MovDelay[x][y]);
8699 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
8702 if (!MovDelay[x][y])
8704 Feld[x][y] = Store[x][y];
8706 TEST_DrawLevelField(x, y);
8711 void AmoebaDisappearing(int x, int y)
8713 static unsigned int sound_delay = 0;
8714 static unsigned int sound_delay_value = 0;
8716 if (!MovDelay[x][y]) /* start new shrinking cycle */
8720 if (DelayReached(&sound_delay, sound_delay_value))
8721 sound_delay_value = 30;
8724 if (MovDelay[x][y]) /* wait some time before shrinking */
8727 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8729 int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
8730 6 - MovDelay[x][y]);
8732 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
8735 if (!MovDelay[x][y])
8737 Feld[x][y] = EL_EMPTY;
8738 TEST_DrawLevelField(x, y);
8740 /* don't let mole enter this field in this cycle;
8741 (give priority to objects falling to this field from above) */
8747 void AmoebeAbleger(int ax, int ay)
8750 int element = Feld[ax][ay];
8751 int graphic = el2img(element);
8752 int newax = ax, neway = ay;
8753 boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
8754 static int xy[4][2] =
8762 if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
8764 Feld[ax][ay] = EL_AMOEBA_DEAD;
8765 TEST_DrawLevelField(ax, ay);
8769 if (IS_ANIMATED(graphic))
8770 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
8772 if (!MovDelay[ax][ay]) /* start making new amoeba field */
8773 MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
8775 if (MovDelay[ax][ay]) /* wait some time before making new amoeba */
8778 if (MovDelay[ax][ay])
8782 if (can_drop) /* EL_AMOEBA_WET or EL_EMC_DRIPPER */
8785 int x = ax + xy[start][0];
8786 int y = ay + xy[start][1];
8788 if (!IN_LEV_FIELD(x, y))
8791 if (IS_FREE(x, y) ||
8792 CAN_GROW_INTO(Feld[x][y]) ||
8793 Feld[x][y] == EL_QUICKSAND_EMPTY ||
8794 Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
8800 if (newax == ax && neway == ay)
8803 else /* normal or "filled" (BD style) amoeba */
8806 boolean waiting_for_player = FALSE;
8808 for (i = 0; i < NUM_DIRECTIONS; i++)
8810 int j = (start + i) % 4;
8811 int x = ax + xy[j][0];
8812 int y = ay + xy[j][1];
8814 if (!IN_LEV_FIELD(x, y))
8817 if (IS_FREE(x, y) ||
8818 CAN_GROW_INTO(Feld[x][y]) ||
8819 Feld[x][y] == EL_QUICKSAND_EMPTY ||
8820 Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
8826 else if (IS_PLAYER(x, y))
8827 waiting_for_player = TRUE;
8830 if (newax == ax && neway == ay) /* amoeba cannot grow */
8832 if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
8834 Feld[ax][ay] = EL_AMOEBA_DEAD;
8835 TEST_DrawLevelField(ax, ay);
8836 AmoebaCnt[AmoebaNr[ax][ay]]--;
8838 if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0) /* amoeba is completely dead */
8840 if (element == EL_AMOEBA_FULL)
8841 AmoebeUmwandeln(ax, ay);
8842 else if (element == EL_BD_AMOEBA)
8843 AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
8848 else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
8850 /* amoeba gets larger by growing in some direction */
8852 int new_group_nr = AmoebaNr[ax][ay];
8855 if (new_group_nr == 0)
8857 printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
8858 printf("AmoebeAbleger(): This should never happen!\n");
8863 AmoebaNr[newax][neway] = new_group_nr;
8864 AmoebaCnt[new_group_nr]++;
8865 AmoebaCnt2[new_group_nr]++;
8867 /* if amoeba touches other amoeba(s) after growing, unify them */
8868 AmoebenVereinigen(newax, neway);
8870 if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
8872 AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
8878 if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
8879 (neway == lev_fieldy - 1 && newax != ax))
8881 Feld[newax][neway] = EL_AMOEBA_GROWING; /* creation of new amoeba */
8882 Store[newax][neway] = element;
8884 else if (neway == ay || element == EL_EMC_DRIPPER)
8886 Feld[newax][neway] = EL_AMOEBA_DROP; /* drop left/right of amoeba */
8888 PlayLevelSoundAction(newax, neway, ACTION_GROWING);
8892 InitMovingField(ax, ay, MV_DOWN); /* drop dripping from amoeba */
8893 Feld[ax][ay] = EL_AMOEBA_DROPPING;
8894 Store[ax][ay] = EL_AMOEBA_DROP;
8895 ContinueMoving(ax, ay);
8899 TEST_DrawLevelField(newax, neway);
8902 void Life(int ax, int ay)
8906 int element = Feld[ax][ay];
8907 int graphic = el2img(element);
8908 int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
8910 boolean changed = FALSE;
8912 if (IS_ANIMATED(graphic))
8913 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
8918 if (!MovDelay[ax][ay]) /* start new "game of life" cycle */
8919 MovDelay[ax][ay] = life_time;
8921 if (MovDelay[ax][ay]) /* wait some time before next cycle */
8924 if (MovDelay[ax][ay])
8928 for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
8930 int xx = ax+x1, yy = ay+y1;
8933 if (!IN_LEV_FIELD(xx, yy))
8936 for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
8938 int x = xx+x2, y = yy+y2;
8940 if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
8943 if (((Feld[x][y] == element ||
8944 (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
8946 (IS_FREE(x, y) && Stop[x][y]))
8950 if (xx == ax && yy == ay) /* field in the middle */
8952 if (nachbarn < life_parameter[0] ||
8953 nachbarn > life_parameter[1])
8955 Feld[xx][yy] = EL_EMPTY;
8957 TEST_DrawLevelField(xx, yy);
8958 Stop[xx][yy] = TRUE;
8962 else if (IS_FREE(xx, yy) || CAN_GROW_INTO(Feld[xx][yy]))
8963 { /* free border field */
8964 if (nachbarn >= life_parameter[2] &&
8965 nachbarn <= life_parameter[3])
8967 Feld[xx][yy] = element;
8968 MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
8970 TEST_DrawLevelField(xx, yy);
8971 Stop[xx][yy] = TRUE;
8978 PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
8979 SND_GAME_OF_LIFE_GROWING);
8982 static void InitRobotWheel(int x, int y)
8984 ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
8987 static void RunRobotWheel(int x, int y)
8989 PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
8992 static void StopRobotWheel(int x, int y)
8994 if (ZX == x && ZY == y)
8998 game.robot_wheel_active = FALSE;
9002 static void InitTimegateWheel(int x, int y)
9004 ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
9007 static void RunTimegateWheel(int x, int y)
9009 PlayLevelSound(x, y, SND_CLASS_TIMEGATE_SWITCH_ACTIVE);
9012 static void InitMagicBallDelay(int x, int y)
9014 ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
9017 static void ActivateMagicBall(int bx, int by)
9021 if (level.ball_random)
9023 int pos_border = RND(8); /* select one of the eight border elements */
9024 int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
9025 int xx = pos_content % 3;
9026 int yy = pos_content / 3;
9031 if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
9032 CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9036 for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
9038 int xx = x - bx + 1;
9039 int yy = y - by + 1;
9041 if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
9042 CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9046 game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
9049 void CheckExit(int x, int y)
9051 if (local_player->gems_still_needed > 0 ||
9052 local_player->sokobanfields_still_needed > 0 ||
9053 local_player->lights_still_needed > 0)
9055 int element = Feld[x][y];
9056 int graphic = el2img(element);
9058 if (IS_ANIMATED(graphic))
9059 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9064 if (AllPlayersGone) /* do not re-open exit door closed after last player */
9067 Feld[x][y] = EL_EXIT_OPENING;
9069 PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
9072 void CheckExitEM(int x, int y)
9074 if (local_player->gems_still_needed > 0 ||
9075 local_player->sokobanfields_still_needed > 0 ||
9076 local_player->lights_still_needed > 0)
9078 int element = Feld[x][y];
9079 int graphic = el2img(element);
9081 if (IS_ANIMATED(graphic))
9082 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9087 if (AllPlayersGone) /* do not re-open exit door closed after last player */
9090 Feld[x][y] = EL_EM_EXIT_OPENING;
9092 PlayLevelSoundNearest(x, y, SND_CLASS_EM_EXIT_OPENING);
9095 void CheckExitSteel(int x, int y)
9097 if (local_player->gems_still_needed > 0 ||
9098 local_player->sokobanfields_still_needed > 0 ||
9099 local_player->lights_still_needed > 0)
9101 int element = Feld[x][y];
9102 int graphic = el2img(element);
9104 if (IS_ANIMATED(graphic))
9105 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9110 if (AllPlayersGone) /* do not re-open exit door closed after last player */
9113 Feld[x][y] = EL_STEEL_EXIT_OPENING;
9115 PlayLevelSoundNearest(x, y, SND_CLASS_STEEL_EXIT_OPENING);
9118 void CheckExitSteelEM(int x, int y)
9120 if (local_player->gems_still_needed > 0 ||
9121 local_player->sokobanfields_still_needed > 0 ||
9122 local_player->lights_still_needed > 0)
9124 int element = Feld[x][y];
9125 int graphic = el2img(element);
9127 if (IS_ANIMATED(graphic))
9128 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9133 if (AllPlayersGone) /* do not re-open exit door closed after last player */
9136 Feld[x][y] = EL_EM_STEEL_EXIT_OPENING;
9138 PlayLevelSoundNearest(x, y, SND_CLASS_EM_STEEL_EXIT_OPENING);
9141 void CheckExitSP(int x, int y)
9143 if (local_player->gems_still_needed > 0)
9145 int element = Feld[x][y];
9146 int graphic = el2img(element);
9148 if (IS_ANIMATED(graphic))
9149 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9154 if (AllPlayersGone) /* do not re-open exit door closed after last player */
9157 Feld[x][y] = EL_SP_EXIT_OPENING;
9159 PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
9162 static void CloseAllOpenTimegates()
9166 SCAN_PLAYFIELD(x, y)
9168 int element = Feld[x][y];
9170 if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
9172 Feld[x][y] = EL_TIMEGATE_CLOSING;
9174 PlayLevelSoundAction(x, y, ACTION_CLOSING);
9179 void DrawTwinkleOnField(int x, int y)
9181 if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
9184 if (Feld[x][y] == EL_BD_DIAMOND)
9187 if (MovDelay[x][y] == 0) /* next animation frame */
9188 MovDelay[x][y] = 11 * !GetSimpleRandom(500);
9190 if (MovDelay[x][y] != 0) /* wait some time before next frame */
9194 DrawLevelElementAnimation(x, y, Feld[x][y]);
9196 if (MovDelay[x][y] != 0)
9198 int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
9199 10 - MovDelay[x][y]);
9201 DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
9206 void MauerWaechst(int x, int y)
9210 if (!MovDelay[x][y]) /* next animation frame */
9211 MovDelay[x][y] = 3 * delay;
9213 if (MovDelay[x][y]) /* wait some time before next frame */
9217 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9219 int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
9220 int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
9222 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
9225 if (!MovDelay[x][y])
9227 if (MovDir[x][y] == MV_LEFT)
9229 if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
9230 TEST_DrawLevelField(x - 1, y);
9232 else if (MovDir[x][y] == MV_RIGHT)
9234 if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
9235 TEST_DrawLevelField(x + 1, y);
9237 else if (MovDir[x][y] == MV_UP)
9239 if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
9240 TEST_DrawLevelField(x, y - 1);
9244 if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
9245 TEST_DrawLevelField(x, y + 1);
9248 Feld[x][y] = Store[x][y];
9250 GfxDir[x][y] = MovDir[x][y] = MV_NONE;
9251 TEST_DrawLevelField(x, y);
9256 void MauerAbleger(int ax, int ay)
9258 int element = Feld[ax][ay];
9259 int graphic = el2img(element);
9260 boolean oben_frei = FALSE, unten_frei = FALSE;
9261 boolean links_frei = FALSE, rechts_frei = FALSE;
9262 boolean oben_massiv = FALSE, unten_massiv = FALSE;
9263 boolean links_massiv = FALSE, rechts_massiv = FALSE;
9264 boolean new_wall = FALSE;
9266 if (IS_ANIMATED(graphic))
9267 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9269 if (!MovDelay[ax][ay]) /* start building new wall */
9270 MovDelay[ax][ay] = 6;
9272 if (MovDelay[ax][ay]) /* wait some time before building new wall */
9275 if (MovDelay[ax][ay])
9279 if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9281 if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9283 if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9285 if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9288 if (element == EL_EXPANDABLE_WALL_VERTICAL ||
9289 element == EL_EXPANDABLE_WALL_ANY)
9293 Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
9294 Store[ax][ay-1] = element;
9295 GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9296 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9297 DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9298 IMG_EXPANDABLE_WALL_GROWING_UP, 0);
9303 Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
9304 Store[ax][ay+1] = element;
9305 GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9306 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9307 DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9308 IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
9313 if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9314 element == EL_EXPANDABLE_WALL_ANY ||
9315 element == EL_EXPANDABLE_WALL ||
9316 element == EL_BD_EXPANDABLE_WALL)
9320 Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
9321 Store[ax-1][ay] = element;
9322 GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9323 if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9324 DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9325 IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
9331 Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
9332 Store[ax+1][ay] = element;
9333 GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9334 if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9335 DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9336 IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
9341 if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
9342 TEST_DrawLevelField(ax, ay);
9344 if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
9346 if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
9347 unten_massiv = TRUE;
9348 if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
9349 links_massiv = TRUE;
9350 if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
9351 rechts_massiv = TRUE;
9353 if (((oben_massiv && unten_massiv) ||
9354 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9355 element == EL_EXPANDABLE_WALL) &&
9356 ((links_massiv && rechts_massiv) ||
9357 element == EL_EXPANDABLE_WALL_VERTICAL))
9358 Feld[ax][ay] = EL_WALL;
9361 PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9364 void MauerAblegerStahl(int ax, int ay)
9366 int element = Feld[ax][ay];
9367 int graphic = el2img(element);
9368 boolean oben_frei = FALSE, unten_frei = FALSE;
9369 boolean links_frei = FALSE, rechts_frei = FALSE;
9370 boolean oben_massiv = FALSE, unten_massiv = FALSE;
9371 boolean links_massiv = FALSE, rechts_massiv = FALSE;
9372 boolean new_wall = FALSE;
9374 if (IS_ANIMATED(graphic))
9375 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9377 if (!MovDelay[ax][ay]) /* start building new wall */
9378 MovDelay[ax][ay] = 6;
9380 if (MovDelay[ax][ay]) /* wait some time before building new wall */
9383 if (MovDelay[ax][ay])
9387 if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9389 if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9391 if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9393 if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9396 if (element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
9397 element == EL_EXPANDABLE_STEELWALL_ANY)
9401 Feld[ax][ay-1] = EL_EXPANDABLE_STEELWALL_GROWING;
9402 Store[ax][ay-1] = element;
9403 GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9404 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9405 DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9406 IMG_EXPANDABLE_STEELWALL_GROWING_UP, 0);
9411 Feld[ax][ay+1] = EL_EXPANDABLE_STEELWALL_GROWING;
9412 Store[ax][ay+1] = element;
9413 GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9414 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9415 DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9416 IMG_EXPANDABLE_STEELWALL_GROWING_DOWN, 0);
9421 if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
9422 element == EL_EXPANDABLE_STEELWALL_ANY)
9426 Feld[ax-1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9427 Store[ax-1][ay] = element;
9428 GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9429 if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9430 DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9431 IMG_EXPANDABLE_STEELWALL_GROWING_LEFT, 0);
9437 Feld[ax+1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9438 Store[ax+1][ay] = element;
9439 GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9440 if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9441 DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9442 IMG_EXPANDABLE_STEELWALL_GROWING_RIGHT, 0);
9447 if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
9449 if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
9450 unten_massiv = TRUE;
9451 if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
9452 links_massiv = TRUE;
9453 if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
9454 rechts_massiv = TRUE;
9456 if (((oben_massiv && unten_massiv) ||
9457 element == EL_EXPANDABLE_STEELWALL_HORIZONTAL) &&
9458 ((links_massiv && rechts_massiv) ||
9459 element == EL_EXPANDABLE_STEELWALL_VERTICAL))
9460 Feld[ax][ay] = EL_STEELWALL;
9463 PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9466 void CheckForDragon(int x, int y)
9469 boolean dragon_found = FALSE;
9470 static int xy[4][2] =
9478 for (i = 0; i < NUM_DIRECTIONS; i++)
9480 for (j = 0; j < 4; j++)
9482 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9484 if (IN_LEV_FIELD(xx, yy) &&
9485 (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
9487 if (Feld[xx][yy] == EL_DRAGON)
9488 dragon_found = TRUE;
9497 for (i = 0; i < NUM_DIRECTIONS; i++)
9499 for (j = 0; j < 3; j++)
9501 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9503 if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
9505 Feld[xx][yy] = EL_EMPTY;
9506 TEST_DrawLevelField(xx, yy);
9515 static void InitBuggyBase(int x, int y)
9517 int element = Feld[x][y];
9518 int activating_delay = FRAMES_PER_SECOND / 4;
9521 (element == EL_SP_BUGGY_BASE ?
9522 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
9523 element == EL_SP_BUGGY_BASE_ACTIVATING ?
9525 element == EL_SP_BUGGY_BASE_ACTIVE ?
9526 1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
9529 static void WarnBuggyBase(int x, int y)
9532 static int xy[4][2] =
9540 for (i = 0; i < NUM_DIRECTIONS; i++)
9542 int xx = x + xy[i][0];
9543 int yy = y + xy[i][1];
9545 if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
9547 PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
9554 static void InitTrap(int x, int y)
9556 ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
9559 static void ActivateTrap(int x, int y)
9561 PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
9564 static void ChangeActiveTrap(int x, int y)
9566 int graphic = IMG_TRAP_ACTIVE;
9568 /* if new animation frame was drawn, correct crumbled sand border */
9569 if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
9570 TEST_DrawLevelFieldCrumbled(x, y);
9573 static int getSpecialActionElement(int element, int number, int base_element)
9575 return (element != EL_EMPTY ? element :
9576 number != -1 ? base_element + number - 1 :
9580 static int getModifiedActionNumber(int value_old, int operator, int operand,
9581 int value_min, int value_max)
9583 int value_new = (operator == CA_MODE_SET ? operand :
9584 operator == CA_MODE_ADD ? value_old + operand :
9585 operator == CA_MODE_SUBTRACT ? value_old - operand :
9586 operator == CA_MODE_MULTIPLY ? value_old * operand :
9587 operator == CA_MODE_DIVIDE ? value_old / MAX(1, operand) :
9588 operator == CA_MODE_MODULO ? value_old % MAX(1, operand) :
9591 return (value_new < value_min ? value_min :
9592 value_new > value_max ? value_max :
9596 static void ExecuteCustomElementAction(int x, int y, int element, int page)
9598 struct ElementInfo *ei = &element_info[element];
9599 struct ElementChangeInfo *change = &ei->change_page[page];
9600 int target_element = change->target_element;
9601 int action_type = change->action_type;
9602 int action_mode = change->action_mode;
9603 int action_arg = change->action_arg;
9604 int action_element = change->action_element;
9607 if (!change->has_action)
9610 /* ---------- determine action paramater values -------------------------- */
9612 int level_time_value =
9613 (level.time > 0 ? TimeLeft :
9616 int action_arg_element_raw =
9617 (action_arg == CA_ARG_PLAYER_TRIGGER ? change->actual_trigger_player :
9618 action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
9619 action_arg == CA_ARG_ELEMENT_TARGET ? change->target_element :
9620 action_arg == CA_ARG_ELEMENT_ACTION ? change->action_element :
9621 action_arg == CA_ARG_INVENTORY_RM_TRIGGER ? change->actual_trigger_element:
9622 action_arg == CA_ARG_INVENTORY_RM_TARGET ? change->target_element :
9623 action_arg == CA_ARG_INVENTORY_RM_ACTION ? change->action_element :
9625 int action_arg_element = GetElementFromGroupElement(action_arg_element_raw);
9627 int action_arg_direction =
9628 (action_arg >= CA_ARG_DIRECTION_LEFT &&
9629 action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
9630 action_arg == CA_ARG_DIRECTION_TRIGGER ?
9631 change->actual_trigger_side :
9632 action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
9633 MV_DIR_OPPOSITE(change->actual_trigger_side) :
9636 int action_arg_number_min =
9637 (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
9640 int action_arg_number_max =
9641 (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
9642 action_type == CA_SET_LEVEL_GEMS ? 999 :
9643 action_type == CA_SET_LEVEL_TIME ? 9999 :
9644 action_type == CA_SET_LEVEL_SCORE ? 99999 :
9645 action_type == CA_SET_CE_VALUE ? 9999 :
9646 action_type == CA_SET_CE_SCORE ? 9999 :
9649 int action_arg_number_reset =
9650 (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize[0] :
9651 action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
9652 action_type == CA_SET_LEVEL_TIME ? level.time :
9653 action_type == CA_SET_LEVEL_SCORE ? 0 :
9654 action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
9655 action_type == CA_SET_CE_SCORE ? 0 :
9658 int action_arg_number =
9659 (action_arg <= CA_ARG_MAX ? action_arg :
9660 action_arg >= CA_ARG_SPEED_NOT_MOVING &&
9661 action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
9662 action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
9663 action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
9664 action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
9665 action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
9666 action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
9667 action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
9668 action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
9669 action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
9670 action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? local_player->gems_still_needed :
9671 action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? local_player->score :
9672 action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
9673 action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
9674 action_arg == CA_ARG_ELEMENT_CV_ACTION ? GET_NEW_CE_VALUE(action_element):
9675 action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
9676 action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
9677 action_arg == CA_ARG_ELEMENT_CS_ACTION ? GET_CE_SCORE(action_element) :
9678 action_arg == CA_ARG_ELEMENT_NR_TARGET ? change->target_element :
9679 action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
9680 action_arg == CA_ARG_ELEMENT_NR_ACTION ? change->action_element :
9683 int action_arg_number_old =
9684 (action_type == CA_SET_LEVEL_GEMS ? local_player->gems_still_needed :
9685 action_type == CA_SET_LEVEL_TIME ? TimeLeft :
9686 action_type == CA_SET_LEVEL_SCORE ? local_player->score :
9687 action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
9688 action_type == CA_SET_CE_SCORE ? ei->collect_score :
9691 int action_arg_number_new =
9692 getModifiedActionNumber(action_arg_number_old,
9693 action_mode, action_arg_number,
9694 action_arg_number_min, action_arg_number_max);
9696 int trigger_player_bits =
9697 (change->actual_trigger_player_bits != CH_PLAYER_NONE ?
9698 change->actual_trigger_player_bits : change->trigger_player);
9700 int action_arg_player_bits =
9701 (action_arg >= CA_ARG_PLAYER_1 &&
9702 action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
9703 action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
9704 action_arg == CA_ARG_PLAYER_ACTION ? 1 << GET_PLAYER_NR(action_element) :
9707 /* ---------- execute action -------------------------------------------- */
9709 switch (action_type)
9716 /* ---------- level actions ------------------------------------------- */
9718 case CA_RESTART_LEVEL:
9720 game.restart_level = TRUE;
9725 case CA_SHOW_ENVELOPE:
9727 int element = getSpecialActionElement(action_arg_element,
9728 action_arg_number, EL_ENVELOPE_1);
9730 if (IS_ENVELOPE(element))
9731 local_player->show_envelope = element;
9736 case CA_SET_LEVEL_TIME:
9738 if (level.time > 0) /* only modify limited time value */
9740 TimeLeft = action_arg_number_new;
9742 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
9744 DisplayGameControlValues();
9746 if (!TimeLeft && setup.time_limit)
9747 for (i = 0; i < MAX_PLAYERS; i++)
9748 KillPlayer(&stored_player[i]);
9754 case CA_SET_LEVEL_SCORE:
9756 local_player->score = action_arg_number_new;
9758 game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
9760 DisplayGameControlValues();
9765 case CA_SET_LEVEL_GEMS:
9767 local_player->gems_still_needed = action_arg_number_new;
9769 game.snapshot.collected_item = TRUE;
9771 game_panel_controls[GAME_PANEL_GEMS].value =
9772 local_player->gems_still_needed;
9774 DisplayGameControlValues();
9779 case CA_SET_LEVEL_WIND:
9781 game.wind_direction = action_arg_direction;
9786 case CA_SET_LEVEL_RANDOM_SEED:
9788 /* ensure that setting a new random seed while playing is predictable */
9789 InitRND(action_arg_number_new ? action_arg_number_new : RND(1000000) + 1);
9794 /* ---------- player actions ------------------------------------------ */
9796 case CA_MOVE_PLAYER:
9798 /* automatically move to the next field in specified direction */
9799 for (i = 0; i < MAX_PLAYERS; i++)
9800 if (trigger_player_bits & (1 << i))
9801 stored_player[i].programmed_action = action_arg_direction;
9806 case CA_EXIT_PLAYER:
9808 for (i = 0; i < MAX_PLAYERS; i++)
9809 if (action_arg_player_bits & (1 << i))
9810 PlayerWins(&stored_player[i]);
9815 case CA_KILL_PLAYER:
9817 for (i = 0; i < MAX_PLAYERS; i++)
9818 if (action_arg_player_bits & (1 << i))
9819 KillPlayer(&stored_player[i]);
9824 case CA_SET_PLAYER_KEYS:
9826 int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
9827 int element = getSpecialActionElement(action_arg_element,
9828 action_arg_number, EL_KEY_1);
9830 if (IS_KEY(element))
9832 for (i = 0; i < MAX_PLAYERS; i++)
9834 if (trigger_player_bits & (1 << i))
9836 stored_player[i].key[KEY_NR(element)] = key_state;
9838 DrawGameDoorValues();
9846 case CA_SET_PLAYER_SPEED:
9848 for (i = 0; i < MAX_PLAYERS; i++)
9850 if (trigger_player_bits & (1 << i))
9852 int move_stepsize = TILEX / stored_player[i].move_delay_value;
9854 if (action_arg == CA_ARG_SPEED_FASTER &&
9855 stored_player[i].cannot_move)
9857 action_arg_number = STEPSIZE_VERY_SLOW;
9859 else if (action_arg == CA_ARG_SPEED_SLOWER ||
9860 action_arg == CA_ARG_SPEED_FASTER)
9862 action_arg_number = 2;
9863 action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
9866 else if (action_arg == CA_ARG_NUMBER_RESET)
9868 action_arg_number = level.initial_player_stepsize[i];
9872 getModifiedActionNumber(move_stepsize,
9875 action_arg_number_min,
9876 action_arg_number_max);
9878 SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
9885 case CA_SET_PLAYER_SHIELD:
9887 for (i = 0; i < MAX_PLAYERS; i++)
9889 if (trigger_player_bits & (1 << i))
9891 if (action_arg == CA_ARG_SHIELD_OFF)
9893 stored_player[i].shield_normal_time_left = 0;
9894 stored_player[i].shield_deadly_time_left = 0;
9896 else if (action_arg == CA_ARG_SHIELD_NORMAL)
9898 stored_player[i].shield_normal_time_left = 999999;
9900 else if (action_arg == CA_ARG_SHIELD_DEADLY)
9902 stored_player[i].shield_normal_time_left = 999999;
9903 stored_player[i].shield_deadly_time_left = 999999;
9911 case CA_SET_PLAYER_GRAVITY:
9913 for (i = 0; i < MAX_PLAYERS; i++)
9915 if (trigger_player_bits & (1 << i))
9917 stored_player[i].gravity =
9918 (action_arg == CA_ARG_GRAVITY_OFF ? FALSE :
9919 action_arg == CA_ARG_GRAVITY_ON ? TRUE :
9920 action_arg == CA_ARG_GRAVITY_TOGGLE ? !stored_player[i].gravity :
9921 stored_player[i].gravity);
9928 case CA_SET_PLAYER_ARTWORK:
9930 for (i = 0; i < MAX_PLAYERS; i++)
9932 if (trigger_player_bits & (1 << i))
9934 int artwork_element = action_arg_element;
9936 if (action_arg == CA_ARG_ELEMENT_RESET)
9938 (level.use_artwork_element[i] ? level.artwork_element[i] :
9939 stored_player[i].element_nr);
9941 if (stored_player[i].artwork_element != artwork_element)
9942 stored_player[i].Frame = 0;
9944 stored_player[i].artwork_element = artwork_element;
9946 SetPlayerWaiting(&stored_player[i], FALSE);
9948 /* set number of special actions for bored and sleeping animation */
9949 stored_player[i].num_special_action_bored =
9950 get_num_special_action(artwork_element,
9951 ACTION_BORING_1, ACTION_BORING_LAST);
9952 stored_player[i].num_special_action_sleeping =
9953 get_num_special_action(artwork_element,
9954 ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
9961 case CA_SET_PLAYER_INVENTORY:
9963 for (i = 0; i < MAX_PLAYERS; i++)
9965 struct PlayerInfo *player = &stored_player[i];
9968 if (trigger_player_bits & (1 << i))
9970 int inventory_element = action_arg_element;
9972 if (action_arg == CA_ARG_ELEMENT_TARGET ||
9973 action_arg == CA_ARG_ELEMENT_TRIGGER ||
9974 action_arg == CA_ARG_ELEMENT_ACTION)
9976 int element = inventory_element;
9977 int collect_count = element_info[element].collect_count_initial;
9979 if (!IS_CUSTOM_ELEMENT(element))
9982 if (collect_count == 0)
9983 player->inventory_infinite_element = element;
9985 for (k = 0; k < collect_count; k++)
9986 if (player->inventory_size < MAX_INVENTORY_SIZE)
9987 player->inventory_element[player->inventory_size++] =
9990 else if (action_arg == CA_ARG_INVENTORY_RM_TARGET ||
9991 action_arg == CA_ARG_INVENTORY_RM_TRIGGER ||
9992 action_arg == CA_ARG_INVENTORY_RM_ACTION)
9994 if (player->inventory_infinite_element != EL_UNDEFINED &&
9995 IS_EQUAL_OR_IN_GROUP(player->inventory_infinite_element,
9996 action_arg_element_raw))
9997 player->inventory_infinite_element = EL_UNDEFINED;
9999 for (k = 0, j = 0; j < player->inventory_size; j++)
10001 if (!IS_EQUAL_OR_IN_GROUP(player->inventory_element[j],
10002 action_arg_element_raw))
10003 player->inventory_element[k++] = player->inventory_element[j];
10006 player->inventory_size = k;
10008 else if (action_arg == CA_ARG_INVENTORY_RM_FIRST)
10010 if (player->inventory_size > 0)
10012 for (j = 0; j < player->inventory_size - 1; j++)
10013 player->inventory_element[j] = player->inventory_element[j + 1];
10015 player->inventory_size--;
10018 else if (action_arg == CA_ARG_INVENTORY_RM_LAST)
10020 if (player->inventory_size > 0)
10021 player->inventory_size--;
10023 else if (action_arg == CA_ARG_INVENTORY_RM_ALL)
10025 player->inventory_infinite_element = EL_UNDEFINED;
10026 player->inventory_size = 0;
10028 else if (action_arg == CA_ARG_INVENTORY_RESET)
10030 player->inventory_infinite_element = EL_UNDEFINED;
10031 player->inventory_size = 0;
10033 if (level.use_initial_inventory[i])
10035 for (j = 0; j < level.initial_inventory_size[i]; j++)
10037 int element = level.initial_inventory_content[i][j];
10038 int collect_count = element_info[element].collect_count_initial;
10040 if (!IS_CUSTOM_ELEMENT(element))
10043 if (collect_count == 0)
10044 player->inventory_infinite_element = element;
10046 for (k = 0; k < collect_count; k++)
10047 if (player->inventory_size < MAX_INVENTORY_SIZE)
10048 player->inventory_element[player->inventory_size++] =
10059 /* ---------- CE actions ---------------------------------------------- */
10061 case CA_SET_CE_VALUE:
10063 int last_ce_value = CustomValue[x][y];
10065 CustomValue[x][y] = action_arg_number_new;
10067 if (CustomValue[x][y] != last_ce_value)
10069 CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_CHANGES);
10070 CheckTriggeredElementChange(x, y, element, CE_VALUE_CHANGES_OF_X);
10072 if (CustomValue[x][y] == 0)
10074 CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
10075 CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
10082 case CA_SET_CE_SCORE:
10084 int last_ce_score = ei->collect_score;
10086 ei->collect_score = action_arg_number_new;
10088 if (ei->collect_score != last_ce_score)
10090 CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
10091 CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
10093 if (ei->collect_score == 0)
10097 CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
10098 CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
10101 This is a very special case that seems to be a mixture between
10102 CheckElementChange() and CheckTriggeredElementChange(): while
10103 the first one only affects single elements that are triggered
10104 directly, the second one affects multiple elements in the playfield
10105 that are triggered indirectly by another element. This is a third
10106 case: Changing the CE score always affects multiple identical CEs,
10107 so every affected CE must be checked, not only the single CE for
10108 which the CE score was changed in the first place (as every instance
10109 of that CE shares the same CE score, and therefore also can change)!
10111 SCAN_PLAYFIELD(xx, yy)
10113 if (Feld[xx][yy] == element)
10114 CheckElementChange(xx, yy, element, EL_UNDEFINED,
10115 CE_SCORE_GETS_ZERO);
10123 case CA_SET_CE_ARTWORK:
10125 int artwork_element = action_arg_element;
10126 boolean reset_frame = FALSE;
10129 if (action_arg == CA_ARG_ELEMENT_RESET)
10130 artwork_element = (ei->use_gfx_element ? ei->gfx_element_initial :
10133 if (ei->gfx_element != artwork_element)
10134 reset_frame = TRUE;
10136 ei->gfx_element = artwork_element;
10138 SCAN_PLAYFIELD(xx, yy)
10140 if (Feld[xx][yy] == element)
10144 ResetGfxAnimation(xx, yy);
10145 ResetRandomAnimationValue(xx, yy);
10148 TEST_DrawLevelField(xx, yy);
10155 /* ---------- engine actions ------------------------------------------ */
10157 case CA_SET_ENGINE_SCAN_MODE:
10159 InitPlayfieldScanMode(action_arg);
10169 static void CreateFieldExt(int x, int y, int element, boolean is_change)
10171 int old_element = Feld[x][y];
10172 int new_element = GetElementFromGroupElement(element);
10173 int previous_move_direction = MovDir[x][y];
10174 int last_ce_value = CustomValue[x][y];
10175 boolean player_explosion_protected = PLAYER_EXPLOSION_PROTECTED(x, y);
10176 boolean new_element_is_player = ELEM_IS_PLAYER(new_element);
10177 boolean add_player_onto_element = (new_element_is_player &&
10178 new_element != EL_SOKOBAN_FIELD_PLAYER &&
10179 IS_WALKABLE(old_element));
10181 if (!add_player_onto_element)
10183 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
10184 RemoveMovingField(x, y);
10188 Feld[x][y] = new_element;
10190 if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
10191 MovDir[x][y] = previous_move_direction;
10193 if (element_info[new_element].use_last_ce_value)
10194 CustomValue[x][y] = last_ce_value;
10196 InitField_WithBug1(x, y, FALSE);
10198 new_element = Feld[x][y]; /* element may have changed */
10200 ResetGfxAnimation(x, y);
10201 ResetRandomAnimationValue(x, y);
10203 TEST_DrawLevelField(x, y);
10205 if (GFX_CRUMBLED(new_element))
10206 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
10209 /* check if element under the player changes from accessible to unaccessible
10210 (needed for special case of dropping element which then changes) */
10211 /* (must be checked after creating new element for walkable group elements) */
10212 if (IS_PLAYER(x, y) && !player_explosion_protected &&
10213 IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
10220 /* "ChangeCount" not set yet to allow "entered by player" change one time */
10221 if (new_element_is_player)
10222 RelocatePlayer(x, y, new_element);
10225 ChangeCount[x][y]++; /* count number of changes in the same frame */
10227 TestIfBadThingTouchesPlayer(x, y);
10228 TestIfPlayerTouchesCustomElement(x, y);
10229 TestIfElementTouchesCustomElement(x, y);
10232 static void CreateField(int x, int y, int element)
10234 CreateFieldExt(x, y, element, FALSE);
10237 static void CreateElementFromChange(int x, int y, int element)
10239 element = GET_VALID_RUNTIME_ELEMENT(element);
10241 if (game.engine_version >= VERSION_IDENT(3,2,0,7))
10243 int old_element = Feld[x][y];
10245 /* prevent changed element from moving in same engine frame
10246 unless both old and new element can either fall or move */
10247 if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
10248 (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
10252 CreateFieldExt(x, y, element, TRUE);
10255 static boolean ChangeElement(int x, int y, int element, int page)
10257 struct ElementInfo *ei = &element_info[element];
10258 struct ElementChangeInfo *change = &ei->change_page[page];
10259 int ce_value = CustomValue[x][y];
10260 int ce_score = ei->collect_score;
10261 int target_element;
10262 int old_element = Feld[x][y];
10264 /* always use default change event to prevent running into a loop */
10265 if (ChangeEvent[x][y] == -1)
10266 ChangeEvent[x][y] = CE_DELAY;
10268 if (ChangeEvent[x][y] == CE_DELAY)
10270 /* reset actual trigger element, trigger player and action element */
10271 change->actual_trigger_element = EL_EMPTY;
10272 change->actual_trigger_player = EL_EMPTY;
10273 change->actual_trigger_player_bits = CH_PLAYER_NONE;
10274 change->actual_trigger_side = CH_SIDE_NONE;
10275 change->actual_trigger_ce_value = 0;
10276 change->actual_trigger_ce_score = 0;
10279 /* do not change elements more than a specified maximum number of changes */
10280 if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
10283 ChangeCount[x][y]++; /* count number of changes in the same frame */
10285 if (change->explode)
10292 if (change->use_target_content)
10294 boolean complete_replace = TRUE;
10295 boolean can_replace[3][3];
10298 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10301 boolean is_walkable;
10302 boolean is_diggable;
10303 boolean is_collectible;
10304 boolean is_removable;
10305 boolean is_destructible;
10306 int ex = x + xx - 1;
10307 int ey = y + yy - 1;
10308 int content_element = change->target_content.e[xx][yy];
10311 can_replace[xx][yy] = TRUE;
10313 if (ex == x && ey == y) /* do not check changing element itself */
10316 if (content_element == EL_EMPTY_SPACE)
10318 can_replace[xx][yy] = FALSE; /* do not replace border with space */
10323 if (!IN_LEV_FIELD(ex, ey))
10325 can_replace[xx][yy] = FALSE;
10326 complete_replace = FALSE;
10333 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10334 e = MovingOrBlocked2Element(ex, ey);
10336 is_empty = (IS_FREE(ex, ey) ||
10337 (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
10339 is_walkable = (is_empty || IS_WALKABLE(e));
10340 is_diggable = (is_empty || IS_DIGGABLE(e));
10341 is_collectible = (is_empty || IS_COLLECTIBLE(e));
10342 is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
10343 is_removable = (is_diggable || is_collectible);
10345 can_replace[xx][yy] =
10346 (((change->replace_when == CP_WHEN_EMPTY && is_empty) ||
10347 (change->replace_when == CP_WHEN_WALKABLE && is_walkable) ||
10348 (change->replace_when == CP_WHEN_DIGGABLE && is_diggable) ||
10349 (change->replace_when == CP_WHEN_COLLECTIBLE && is_collectible) ||
10350 (change->replace_when == CP_WHEN_REMOVABLE && is_removable) ||
10351 (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
10352 !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
10354 if (!can_replace[xx][yy])
10355 complete_replace = FALSE;
10358 if (!change->only_if_complete || complete_replace)
10360 boolean something_has_changed = FALSE;
10362 if (change->only_if_complete && change->use_random_replace &&
10363 RND(100) < change->random_percentage)
10366 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10368 int ex = x + xx - 1;
10369 int ey = y + yy - 1;
10370 int content_element;
10372 if (can_replace[xx][yy] && (!change->use_random_replace ||
10373 RND(100) < change->random_percentage))
10375 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10376 RemoveMovingField(ex, ey);
10378 ChangeEvent[ex][ey] = ChangeEvent[x][y];
10380 content_element = change->target_content.e[xx][yy];
10381 target_element = GET_TARGET_ELEMENT(element, content_element, change,
10382 ce_value, ce_score);
10384 CreateElementFromChange(ex, ey, target_element);
10386 something_has_changed = TRUE;
10388 /* for symmetry reasons, freeze newly created border elements */
10389 if (ex != x || ey != y)
10390 Stop[ex][ey] = TRUE; /* no more moving in this frame */
10394 if (something_has_changed)
10396 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10397 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10403 target_element = GET_TARGET_ELEMENT(element, change->target_element, change,
10404 ce_value, ce_score);
10406 if (element == EL_DIAGONAL_GROWING ||
10407 element == EL_DIAGONAL_SHRINKING)
10409 target_element = Store[x][y];
10411 Store[x][y] = EL_EMPTY;
10414 CreateElementFromChange(x, y, target_element);
10416 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10417 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10420 /* this uses direct change before indirect change */
10421 CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
10426 static void HandleElementChange(int x, int y, int page)
10428 int element = MovingOrBlocked2Element(x, y);
10429 struct ElementInfo *ei = &element_info[element];
10430 struct ElementChangeInfo *change = &ei->change_page[page];
10431 boolean handle_action_before_change = FALSE;
10434 if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
10435 !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
10438 printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
10439 x, y, element, element_info[element].token_name);
10440 printf("HandleElementChange(): This should never happen!\n");
10445 /* this can happen with classic bombs on walkable, changing elements */
10446 if (!CAN_CHANGE_OR_HAS_ACTION(element))
10451 if (ChangeDelay[x][y] == 0) /* initialize element change */
10453 ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
10455 if (change->can_change)
10457 /* !!! not clear why graphic animation should be reset at all here !!! */
10458 /* !!! UPDATE: but is needed for correct Snake Bite tail animation !!! */
10459 /* !!! SOLUTION: do not reset if graphics engine set to 4 or above !!! */
10462 GRAPHICAL BUG ADDRESSED BY CHECKING GRAPHICS ENGINE VERSION:
10464 When using an animation frame delay of 1 (this only happens with
10465 "sp_zonk.moving.left/right" in the classic graphics), the default
10466 (non-moving) animation shows wrong animation frames (while the
10467 moving animation, like "sp_zonk.moving.left/right", is correct,
10468 so this graphical bug never shows up with the classic graphics).
10469 For an animation with 4 frames, this causes wrong frames 0,0,1,2
10470 be drawn instead of the correct frames 0,1,2,3. This is caused by
10471 "GfxFrame[][]" being reset *twice* (in two successive frames) after
10472 an element change: First when the change delay ("ChangeDelay[][]")
10473 counter has reached zero after decrementing, then a second time in
10474 the next frame (after "GfxFrame[][]" was already incremented) when
10475 "ChangeDelay[][]" is reset to the initial delay value again.
10477 This causes frame 0 to be drawn twice, while the last frame won't
10478 be drawn anymore, resulting in the wrong frame sequence 0,0,1,2.
10480 As some animations may already be cleverly designed around this bug
10481 (at least the "Snake Bite" snake tail animation does this), it cannot
10482 simply be fixed here without breaking such existing animations.
10483 Unfortunately, it cannot easily be detected if a graphics set was
10484 designed "before" or "after" the bug was fixed. As a workaround,
10485 a new graphics set option "game.graphics_engine_version" was added
10486 to be able to specify the game's major release version for which the
10487 graphics set was designed, which can then be used to decide if the
10488 bugfix should be used (version 4 and above) or not (version 3 or
10489 below, or if no version was specified at all, as with old sets).
10491 (The wrong/fixed animation frames can be tested with the test level set
10492 "test_gfxframe" and level "000", which contains a specially prepared
10493 custom element at level position (x/y) == (11/9) which uses the zonk
10494 animation mentioned above. Using "game.graphics_engine_version: 4"
10495 fixes the wrong animation frames, showing the correct frames 0,1,2,3.
10496 This can also be seen from the debug output for this test element.)
10499 /* when a custom element is about to change (for example by change delay),
10500 do not reset graphic animation when the custom element is moving */
10501 if (game.graphics_engine_version < 4 &&
10504 ResetGfxAnimation(x, y);
10505 ResetRandomAnimationValue(x, y);
10508 if (change->pre_change_function)
10509 change->pre_change_function(x, y);
10513 ChangeDelay[x][y]--;
10515 if (ChangeDelay[x][y] != 0) /* continue element change */
10517 if (change->can_change)
10519 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10521 if (IS_ANIMATED(graphic))
10522 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10524 if (change->change_function)
10525 change->change_function(x, y);
10528 else /* finish element change */
10530 if (ChangePage[x][y] != -1) /* remember page from delayed change */
10532 page = ChangePage[x][y];
10533 ChangePage[x][y] = -1;
10535 change = &ei->change_page[page];
10538 if (IS_MOVING(x, y)) /* never change a running system ;-) */
10540 ChangeDelay[x][y] = 1; /* try change after next move step */
10541 ChangePage[x][y] = page; /* remember page to use for change */
10546 /* special case: set new level random seed before changing element */
10547 if (change->has_action && change->action_type == CA_SET_LEVEL_RANDOM_SEED)
10548 handle_action_before_change = TRUE;
10550 if (change->has_action && handle_action_before_change)
10551 ExecuteCustomElementAction(x, y, element, page);
10553 if (change->can_change)
10555 if (ChangeElement(x, y, element, page))
10557 if (change->post_change_function)
10558 change->post_change_function(x, y);
10562 if (change->has_action && !handle_action_before_change)
10563 ExecuteCustomElementAction(x, y, element, page);
10567 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
10568 int trigger_element,
10570 int trigger_player,
10574 boolean change_done_any = FALSE;
10575 int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
10578 if (!(trigger_events[trigger_element][trigger_event]))
10581 RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
10583 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
10585 int element = EL_CUSTOM_START + i;
10586 boolean change_done = FALSE;
10589 if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
10590 !HAS_ANY_CHANGE_EVENT(element, trigger_event))
10593 for (p = 0; p < element_info[element].num_change_pages; p++)
10595 struct ElementChangeInfo *change = &element_info[element].change_page[p];
10597 if (change->can_change_or_has_action &&
10598 change->has_event[trigger_event] &&
10599 change->trigger_side & trigger_side &&
10600 change->trigger_player & trigger_player &&
10601 change->trigger_page & trigger_page_bits &&
10602 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
10604 change->actual_trigger_element = trigger_element;
10605 change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
10606 change->actual_trigger_player_bits = trigger_player;
10607 change->actual_trigger_side = trigger_side;
10608 change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
10609 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10611 if ((change->can_change && !change_done) || change->has_action)
10615 SCAN_PLAYFIELD(x, y)
10617 if (Feld[x][y] == element)
10619 if (change->can_change && !change_done)
10621 /* if element already changed in this frame, not only prevent
10622 another element change (checked in ChangeElement()), but
10623 also prevent additional element actions for this element */
10625 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
10626 !level.use_action_after_change_bug)
10629 ChangeDelay[x][y] = 1;
10630 ChangeEvent[x][y] = trigger_event;
10632 HandleElementChange(x, y, p);
10634 else if (change->has_action)
10636 /* if element already changed in this frame, not only prevent
10637 another element change (checked in ChangeElement()), but
10638 also prevent additional element actions for this element */
10640 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
10641 !level.use_action_after_change_bug)
10644 ExecuteCustomElementAction(x, y, element, p);
10645 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10650 if (change->can_change)
10652 change_done = TRUE;
10653 change_done_any = TRUE;
10660 RECURSION_LOOP_DETECTION_END();
10662 return change_done_any;
10665 static boolean CheckElementChangeExt(int x, int y,
10667 int trigger_element,
10669 int trigger_player,
10672 boolean change_done = FALSE;
10675 if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
10676 !HAS_ANY_CHANGE_EVENT(element, trigger_event))
10679 if (Feld[x][y] == EL_BLOCKED)
10681 Blocked2Moving(x, y, &x, &y);
10682 element = Feld[x][y];
10685 /* check if element has already changed or is about to change after moving */
10686 if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
10687 Feld[x][y] != element) ||
10689 (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
10690 (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
10691 ChangePage[x][y] != -1)))
10694 RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
10696 for (p = 0; p < element_info[element].num_change_pages; p++)
10698 struct ElementChangeInfo *change = &element_info[element].change_page[p];
10700 /* check trigger element for all events where the element that is checked
10701 for changing interacts with a directly adjacent element -- this is
10702 different to element changes that affect other elements to change on the
10703 whole playfield (which is handeld by CheckTriggeredElementChangeExt()) */
10704 boolean check_trigger_element =
10705 (trigger_event == CE_TOUCHING_X ||
10706 trigger_event == CE_HITTING_X ||
10707 trigger_event == CE_HIT_BY_X ||
10708 trigger_event == CE_DIGGING_X); /* this one was forgotten until 3.2.3 */
10710 if (change->can_change_or_has_action &&
10711 change->has_event[trigger_event] &&
10712 change->trigger_side & trigger_side &&
10713 change->trigger_player & trigger_player &&
10714 (!check_trigger_element ||
10715 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
10717 change->actual_trigger_element = trigger_element;
10718 change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
10719 change->actual_trigger_player_bits = trigger_player;
10720 change->actual_trigger_side = trigger_side;
10721 change->actual_trigger_ce_value = CustomValue[x][y];
10722 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10724 /* special case: trigger element not at (x,y) position for some events */
10725 if (check_trigger_element)
10737 { 0, 0 }, { 0, 0 }, { 0, 0 },
10741 int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
10742 int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
10744 change->actual_trigger_ce_value = CustomValue[xx][yy];
10745 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10748 if (change->can_change && !change_done)
10750 ChangeDelay[x][y] = 1;
10751 ChangeEvent[x][y] = trigger_event;
10753 HandleElementChange(x, y, p);
10755 change_done = TRUE;
10757 else if (change->has_action)
10759 ExecuteCustomElementAction(x, y, element, p);
10760 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10765 RECURSION_LOOP_DETECTION_END();
10767 return change_done;
10770 static void PlayPlayerSound(struct PlayerInfo *player)
10772 int jx = player->jx, jy = player->jy;
10773 int sound_element = player->artwork_element;
10774 int last_action = player->last_action_waiting;
10775 int action = player->action_waiting;
10777 if (player->is_waiting)
10779 if (action != last_action)
10780 PlayLevelSoundElementAction(jx, jy, sound_element, action);
10782 PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
10786 if (action != last_action)
10787 StopSound(element_info[sound_element].sound[last_action]);
10789 if (last_action == ACTION_SLEEPING)
10790 PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
10794 static void PlayAllPlayersSound()
10798 for (i = 0; i < MAX_PLAYERS; i++)
10799 if (stored_player[i].active)
10800 PlayPlayerSound(&stored_player[i]);
10803 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
10805 boolean last_waiting = player->is_waiting;
10806 int move_dir = player->MovDir;
10808 player->dir_waiting = move_dir;
10809 player->last_action_waiting = player->action_waiting;
10813 if (!last_waiting) /* not waiting -> waiting */
10815 player->is_waiting = TRUE;
10817 player->frame_counter_bored =
10819 game.player_boring_delay_fixed +
10820 GetSimpleRandom(game.player_boring_delay_random);
10821 player->frame_counter_sleeping =
10823 game.player_sleeping_delay_fixed +
10824 GetSimpleRandom(game.player_sleeping_delay_random);
10826 InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
10829 if (game.player_sleeping_delay_fixed +
10830 game.player_sleeping_delay_random > 0 &&
10831 player->anim_delay_counter == 0 &&
10832 player->post_delay_counter == 0 &&
10833 FrameCounter >= player->frame_counter_sleeping)
10834 player->is_sleeping = TRUE;
10835 else if (game.player_boring_delay_fixed +
10836 game.player_boring_delay_random > 0 &&
10837 FrameCounter >= player->frame_counter_bored)
10838 player->is_bored = TRUE;
10840 player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
10841 player->is_bored ? ACTION_BORING :
10844 if (player->is_sleeping && player->use_murphy)
10846 /* special case for sleeping Murphy when leaning against non-free tile */
10848 if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
10849 (Feld[player->jx - 1][player->jy] != EL_EMPTY &&
10850 !IS_MOVING(player->jx - 1, player->jy)))
10851 move_dir = MV_LEFT;
10852 else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
10853 (Feld[player->jx + 1][player->jy] != EL_EMPTY &&
10854 !IS_MOVING(player->jx + 1, player->jy)))
10855 move_dir = MV_RIGHT;
10857 player->is_sleeping = FALSE;
10859 player->dir_waiting = move_dir;
10862 if (player->is_sleeping)
10864 if (player->num_special_action_sleeping > 0)
10866 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
10868 int last_special_action = player->special_action_sleeping;
10869 int num_special_action = player->num_special_action_sleeping;
10870 int special_action =
10871 (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
10872 last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
10873 last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
10874 last_special_action + 1 : ACTION_SLEEPING);
10875 int special_graphic =
10876 el_act_dir2img(player->artwork_element, special_action, move_dir);
10878 player->anim_delay_counter =
10879 graphic_info[special_graphic].anim_delay_fixed +
10880 GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
10881 player->post_delay_counter =
10882 graphic_info[special_graphic].post_delay_fixed +
10883 GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
10885 player->special_action_sleeping = special_action;
10888 if (player->anim_delay_counter > 0)
10890 player->action_waiting = player->special_action_sleeping;
10891 player->anim_delay_counter--;
10893 else if (player->post_delay_counter > 0)
10895 player->post_delay_counter--;
10899 else if (player->is_bored)
10901 if (player->num_special_action_bored > 0)
10903 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
10905 int special_action =
10906 ACTION_BORING_1 + GetSimpleRandom(player->num_special_action_bored);
10907 int special_graphic =
10908 el_act_dir2img(player->artwork_element, special_action, move_dir);
10910 player->anim_delay_counter =
10911 graphic_info[special_graphic].anim_delay_fixed +
10912 GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
10913 player->post_delay_counter =
10914 graphic_info[special_graphic].post_delay_fixed +
10915 GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
10917 player->special_action_bored = special_action;
10920 if (player->anim_delay_counter > 0)
10922 player->action_waiting = player->special_action_bored;
10923 player->anim_delay_counter--;
10925 else if (player->post_delay_counter > 0)
10927 player->post_delay_counter--;
10932 else if (last_waiting) /* waiting -> not waiting */
10934 player->is_waiting = FALSE;
10935 player->is_bored = FALSE;
10936 player->is_sleeping = FALSE;
10938 player->frame_counter_bored = -1;
10939 player->frame_counter_sleeping = -1;
10941 player->anim_delay_counter = 0;
10942 player->post_delay_counter = 0;
10944 player->dir_waiting = player->MovDir;
10945 player->action_waiting = ACTION_DEFAULT;
10947 player->special_action_bored = ACTION_DEFAULT;
10948 player->special_action_sleeping = ACTION_DEFAULT;
10952 static void CheckSaveEngineSnapshot(struct PlayerInfo *player)
10954 if ((!player->is_moving && player->was_moving) ||
10955 (player->MovPos == 0 && player->was_moving) ||
10956 (player->is_snapping && !player->was_snapping) ||
10957 (player->is_dropping && !player->was_dropping))
10959 if (!CheckSaveEngineSnapshotToList())
10962 player->was_moving = FALSE;
10963 player->was_snapping = TRUE;
10964 player->was_dropping = TRUE;
10968 if (player->is_moving)
10969 player->was_moving = TRUE;
10971 if (!player->is_snapping)
10972 player->was_snapping = FALSE;
10974 if (!player->is_dropping)
10975 player->was_dropping = FALSE;
10979 static void CheckSingleStepMode(struct PlayerInfo *player)
10981 if (tape.single_step && tape.recording && !tape.pausing)
10983 /* as it is called "single step mode", just return to pause mode when the
10984 player stopped moving after one tile (or never starts moving at all) */
10985 if (!player->is_moving &&
10986 !player->is_pushing &&
10987 !player->is_dropping_pressed)
10989 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
10990 SnapField(player, 0, 0); /* stop snapping */
10994 CheckSaveEngineSnapshot(player);
10997 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
10999 int left = player_action & JOY_LEFT;
11000 int right = player_action & JOY_RIGHT;
11001 int up = player_action & JOY_UP;
11002 int down = player_action & JOY_DOWN;
11003 int button1 = player_action & JOY_BUTTON_1;
11004 int button2 = player_action & JOY_BUTTON_2;
11005 int dx = (left ? -1 : right ? 1 : 0);
11006 int dy = (up ? -1 : down ? 1 : 0);
11008 if (!player->active || tape.pausing)
11014 SnapField(player, dx, dy);
11018 DropElement(player);
11020 MovePlayer(player, dx, dy);
11023 CheckSingleStepMode(player);
11025 SetPlayerWaiting(player, FALSE);
11027 return player_action;
11031 /* no actions for this player (no input at player's configured device) */
11033 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
11034 SnapField(player, 0, 0);
11035 CheckGravityMovementWhenNotMoving(player);
11037 if (player->MovPos == 0)
11038 SetPlayerWaiting(player, TRUE);
11040 if (player->MovPos == 0) /* needed for tape.playing */
11041 player->is_moving = FALSE;
11043 player->is_dropping = FALSE;
11044 player->is_dropping_pressed = FALSE;
11045 player->drop_pressed_delay = 0;
11047 CheckSingleStepMode(player);
11053 static void SetMouseActionFromTapeAction(struct MouseActionInfo *mouse_action,
11056 if (!tape.use_mouse)
11059 mouse_action->lx = tape_action[TAPE_ACTION_LX];
11060 mouse_action->ly = tape_action[TAPE_ACTION_LY];
11061 mouse_action->button = tape_action[TAPE_ACTION_BUTTON];
11064 static void SetTapeActionFromMouseAction(byte *tape_action,
11065 struct MouseActionInfo *mouse_action)
11067 if (!tape.use_mouse)
11070 tape_action[TAPE_ACTION_LX] = mouse_action->lx;
11071 tape_action[TAPE_ACTION_LY] = mouse_action->ly;
11072 tape_action[TAPE_ACTION_BUTTON] = mouse_action->button;
11075 static void CheckLevelTime()
11079 /* !!! SAME CODE AS IN "GameActions()" -- FIX THIS !!! */
11080 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11082 if (level.native_em_level->lev->home == 0) /* all players at home */
11084 PlayerWins(local_player);
11086 AllPlayersGone = TRUE;
11088 level.native_em_level->lev->home = -1;
11091 if (level.native_em_level->ply[0]->alive == 0 &&
11092 level.native_em_level->ply[1]->alive == 0 &&
11093 level.native_em_level->ply[2]->alive == 0 &&
11094 level.native_em_level->ply[3]->alive == 0) /* all dead */
11095 AllPlayersGone = TRUE;
11097 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11099 if (game_sp.LevelSolved &&
11100 !game_sp.GameOver) /* game won */
11102 PlayerWins(local_player);
11104 game_sp.GameOver = TRUE;
11106 AllPlayersGone = TRUE;
11109 if (game_sp.GameOver) /* game lost */
11110 AllPlayersGone = TRUE;
11112 else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
11114 if (game_mm.level_solved &&
11115 !game_mm.game_over) /* game won */
11117 PlayerWins(local_player);
11119 game_mm.game_over = TRUE;
11121 AllPlayersGone = TRUE;
11124 if (game_mm.game_over) /* game lost */
11125 AllPlayersGone = TRUE;
11128 if (TimeFrames >= FRAMES_PER_SECOND)
11133 for (i = 0; i < MAX_PLAYERS; i++)
11135 struct PlayerInfo *player = &stored_player[i];
11137 if (SHIELD_ON(player))
11139 player->shield_normal_time_left--;
11141 if (player->shield_deadly_time_left > 0)
11142 player->shield_deadly_time_left--;
11146 if (!local_player->LevelSolved && !level.use_step_counter)
11154 if (TimeLeft <= 10 && setup.time_limit)
11155 PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
11157 /* this does not make sense: game_panel_controls[GAME_PANEL_TIME].value
11158 is reset from other values in UpdateGameDoorValues() -- FIX THIS */
11160 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
11162 if (!TimeLeft && setup.time_limit)
11164 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11165 level.native_em_level->lev->killed_out_of_time = TRUE;
11167 for (i = 0; i < MAX_PLAYERS; i++)
11168 KillPlayer(&stored_player[i]);
11171 else if (game.no_time_limit && !AllPlayersGone) /* level w/o time limit */
11173 game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
11176 level.native_em_level->lev->time =
11177 (game.no_time_limit ? TimePlayed : TimeLeft);
11180 if (tape.recording || tape.playing)
11181 DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
11184 if (tape.recording || tape.playing)
11185 DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
11187 UpdateAndDisplayGameControlValues();
11190 void AdvanceFrameAndPlayerCounters(int player_nr)
11194 /* advance frame counters (global frame counter and time frame counter) */
11198 /* advance player counters (counters for move delay, move animation etc.) */
11199 for (i = 0; i < MAX_PLAYERS; i++)
11201 boolean advance_player_counters = (player_nr == -1 || player_nr == i);
11202 int move_delay_value = stored_player[i].move_delay_value;
11203 int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
11205 if (!advance_player_counters) /* not all players may be affected */
11208 if (move_frames == 0) /* less than one move per game frame */
11210 int stepsize = TILEX / move_delay_value;
11211 int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
11212 int count = (stored_player[i].is_moving ?
11213 ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
11215 if (count % delay == 0)
11219 stored_player[i].Frame += move_frames;
11221 if (stored_player[i].MovPos != 0)
11222 stored_player[i].StepFrame += move_frames;
11224 if (stored_player[i].move_delay > 0)
11225 stored_player[i].move_delay--;
11227 /* due to bugs in previous versions, counter must count up, not down */
11228 if (stored_player[i].push_delay != -1)
11229 stored_player[i].push_delay++;
11231 if (stored_player[i].drop_delay > 0)
11232 stored_player[i].drop_delay--;
11234 if (stored_player[i].is_dropping_pressed)
11235 stored_player[i].drop_pressed_delay++;
11239 void StartGameActions(boolean init_network_game, boolean record_tape,
11242 unsigned int new_random_seed = InitRND(random_seed);
11245 TapeStartRecording(new_random_seed);
11247 #if defined(NETWORK_AVALIABLE)
11248 if (init_network_game)
11250 SendToServer_StartPlaying();
11259 void GameActionsExt()
11262 static unsigned int game_frame_delay = 0;
11264 unsigned int game_frame_delay_value;
11265 byte *recorded_player_action;
11266 byte summarized_player_action = 0;
11267 byte tape_action[MAX_PLAYERS];
11270 /* detect endless loops, caused by custom element programming */
11271 if (recursion_loop_detected && recursion_loop_depth == 0)
11273 char *message = getStringCat3("Internal Error! Element ",
11274 EL_NAME(recursion_loop_element),
11275 " caused endless loop! Quit the game?");
11277 Error(ERR_WARN, "element '%s' caused endless loop in game engine",
11278 EL_NAME(recursion_loop_element));
11280 RequestQuitGameExt(FALSE, level_editor_test_game, message);
11282 recursion_loop_detected = FALSE; /* if game should be continued */
11289 if (game.restart_level)
11290 StartGameActions(options.network, setup.autorecord, level.random_seed);
11292 /* !!! SAME CODE AS IN "CheckLevelTime()" -- FIX THIS !!! */
11293 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11295 if (level.native_em_level->lev->home == 0) /* all players at home */
11297 PlayerWins(local_player);
11299 AllPlayersGone = TRUE;
11301 level.native_em_level->lev->home = -1;
11304 if (level.native_em_level->ply[0]->alive == 0 &&
11305 level.native_em_level->ply[1]->alive == 0 &&
11306 level.native_em_level->ply[2]->alive == 0 &&
11307 level.native_em_level->ply[3]->alive == 0) /* all dead */
11308 AllPlayersGone = TRUE;
11310 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11312 if (game_sp.LevelSolved &&
11313 !game_sp.GameOver) /* game won */
11315 PlayerWins(local_player);
11317 game_sp.GameOver = TRUE;
11319 AllPlayersGone = TRUE;
11322 if (game_sp.GameOver) /* game lost */
11323 AllPlayersGone = TRUE;
11325 else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
11327 if (game_mm.level_solved &&
11328 !game_mm.game_over) /* game won */
11330 PlayerWins(local_player);
11332 game_mm.game_over = TRUE;
11334 AllPlayersGone = TRUE;
11337 if (game_mm.game_over) /* game lost */
11338 AllPlayersGone = TRUE;
11341 if (local_player->LevelSolved && !local_player->LevelSolved_GameEnd)
11344 if (AllPlayersGone && !TAPE_IS_STOPPED(tape))
11347 if (game_status != GAME_MODE_PLAYING) /* status might have changed */
11350 game_frame_delay_value =
11351 (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
11353 if (tape.playing && tape.warp_forward && !tape.pausing)
11354 game_frame_delay_value = 0;
11356 SetVideoFrameDelay(game_frame_delay_value);
11360 /* ---------- main game synchronization point ---------- */
11362 int skip = WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11364 printf("::: skip == %d\n", skip);
11367 /* ---------- main game synchronization point ---------- */
11369 WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11373 if (network_playing && !network_player_action_received)
11375 /* try to get network player actions in time */
11377 #if defined(NETWORK_AVALIABLE)
11378 /* last chance to get network player actions without main loop delay */
11379 HandleNetworking();
11382 /* game was quit by network peer */
11383 if (game_status != GAME_MODE_PLAYING)
11386 if (!network_player_action_received)
11387 return; /* failed to get network player actions in time */
11389 /* do not yet reset "network_player_action_received" (for tape.pausing) */
11395 /* at this point we know that we really continue executing the game */
11397 network_player_action_received = FALSE;
11399 /* when playing tape, read previously recorded player input from tape data */
11400 recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
11402 local_player->effective_mouse_action = local_player->mouse_action;
11404 if (recorded_player_action != NULL)
11405 SetMouseActionFromTapeAction(&local_player->effective_mouse_action,
11406 recorded_player_action);
11408 /* TapePlayAction() may return NULL when toggling to "pause before death" */
11412 if (tape.set_centered_player)
11414 game.centered_player_nr_next = tape.centered_player_nr_next;
11415 game.set_centered_player = TRUE;
11418 for (i = 0; i < MAX_PLAYERS; i++)
11420 summarized_player_action |= stored_player[i].action;
11422 if (!network_playing && (game.team_mode || tape.playing))
11423 stored_player[i].effective_action = stored_player[i].action;
11426 #if defined(NETWORK_AVALIABLE)
11427 if (network_playing)
11428 SendToServer_MovePlayer(summarized_player_action);
11431 // summarize all actions at local players mapped input device position
11432 // (this allows using different input devices in single player mode)
11433 if (!options.network && !game.team_mode)
11434 stored_player[map_player_action[local_player->index_nr]].effective_action =
11435 summarized_player_action;
11437 if (tape.recording &&
11439 setup.input_on_focus &&
11440 game.centered_player_nr != -1)
11442 for (i = 0; i < MAX_PLAYERS; i++)
11443 stored_player[i].effective_action =
11444 (i == game.centered_player_nr ? summarized_player_action : 0);
11447 if (recorded_player_action != NULL)
11448 for (i = 0; i < MAX_PLAYERS; i++)
11449 stored_player[i].effective_action = recorded_player_action[i];
11451 for (i = 0; i < MAX_PLAYERS; i++)
11453 tape_action[i] = stored_player[i].effective_action;
11455 /* (this may happen in the RND game engine if a player was not present on
11456 the playfield on level start, but appeared later from a custom element */
11457 if (setup.team_mode &&
11460 !tape.player_participates[i])
11461 tape.player_participates[i] = TRUE;
11464 SetTapeActionFromMouseAction(tape_action,
11465 &local_player->effective_mouse_action);
11467 /* only record actions from input devices, but not programmed actions */
11468 if (tape.recording)
11469 TapeRecordAction(tape_action);
11471 #if USE_NEW_PLAYER_ASSIGNMENTS
11472 // !!! also map player actions in single player mode !!!
11473 // if (game.team_mode)
11476 byte mapped_action[MAX_PLAYERS];
11478 #if DEBUG_PLAYER_ACTIONS
11480 for (i = 0; i < MAX_PLAYERS; i++)
11481 printf(" %d, ", stored_player[i].effective_action);
11484 for (i = 0; i < MAX_PLAYERS; i++)
11485 mapped_action[i] = stored_player[map_player_action[i]].effective_action;
11487 for (i = 0; i < MAX_PLAYERS; i++)
11488 stored_player[i].effective_action = mapped_action[i];
11490 #if DEBUG_PLAYER_ACTIONS
11492 for (i = 0; i < MAX_PLAYERS; i++)
11493 printf(" %d, ", stored_player[i].effective_action);
11497 #if DEBUG_PLAYER_ACTIONS
11501 for (i = 0; i < MAX_PLAYERS; i++)
11502 printf(" %d, ", stored_player[i].effective_action);
11508 for (i = 0; i < MAX_PLAYERS; i++)
11510 // allow engine snapshot in case of changed movement attempt
11511 if ((game.snapshot.last_action[i] & KEY_MOTION) !=
11512 (stored_player[i].effective_action & KEY_MOTION))
11513 game.snapshot.changed_action = TRUE;
11515 // allow engine snapshot in case of snapping/dropping attempt
11516 if ((game.snapshot.last_action[i] & KEY_BUTTON) == 0 &&
11517 (stored_player[i].effective_action & KEY_BUTTON) != 0)
11518 game.snapshot.changed_action = TRUE;
11520 game.snapshot.last_action[i] = stored_player[i].effective_action;
11523 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11525 GameActions_EM_Main();
11527 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11529 GameActions_SP_Main();
11531 else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
11533 GameActions_MM_Main();
11537 GameActions_RND_Main();
11540 BlitScreenToBitmap(backbuffer);
11544 AdvanceFrameAndPlayerCounters(-1); /* advance counters for all players */
11546 if (global.show_frames_per_second)
11548 static unsigned int fps_counter = 0;
11549 static int fps_frames = 0;
11550 unsigned int fps_delay_ms = Counter() - fps_counter;
11554 if (fps_delay_ms >= 500) /* calculate FPS every 0.5 seconds */
11556 global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
11559 fps_counter = Counter();
11561 /* always draw FPS to screen after FPS value was updated */
11562 redraw_mask |= REDRAW_FPS;
11565 /* only draw FPS if no screen areas are deactivated (invisible warp mode) */
11566 if (GetDrawDeactivationMask() == REDRAW_NONE)
11567 redraw_mask |= REDRAW_FPS;
11571 static void GameActions_CheckSaveEngineSnapshot()
11573 if (!game.snapshot.save_snapshot)
11576 // clear flag for saving snapshot _before_ saving snapshot
11577 game.snapshot.save_snapshot = FALSE;
11579 SaveEngineSnapshotToList();
11586 GameActions_CheckSaveEngineSnapshot();
11589 void GameActions_EM_Main()
11591 byte effective_action[MAX_PLAYERS];
11592 boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11595 for (i = 0; i < MAX_PLAYERS; i++)
11596 effective_action[i] = stored_player[i].effective_action;
11598 GameActions_EM(effective_action, warp_mode);
11601 void GameActions_SP_Main()
11603 byte effective_action[MAX_PLAYERS];
11604 boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11607 for (i = 0; i < MAX_PLAYERS; i++)
11608 effective_action[i] = stored_player[i].effective_action;
11610 GameActions_SP(effective_action, warp_mode);
11612 for (i = 0; i < MAX_PLAYERS; i++)
11614 if (stored_player[i].force_dropping)
11615 stored_player[i].action |= KEY_BUTTON_DROP;
11617 stored_player[i].force_dropping = FALSE;
11621 void GameActions_MM_Main()
11623 boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11625 GameActions_MM(local_player->effective_mouse_action, warp_mode);
11628 void GameActions_RND_Main()
11633 void GameActions_RND()
11635 int magic_wall_x = 0, magic_wall_y = 0;
11636 int i, x, y, element, graphic, last_gfx_frame;
11638 InitPlayfieldScanModeVars();
11640 if (game.engine_version >= VERSION_IDENT(3,2,0,7))
11642 SCAN_PLAYFIELD(x, y)
11644 ChangeCount[x][y] = 0;
11645 ChangeEvent[x][y] = -1;
11649 if (game.set_centered_player)
11651 boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
11653 /* switching to "all players" only possible if all players fit to screen */
11654 if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
11656 game.centered_player_nr_next = game.centered_player_nr;
11657 game.set_centered_player = FALSE;
11660 /* do not switch focus to non-existing (or non-active) player */
11661 if (game.centered_player_nr_next >= 0 &&
11662 !stored_player[game.centered_player_nr_next].active)
11664 game.centered_player_nr_next = game.centered_player_nr;
11665 game.set_centered_player = FALSE;
11669 if (game.set_centered_player &&
11670 ScreenMovPos == 0) /* screen currently aligned at tile position */
11674 if (game.centered_player_nr_next == -1)
11676 setScreenCenteredToAllPlayers(&sx, &sy);
11680 sx = stored_player[game.centered_player_nr_next].jx;
11681 sy = stored_player[game.centered_player_nr_next].jy;
11684 game.centered_player_nr = game.centered_player_nr_next;
11685 game.set_centered_player = FALSE;
11687 DrawRelocateScreen(0, 0, sx, sy, MV_NONE, TRUE, setup.quick_switch);
11688 DrawGameDoorValues();
11691 for (i = 0; i < MAX_PLAYERS; i++)
11693 int actual_player_action = stored_player[i].effective_action;
11696 /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
11697 - rnd_equinox_tetrachloride 048
11698 - rnd_equinox_tetrachloride_ii 096
11699 - rnd_emanuel_schmieg 002
11700 - doctor_sloan_ww 001, 020
11702 if (stored_player[i].MovPos == 0)
11703 CheckGravityMovement(&stored_player[i]);
11706 /* overwrite programmed action with tape action */
11707 if (stored_player[i].programmed_action)
11708 actual_player_action = stored_player[i].programmed_action;
11710 PlayerActions(&stored_player[i], actual_player_action);
11712 ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
11715 ScrollScreen(NULL, SCROLL_GO_ON);
11717 /* for backwards compatibility, the following code emulates a fixed bug that
11718 occured when pushing elements (causing elements that just made their last
11719 pushing step to already (if possible) make their first falling step in the
11720 same game frame, which is bad); this code is also needed to use the famous
11721 "spring push bug" which is used in older levels and might be wanted to be
11722 used also in newer levels, but in this case the buggy pushing code is only
11723 affecting the "spring" element and no other elements */
11725 if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
11727 for (i = 0; i < MAX_PLAYERS; i++)
11729 struct PlayerInfo *player = &stored_player[i];
11730 int x = player->jx;
11731 int y = player->jy;
11733 if (player->active && player->is_pushing && player->is_moving &&
11735 (game.engine_version < VERSION_IDENT(2,2,0,7) ||
11736 Feld[x][y] == EL_SPRING))
11738 ContinueMoving(x, y);
11740 /* continue moving after pushing (this is actually a bug) */
11741 if (!IS_MOVING(x, y))
11742 Stop[x][y] = FALSE;
11747 SCAN_PLAYFIELD(x, y)
11749 ChangeCount[x][y] = 0;
11750 ChangeEvent[x][y] = -1;
11752 /* this must be handled before main playfield loop */
11753 if (Feld[x][y] == EL_PLAYER_IS_LEAVING)
11756 if (MovDelay[x][y] <= 0)
11760 if (Feld[x][y] == EL_ELEMENT_SNAPPING)
11763 if (MovDelay[x][y] <= 0)
11766 TEST_DrawLevelField(x, y);
11768 TestIfElementTouchesCustomElement(x, y); /* for empty space */
11773 if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
11775 printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
11776 printf("GameActions(): This should never happen!\n");
11778 ChangePage[x][y] = -1;
11782 Stop[x][y] = FALSE;
11783 if (WasJustMoving[x][y] > 0)
11784 WasJustMoving[x][y]--;
11785 if (WasJustFalling[x][y] > 0)
11786 WasJustFalling[x][y]--;
11787 if (CheckCollision[x][y] > 0)
11788 CheckCollision[x][y]--;
11789 if (CheckImpact[x][y] > 0)
11790 CheckImpact[x][y]--;
11794 /* reset finished pushing action (not done in ContinueMoving() to allow
11795 continuous pushing animation for elements with zero push delay) */
11796 if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
11798 ResetGfxAnimation(x, y);
11799 TEST_DrawLevelField(x, y);
11803 if (IS_BLOCKED(x, y))
11807 Blocked2Moving(x, y, &oldx, &oldy);
11808 if (!IS_MOVING(oldx, oldy))
11810 printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
11811 printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
11812 printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
11813 printf("GameActions(): This should never happen!\n");
11819 SCAN_PLAYFIELD(x, y)
11821 element = Feld[x][y];
11822 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11823 last_gfx_frame = GfxFrame[x][y];
11825 ResetGfxFrame(x, y);
11827 if (GfxFrame[x][y] != last_gfx_frame && !Stop[x][y])
11828 DrawLevelGraphicAnimation(x, y, graphic);
11830 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
11831 IS_NEXT_FRAME(GfxFrame[x][y], graphic))
11832 ResetRandomAnimationValue(x, y);
11834 SetRandomAnimationValue(x, y);
11836 PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
11838 if (IS_INACTIVE(element))
11840 if (IS_ANIMATED(graphic))
11841 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11846 /* this may take place after moving, so 'element' may have changed */
11847 if (IS_CHANGING(x, y) &&
11848 (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
11850 int page = element_info[element].event_page_nr[CE_DELAY];
11852 HandleElementChange(x, y, page);
11854 element = Feld[x][y];
11855 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11858 if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
11862 element = Feld[x][y];
11863 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11865 if (IS_ANIMATED(graphic) &&
11866 !IS_MOVING(x, y) &&
11868 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11870 if (IS_GEM(element) || element == EL_SP_INFOTRON)
11871 TEST_DrawTwinkleOnField(x, y);
11873 else if (element == EL_ACID)
11876 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11878 else if ((element == EL_EXIT_OPEN ||
11879 element == EL_EM_EXIT_OPEN ||
11880 element == EL_SP_EXIT_OPEN ||
11881 element == EL_STEEL_EXIT_OPEN ||
11882 element == EL_EM_STEEL_EXIT_OPEN ||
11883 element == EL_SP_TERMINAL ||
11884 element == EL_SP_TERMINAL_ACTIVE ||
11885 element == EL_EXTRA_TIME ||
11886 element == EL_SHIELD_NORMAL ||
11887 element == EL_SHIELD_DEADLY) &&
11888 IS_ANIMATED(graphic))
11889 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11890 else if (IS_MOVING(x, y))
11891 ContinueMoving(x, y);
11892 else if (IS_ACTIVE_BOMB(element))
11893 CheckDynamite(x, y);
11894 else if (element == EL_AMOEBA_GROWING)
11895 AmoebeWaechst(x, y);
11896 else if (element == EL_AMOEBA_SHRINKING)
11897 AmoebaDisappearing(x, y);
11899 #if !USE_NEW_AMOEBA_CODE
11900 else if (IS_AMOEBALIVE(element))
11901 AmoebeAbleger(x, y);
11904 else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
11906 else if (element == EL_EXIT_CLOSED)
11908 else if (element == EL_EM_EXIT_CLOSED)
11910 else if (element == EL_STEEL_EXIT_CLOSED)
11911 CheckExitSteel(x, y);
11912 else if (element == EL_EM_STEEL_EXIT_CLOSED)
11913 CheckExitSteelEM(x, y);
11914 else if (element == EL_SP_EXIT_CLOSED)
11916 else if (element == EL_EXPANDABLE_WALL_GROWING ||
11917 element == EL_EXPANDABLE_STEELWALL_GROWING)
11918 MauerWaechst(x, y);
11919 else if (element == EL_EXPANDABLE_WALL ||
11920 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
11921 element == EL_EXPANDABLE_WALL_VERTICAL ||
11922 element == EL_EXPANDABLE_WALL_ANY ||
11923 element == EL_BD_EXPANDABLE_WALL)
11924 MauerAbleger(x, y);
11925 else if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
11926 element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
11927 element == EL_EXPANDABLE_STEELWALL_ANY)
11928 MauerAblegerStahl(x, y);
11929 else if (element == EL_FLAMES)
11930 CheckForDragon(x, y);
11931 else if (element == EL_EXPLOSION)
11932 ; /* drawing of correct explosion animation is handled separately */
11933 else if (element == EL_ELEMENT_SNAPPING ||
11934 element == EL_DIAGONAL_SHRINKING ||
11935 element == EL_DIAGONAL_GROWING)
11937 graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
11939 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11941 else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
11942 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11944 if (IS_BELT_ACTIVE(element))
11945 PlayLevelSoundAction(x, y, ACTION_ACTIVE);
11947 if (game.magic_wall_active)
11949 int jx = local_player->jx, jy = local_player->jy;
11951 /* play the element sound at the position nearest to the player */
11952 if ((element == EL_MAGIC_WALL_FULL ||
11953 element == EL_MAGIC_WALL_ACTIVE ||
11954 element == EL_MAGIC_WALL_EMPTYING ||
11955 element == EL_BD_MAGIC_WALL_FULL ||
11956 element == EL_BD_MAGIC_WALL_ACTIVE ||
11957 element == EL_BD_MAGIC_WALL_EMPTYING ||
11958 element == EL_DC_MAGIC_WALL_FULL ||
11959 element == EL_DC_MAGIC_WALL_ACTIVE ||
11960 element == EL_DC_MAGIC_WALL_EMPTYING) &&
11961 ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
11969 #if USE_NEW_AMOEBA_CODE
11970 /* new experimental amoeba growth stuff */
11971 if (!(FrameCounter % 8))
11973 static unsigned int random = 1684108901;
11975 for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
11977 x = RND(lev_fieldx);
11978 y = RND(lev_fieldy);
11979 element = Feld[x][y];
11981 if (!IS_PLAYER(x,y) &&
11982 (element == EL_EMPTY ||
11983 CAN_GROW_INTO(element) ||
11984 element == EL_QUICKSAND_EMPTY ||
11985 element == EL_QUICKSAND_FAST_EMPTY ||
11986 element == EL_ACID_SPLASH_LEFT ||
11987 element == EL_ACID_SPLASH_RIGHT))
11989 if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
11990 (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
11991 (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
11992 (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
11993 Feld[x][y] = EL_AMOEBA_DROP;
11996 random = random * 129 + 1;
12001 game.explosions_delayed = FALSE;
12003 SCAN_PLAYFIELD(x, y)
12005 element = Feld[x][y];
12007 if (ExplodeField[x][y])
12008 Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
12009 else if (element == EL_EXPLOSION)
12010 Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
12012 ExplodeField[x][y] = EX_TYPE_NONE;
12015 game.explosions_delayed = TRUE;
12017 if (game.magic_wall_active)
12019 if (!(game.magic_wall_time_left % 4))
12021 int element = Feld[magic_wall_x][magic_wall_y];
12023 if (element == EL_BD_MAGIC_WALL_FULL ||
12024 element == EL_BD_MAGIC_WALL_ACTIVE ||
12025 element == EL_BD_MAGIC_WALL_EMPTYING)
12026 PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
12027 else if (element == EL_DC_MAGIC_WALL_FULL ||
12028 element == EL_DC_MAGIC_WALL_ACTIVE ||
12029 element == EL_DC_MAGIC_WALL_EMPTYING)
12030 PlayLevelSound(magic_wall_x, magic_wall_y, SND_DC_MAGIC_WALL_ACTIVE);
12032 PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
12035 if (game.magic_wall_time_left > 0)
12037 game.magic_wall_time_left--;
12039 if (!game.magic_wall_time_left)
12041 SCAN_PLAYFIELD(x, y)
12043 element = Feld[x][y];
12045 if (element == EL_MAGIC_WALL_ACTIVE ||
12046 element == EL_MAGIC_WALL_FULL)
12048 Feld[x][y] = EL_MAGIC_WALL_DEAD;
12049 TEST_DrawLevelField(x, y);
12051 else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
12052 element == EL_BD_MAGIC_WALL_FULL)
12054 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
12055 TEST_DrawLevelField(x, y);
12057 else if (element == EL_DC_MAGIC_WALL_ACTIVE ||
12058 element == EL_DC_MAGIC_WALL_FULL)
12060 Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
12061 TEST_DrawLevelField(x, y);
12065 game.magic_wall_active = FALSE;
12070 if (game.light_time_left > 0)
12072 game.light_time_left--;
12074 if (game.light_time_left == 0)
12075 RedrawAllLightSwitchesAndInvisibleElements();
12078 if (game.timegate_time_left > 0)
12080 game.timegate_time_left--;
12082 if (game.timegate_time_left == 0)
12083 CloseAllOpenTimegates();
12086 if (game.lenses_time_left > 0)
12088 game.lenses_time_left--;
12090 if (game.lenses_time_left == 0)
12091 RedrawAllInvisibleElementsForLenses();
12094 if (game.magnify_time_left > 0)
12096 game.magnify_time_left--;
12098 if (game.magnify_time_left == 0)
12099 RedrawAllInvisibleElementsForMagnifier();
12102 for (i = 0; i < MAX_PLAYERS; i++)
12104 struct PlayerInfo *player = &stored_player[i];
12106 if (SHIELD_ON(player))
12108 if (player->shield_deadly_time_left)
12109 PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
12110 else if (player->shield_normal_time_left)
12111 PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
12115 #if USE_DELAYED_GFX_REDRAW
12116 SCAN_PLAYFIELD(x, y)
12118 if (GfxRedraw[x][y] != GFX_REDRAW_NONE)
12120 /* !!! PROBLEM: THIS REDRAWS THE PLAYFIELD _AFTER_ THE SCAN, BUT TILES
12121 !!! MAY HAVE CHANGED AFTER BEING DRAWN DURING PLAYFIELD SCAN !!! */
12123 if (GfxRedraw[x][y] & GFX_REDRAW_TILE)
12124 DrawLevelField(x, y);
12126 if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED)
12127 DrawLevelFieldCrumbled(x, y);
12129 if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS)
12130 DrawLevelFieldCrumbledNeighbours(x, y);
12132 if (GfxRedraw[x][y] & GFX_REDRAW_TILE_TWINKLED)
12133 DrawTwinkleOnField(x, y);
12136 GfxRedraw[x][y] = GFX_REDRAW_NONE;
12141 PlayAllPlayersSound();
12143 if (local_player->show_envelope != 0 && local_player->MovPos == 0)
12145 ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
12147 local_player->show_envelope = 0;
12150 /* use random number generator in every frame to make it less predictable */
12151 if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12155 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
12157 int min_x = x, min_y = y, max_x = x, max_y = y;
12160 for (i = 0; i < MAX_PLAYERS; i++)
12162 int jx = stored_player[i].jx, jy = stored_player[i].jy;
12164 if (!stored_player[i].active || &stored_player[i] == player)
12167 min_x = MIN(min_x, jx);
12168 min_y = MIN(min_y, jy);
12169 max_x = MAX(max_x, jx);
12170 max_y = MAX(max_y, jy);
12173 return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
12176 static boolean AllPlayersInVisibleScreen()
12180 for (i = 0; i < MAX_PLAYERS; i++)
12182 int jx = stored_player[i].jx, jy = stored_player[i].jy;
12184 if (!stored_player[i].active)
12187 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12194 void ScrollLevel(int dx, int dy)
12196 int scroll_offset = 2 * TILEX_VAR;
12199 BlitBitmap(drawto_field, drawto_field,
12200 FX + TILEX_VAR * (dx == -1) - scroll_offset,
12201 FY + TILEY_VAR * (dy == -1) - scroll_offset,
12202 SXSIZE - TILEX_VAR * (dx != 0) + 2 * scroll_offset,
12203 SYSIZE - TILEY_VAR * (dy != 0) + 2 * scroll_offset,
12204 FX + TILEX_VAR * (dx == 1) - scroll_offset,
12205 FY + TILEY_VAR * (dy == 1) - scroll_offset);
12209 x = (dx == 1 ? BX1 : BX2);
12210 for (y = BY1; y <= BY2; y++)
12211 DrawScreenField(x, y);
12216 y = (dy == 1 ? BY1 : BY2);
12217 for (x = BX1; x <= BX2; x++)
12218 DrawScreenField(x, y);
12221 redraw_mask |= REDRAW_FIELD;
12224 static boolean canFallDown(struct PlayerInfo *player)
12226 int jx = player->jx, jy = player->jy;
12228 return (IN_LEV_FIELD(jx, jy + 1) &&
12229 (IS_FREE(jx, jy + 1) ||
12230 (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
12231 IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
12232 !IS_WALKABLE_INSIDE(Feld[jx][jy]));
12235 static boolean canPassField(int x, int y, int move_dir)
12237 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12238 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12239 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
12240 int nextx = x + dx;
12241 int nexty = y + dy;
12242 int element = Feld[x][y];
12244 return (IS_PASSABLE_FROM(element, opposite_dir) &&
12245 !CAN_MOVE(element) &&
12246 IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
12247 IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
12248 (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
12251 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
12253 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12254 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12255 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
12259 return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
12260 IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
12261 (IS_DIGGABLE(Feld[newx][newy]) ||
12262 IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
12263 canPassField(newx, newy, move_dir)));
12266 static void CheckGravityMovement(struct PlayerInfo *player)
12268 if (player->gravity && !player->programmed_action)
12270 int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
12271 int move_dir_vertical = player->effective_action & MV_VERTICAL;
12272 boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
12273 int jx = player->jx, jy = player->jy;
12274 boolean player_is_moving_to_valid_field =
12275 (!player_is_snapping &&
12276 (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
12277 canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
12278 boolean player_can_fall_down = canFallDown(player);
12280 if (player_can_fall_down &&
12281 !player_is_moving_to_valid_field)
12282 player->programmed_action = MV_DOWN;
12286 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
12288 return CheckGravityMovement(player);
12290 if (player->gravity && !player->programmed_action)
12292 int jx = player->jx, jy = player->jy;
12293 boolean field_under_player_is_free =
12294 (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
12295 boolean player_is_standing_on_valid_field =
12296 (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
12297 (IS_WALKABLE(Feld[jx][jy]) &&
12298 !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
12300 if (field_under_player_is_free && !player_is_standing_on_valid_field)
12301 player->programmed_action = MV_DOWN;
12306 MovePlayerOneStep()
12307 -----------------------------------------------------------------------------
12308 dx, dy: direction (non-diagonal) to try to move the player to
12309 real_dx, real_dy: direction as read from input device (can be diagonal)
12312 boolean MovePlayerOneStep(struct PlayerInfo *player,
12313 int dx, int dy, int real_dx, int real_dy)
12315 int jx = player->jx, jy = player->jy;
12316 int new_jx = jx + dx, new_jy = jy + dy;
12318 boolean player_can_move = !player->cannot_move;
12320 if (!player->active || (!dx && !dy))
12321 return MP_NO_ACTION;
12323 player->MovDir = (dx < 0 ? MV_LEFT :
12324 dx > 0 ? MV_RIGHT :
12326 dy > 0 ? MV_DOWN : MV_NONE);
12328 if (!IN_LEV_FIELD(new_jx, new_jy))
12329 return MP_NO_ACTION;
12331 if (!player_can_move)
12333 if (player->MovPos == 0)
12335 player->is_moving = FALSE;
12336 player->is_digging = FALSE;
12337 player->is_collecting = FALSE;
12338 player->is_snapping = FALSE;
12339 player->is_pushing = FALSE;
12343 if (!options.network && game.centered_player_nr == -1 &&
12344 !AllPlayersInSight(player, new_jx, new_jy))
12345 return MP_NO_ACTION;
12347 can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
12348 if (can_move != MP_MOVING)
12351 /* check if DigField() has caused relocation of the player */
12352 if (player->jx != jx || player->jy != jy)
12353 return MP_NO_ACTION; /* <-- !!! CHECK THIS [-> MP_ACTION ?] !!! */
12355 StorePlayer[jx][jy] = 0;
12356 player->last_jx = jx;
12357 player->last_jy = jy;
12358 player->jx = new_jx;
12359 player->jy = new_jy;
12360 StorePlayer[new_jx][new_jy] = player->element_nr;
12362 if (player->move_delay_value_next != -1)
12364 player->move_delay_value = player->move_delay_value_next;
12365 player->move_delay_value_next = -1;
12369 (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
12371 player->step_counter++;
12373 PlayerVisit[jx][jy] = FrameCounter;
12375 player->is_moving = TRUE;
12378 /* should better be called in MovePlayer(), but this breaks some tapes */
12379 ScrollPlayer(player, SCROLL_INIT);
12385 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
12387 int jx = player->jx, jy = player->jy;
12388 int old_jx = jx, old_jy = jy;
12389 int moved = MP_NO_ACTION;
12391 if (!player->active)
12396 if (player->MovPos == 0)
12398 player->is_moving = FALSE;
12399 player->is_digging = FALSE;
12400 player->is_collecting = FALSE;
12401 player->is_snapping = FALSE;
12402 player->is_pushing = FALSE;
12408 if (player->move_delay > 0)
12411 player->move_delay = -1; /* set to "uninitialized" value */
12413 /* store if player is automatically moved to next field */
12414 player->is_auto_moving = (player->programmed_action != MV_NONE);
12416 /* remove the last programmed player action */
12417 player->programmed_action = 0;
12419 if (player->MovPos)
12421 /* should only happen if pre-1.2 tape recordings are played */
12422 /* this is only for backward compatibility */
12424 int original_move_delay_value = player->move_delay_value;
12427 printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%d]\n",
12431 /* scroll remaining steps with finest movement resolution */
12432 player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
12434 while (player->MovPos)
12436 ScrollPlayer(player, SCROLL_GO_ON);
12437 ScrollScreen(NULL, SCROLL_GO_ON);
12439 AdvanceFrameAndPlayerCounters(player->index_nr);
12442 BackToFront_WithFrameDelay(0);
12445 player->move_delay_value = original_move_delay_value;
12448 player->is_active = FALSE;
12450 if (player->last_move_dir & MV_HORIZONTAL)
12452 if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
12453 moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
12457 if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
12458 moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
12461 if (!moved && !player->is_active)
12463 player->is_moving = FALSE;
12464 player->is_digging = FALSE;
12465 player->is_collecting = FALSE;
12466 player->is_snapping = FALSE;
12467 player->is_pushing = FALSE;
12473 if (moved & MP_MOVING && !ScreenMovPos &&
12474 (player->index_nr == game.centered_player_nr ||
12475 game.centered_player_nr == -1))
12477 int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
12478 int offset = game.scroll_delay_value;
12480 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12482 /* actual player has left the screen -- scroll in that direction */
12483 if (jx != old_jx) /* player has moved horizontally */
12484 scroll_x += (jx - old_jx);
12485 else /* player has moved vertically */
12486 scroll_y += (jy - old_jy);
12490 if (jx != old_jx) /* player has moved horizontally */
12492 if ((player->MovDir == MV_LEFT && scroll_x > jx - MIDPOSX + offset) ||
12493 (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
12494 scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
12496 /* don't scroll over playfield boundaries */
12497 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
12498 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
12500 /* don't scroll more than one field at a time */
12501 scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
12503 /* don't scroll against the player's moving direction */
12504 if ((player->MovDir == MV_LEFT && scroll_x > old_scroll_x) ||
12505 (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
12506 scroll_x = old_scroll_x;
12508 else /* player has moved vertically */
12510 if ((player->MovDir == MV_UP && scroll_y > jy - MIDPOSY + offset) ||
12511 (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
12512 scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
12514 /* don't scroll over playfield boundaries */
12515 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
12516 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
12518 /* don't scroll more than one field at a time */
12519 scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
12521 /* don't scroll against the player's moving direction */
12522 if ((player->MovDir == MV_UP && scroll_y > old_scroll_y) ||
12523 (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
12524 scroll_y = old_scroll_y;
12528 if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
12530 if (!options.network && game.centered_player_nr == -1 &&
12531 !AllPlayersInVisibleScreen())
12533 scroll_x = old_scroll_x;
12534 scroll_y = old_scroll_y;
12538 ScrollScreen(player, SCROLL_INIT);
12539 ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
12544 player->StepFrame = 0;
12546 if (moved & MP_MOVING)
12548 if (old_jx != jx && old_jy == jy)
12549 player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
12550 else if (old_jx == jx && old_jy != jy)
12551 player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
12553 TEST_DrawLevelField(jx, jy); /* for "crumbled sand" */
12555 player->last_move_dir = player->MovDir;
12556 player->is_moving = TRUE;
12557 player->is_snapping = FALSE;
12558 player->is_switching = FALSE;
12559 player->is_dropping = FALSE;
12560 player->is_dropping_pressed = FALSE;
12561 player->drop_pressed_delay = 0;
12564 /* should better be called here than above, but this breaks some tapes */
12565 ScrollPlayer(player, SCROLL_INIT);
12570 CheckGravityMovementWhenNotMoving(player);
12572 player->is_moving = FALSE;
12574 /* at this point, the player is allowed to move, but cannot move right now
12575 (e.g. because of something blocking the way) -- ensure that the player
12576 is also allowed to move in the next frame (in old versions before 3.1.1,
12577 the player was forced to wait again for eight frames before next try) */
12579 if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12580 player->move_delay = 0; /* allow direct movement in the next frame */
12583 if (player->move_delay == -1) /* not yet initialized by DigField() */
12584 player->move_delay = player->move_delay_value;
12586 if (game.engine_version < VERSION_IDENT(3,0,7,0))
12588 TestIfPlayerTouchesBadThing(jx, jy);
12589 TestIfPlayerTouchesCustomElement(jx, jy);
12592 if (!player->active)
12593 RemovePlayer(player);
12598 void ScrollPlayer(struct PlayerInfo *player, int mode)
12600 int jx = player->jx, jy = player->jy;
12601 int last_jx = player->last_jx, last_jy = player->last_jy;
12602 int move_stepsize = TILEX / player->move_delay_value;
12604 if (!player->active)
12607 if (player->MovPos == 0 && mode == SCROLL_GO_ON) /* player not moving */
12610 if (mode == SCROLL_INIT)
12612 player->actual_frame_counter = FrameCounter;
12613 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
12615 if ((player->block_last_field || player->block_delay_adjustment > 0) &&
12616 Feld[last_jx][last_jy] == EL_EMPTY)
12618 int last_field_block_delay = 0; /* start with no blocking at all */
12619 int block_delay_adjustment = player->block_delay_adjustment;
12621 /* if player blocks last field, add delay for exactly one move */
12622 if (player->block_last_field)
12624 last_field_block_delay += player->move_delay_value;
12626 /* when blocking enabled, prevent moving up despite gravity */
12627 if (player->gravity && player->MovDir == MV_UP)
12628 block_delay_adjustment = -1;
12631 /* add block delay adjustment (also possible when not blocking) */
12632 last_field_block_delay += block_delay_adjustment;
12634 Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
12635 MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
12638 if (player->MovPos != 0) /* player has not yet reached destination */
12641 else if (!FrameReached(&player->actual_frame_counter, 1))
12644 if (player->MovPos != 0)
12646 player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
12647 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
12649 /* before DrawPlayer() to draw correct player graphic for this case */
12650 if (player->MovPos == 0)
12651 CheckGravityMovement(player);
12654 if (player->MovPos == 0) /* player reached destination field */
12656 if (player->move_delay_reset_counter > 0)
12658 player->move_delay_reset_counter--;
12660 if (player->move_delay_reset_counter == 0)
12662 /* continue with normal speed after quickly moving through gate */
12663 HALVE_PLAYER_SPEED(player);
12665 /* be able to make the next move without delay */
12666 player->move_delay = 0;
12670 player->last_jx = jx;
12671 player->last_jy = jy;
12673 if (Feld[jx][jy] == EL_EXIT_OPEN ||
12674 Feld[jx][jy] == EL_EM_EXIT_OPEN ||
12675 Feld[jx][jy] == EL_EM_EXIT_OPENING ||
12676 Feld[jx][jy] == EL_STEEL_EXIT_OPEN ||
12677 Feld[jx][jy] == EL_EM_STEEL_EXIT_OPEN ||
12678 Feld[jx][jy] == EL_EM_STEEL_EXIT_OPENING ||
12679 Feld[jx][jy] == EL_SP_EXIT_OPEN ||
12680 Feld[jx][jy] == EL_SP_EXIT_OPENING) /* <-- special case */
12682 DrawPlayer(player); /* needed here only to cleanup last field */
12683 RemovePlayer(player);
12685 if (local_player->friends_still_needed == 0 ||
12686 IS_SP_ELEMENT(Feld[jx][jy]))
12687 PlayerWins(player);
12690 /* this breaks one level: "machine", level 000 */
12692 int move_direction = player->MovDir;
12693 int enter_side = MV_DIR_OPPOSITE(move_direction);
12694 int leave_side = move_direction;
12695 int old_jx = last_jx;
12696 int old_jy = last_jy;
12697 int old_element = Feld[old_jx][old_jy];
12698 int new_element = Feld[jx][jy];
12700 if (IS_CUSTOM_ELEMENT(old_element))
12701 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
12703 player->index_bit, leave_side);
12705 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
12706 CE_PLAYER_LEAVES_X,
12707 player->index_bit, leave_side);
12709 if (IS_CUSTOM_ELEMENT(new_element))
12710 CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
12711 player->index_bit, enter_side);
12713 CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
12714 CE_PLAYER_ENTERS_X,
12715 player->index_bit, enter_side);
12717 CheckTriggeredElementChangeBySide(jx, jy, player->initial_element,
12718 CE_MOVE_OF_X, move_direction);
12721 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
12723 TestIfPlayerTouchesBadThing(jx, jy);
12724 TestIfPlayerTouchesCustomElement(jx, jy);
12726 /* needed because pushed element has not yet reached its destination,
12727 so it would trigger a change event at its previous field location */
12728 if (!player->is_pushing)
12729 TestIfElementTouchesCustomElement(jx, jy); /* for empty space */
12731 if (!player->active)
12732 RemovePlayer(player);
12735 if (!local_player->LevelSolved && level.use_step_counter)
12745 if (TimeLeft <= 10 && setup.time_limit)
12746 PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
12748 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
12750 DisplayGameControlValues();
12752 if (!TimeLeft && setup.time_limit)
12753 for (i = 0; i < MAX_PLAYERS; i++)
12754 KillPlayer(&stored_player[i]);
12756 else if (game.no_time_limit && !AllPlayersGone) /* level w/o time limit */
12758 game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
12760 DisplayGameControlValues();
12764 if (tape.single_step && tape.recording && !tape.pausing &&
12765 !player->programmed_action)
12766 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
12768 if (!player->programmed_action)
12769 CheckSaveEngineSnapshot(player);
12773 void ScrollScreen(struct PlayerInfo *player, int mode)
12775 static unsigned int screen_frame_counter = 0;
12777 if (mode == SCROLL_INIT)
12779 /* set scrolling step size according to actual player's moving speed */
12780 ScrollStepSize = TILEX / player->move_delay_value;
12782 screen_frame_counter = FrameCounter;
12783 ScreenMovDir = player->MovDir;
12784 ScreenMovPos = player->MovPos;
12785 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
12788 else if (!FrameReached(&screen_frame_counter, 1))
12793 ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
12794 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
12795 redraw_mask |= REDRAW_FIELD;
12798 ScreenMovDir = MV_NONE;
12801 void TestIfPlayerTouchesCustomElement(int x, int y)
12803 static int xy[4][2] =
12810 static int trigger_sides[4][2] =
12812 /* center side border side */
12813 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
12814 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
12815 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
12816 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
12818 static int touch_dir[4] =
12820 MV_LEFT | MV_RIGHT,
12825 int center_element = Feld[x][y]; /* should always be non-moving! */
12828 for (i = 0; i < NUM_DIRECTIONS; i++)
12830 int xx = x + xy[i][0];
12831 int yy = y + xy[i][1];
12832 int center_side = trigger_sides[i][0];
12833 int border_side = trigger_sides[i][1];
12834 int border_element;
12836 if (!IN_LEV_FIELD(xx, yy))
12839 if (IS_PLAYER(x, y)) /* player found at center element */
12841 struct PlayerInfo *player = PLAYERINFO(x, y);
12843 if (game.engine_version < VERSION_IDENT(3,0,7,0))
12844 border_element = Feld[xx][yy]; /* may be moving! */
12845 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
12846 border_element = Feld[xx][yy];
12847 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
12848 border_element = MovingOrBlocked2Element(xx, yy);
12850 continue; /* center and border element do not touch */
12852 CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
12853 player->index_bit, border_side);
12854 CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
12855 CE_PLAYER_TOUCHES_X,
12856 player->index_bit, border_side);
12859 /* use player element that is initially defined in the level playfield,
12860 not the player element that corresponds to the runtime player number
12861 (example: a level that contains EL_PLAYER_3 as the only player would
12862 incorrectly give EL_PLAYER_1 for "player->element_nr") */
12863 int player_element = PLAYERINFO(x, y)->initial_element;
12865 CheckElementChangeBySide(xx, yy, border_element, player_element,
12866 CE_TOUCHING_X, border_side);
12869 else if (IS_PLAYER(xx, yy)) /* player found at border element */
12871 struct PlayerInfo *player = PLAYERINFO(xx, yy);
12873 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
12875 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
12876 continue; /* center and border element do not touch */
12879 CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
12880 player->index_bit, center_side);
12881 CheckTriggeredElementChangeByPlayer(x, y, center_element,
12882 CE_PLAYER_TOUCHES_X,
12883 player->index_bit, center_side);
12886 /* use player element that is initially defined in the level playfield,
12887 not the player element that corresponds to the runtime player number
12888 (example: a level that contains EL_PLAYER_3 as the only player would
12889 incorrectly give EL_PLAYER_1 for "player->element_nr") */
12890 int player_element = PLAYERINFO(xx, yy)->initial_element;
12892 CheckElementChangeBySide(x, y, center_element, player_element,
12893 CE_TOUCHING_X, center_side);
12901 void TestIfElementTouchesCustomElement(int x, int y)
12903 static int xy[4][2] =
12910 static int trigger_sides[4][2] =
12912 /* center side border side */
12913 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
12914 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
12915 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
12916 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
12918 static int touch_dir[4] =
12920 MV_LEFT | MV_RIGHT,
12925 boolean change_center_element = FALSE;
12926 int center_element = Feld[x][y]; /* should always be non-moving! */
12927 int border_element_old[NUM_DIRECTIONS];
12930 for (i = 0; i < NUM_DIRECTIONS; i++)
12932 int xx = x + xy[i][0];
12933 int yy = y + xy[i][1];
12934 int border_element;
12936 border_element_old[i] = -1;
12938 if (!IN_LEV_FIELD(xx, yy))
12941 if (game.engine_version < VERSION_IDENT(3,0,7,0))
12942 border_element = Feld[xx][yy]; /* may be moving! */
12943 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
12944 border_element = Feld[xx][yy];
12945 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
12946 border_element = MovingOrBlocked2Element(xx, yy);
12948 continue; /* center and border element do not touch */
12950 border_element_old[i] = border_element;
12953 for (i = 0; i < NUM_DIRECTIONS; i++)
12955 int xx = x + xy[i][0];
12956 int yy = y + xy[i][1];
12957 int center_side = trigger_sides[i][0];
12958 int border_element = border_element_old[i];
12960 if (border_element == -1)
12963 /* check for change of border element */
12964 CheckElementChangeBySide(xx, yy, border_element, center_element,
12965 CE_TOUCHING_X, center_side);
12967 /* (center element cannot be player, so we dont have to check this here) */
12970 for (i = 0; i < NUM_DIRECTIONS; i++)
12972 int xx = x + xy[i][0];
12973 int yy = y + xy[i][1];
12974 int border_side = trigger_sides[i][1];
12975 int border_element = border_element_old[i];
12977 if (border_element == -1)
12980 /* check for change of center element (but change it only once) */
12981 if (!change_center_element)
12982 change_center_element =
12983 CheckElementChangeBySide(x, y, center_element, border_element,
12984 CE_TOUCHING_X, border_side);
12986 if (IS_PLAYER(xx, yy))
12988 /* use player element that is initially defined in the level playfield,
12989 not the player element that corresponds to the runtime player number
12990 (example: a level that contains EL_PLAYER_3 as the only player would
12991 incorrectly give EL_PLAYER_1 for "player->element_nr") */
12992 int player_element = PLAYERINFO(xx, yy)->initial_element;
12994 CheckElementChangeBySide(x, y, center_element, player_element,
12995 CE_TOUCHING_X, border_side);
13000 void TestIfElementHitsCustomElement(int x, int y, int direction)
13002 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
13003 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
13004 int hitx = x + dx, hity = y + dy;
13005 int hitting_element = Feld[x][y];
13006 int touched_element;
13008 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
13011 touched_element = (IN_LEV_FIELD(hitx, hity) ?
13012 MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
13014 if (IN_LEV_FIELD(hitx, hity))
13016 int opposite_direction = MV_DIR_OPPOSITE(direction);
13017 int hitting_side = direction;
13018 int touched_side = opposite_direction;
13019 boolean object_hit = (!IS_MOVING(hitx, hity) ||
13020 MovDir[hitx][hity] != direction ||
13021 ABS(MovPos[hitx][hity]) <= TILEY / 2);
13027 CheckElementChangeBySide(x, y, hitting_element, touched_element,
13028 CE_HITTING_X, touched_side);
13030 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13031 CE_HIT_BY_X, hitting_side);
13033 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13034 CE_HIT_BY_SOMETHING, opposite_direction);
13036 if (IS_PLAYER(hitx, hity))
13038 /* use player element that is initially defined in the level playfield,
13039 not the player element that corresponds to the runtime player number
13040 (example: a level that contains EL_PLAYER_3 as the only player would
13041 incorrectly give EL_PLAYER_1 for "player->element_nr") */
13042 int player_element = PLAYERINFO(hitx, hity)->initial_element;
13044 CheckElementChangeBySide(x, y, hitting_element, player_element,
13045 CE_HITTING_X, touched_side);
13050 /* "hitting something" is also true when hitting the playfield border */
13051 CheckElementChangeBySide(x, y, hitting_element, touched_element,
13052 CE_HITTING_SOMETHING, direction);
13055 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
13057 int i, kill_x = -1, kill_y = -1;
13059 int bad_element = -1;
13060 static int test_xy[4][2] =
13067 static int test_dir[4] =
13075 for (i = 0; i < NUM_DIRECTIONS; i++)
13077 int test_x, test_y, test_move_dir, test_element;
13079 test_x = good_x + test_xy[i][0];
13080 test_y = good_y + test_xy[i][1];
13082 if (!IN_LEV_FIELD(test_x, test_y))
13086 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13088 test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
13090 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13091 2nd case: DONT_TOUCH style bad thing does not move away from good thing
13093 if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
13094 (DONT_TOUCH(test_element) && test_move_dir != test_dir[i]))
13098 bad_element = test_element;
13104 if (kill_x != -1 || kill_y != -1)
13106 if (IS_PLAYER(good_x, good_y))
13108 struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
13110 if (player->shield_deadly_time_left > 0 &&
13111 !IS_INDESTRUCTIBLE(bad_element))
13112 Bang(kill_x, kill_y);
13113 else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
13114 KillPlayer(player);
13117 Bang(good_x, good_y);
13121 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
13123 int i, kill_x = -1, kill_y = -1;
13124 int bad_element = Feld[bad_x][bad_y];
13125 static int test_xy[4][2] =
13132 static int touch_dir[4] =
13134 MV_LEFT | MV_RIGHT,
13139 static int test_dir[4] =
13147 if (bad_element == EL_EXPLOSION) /* skip just exploding bad things */
13150 for (i = 0; i < NUM_DIRECTIONS; i++)
13152 int test_x, test_y, test_move_dir, test_element;
13154 test_x = bad_x + test_xy[i][0];
13155 test_y = bad_y + test_xy[i][1];
13157 if (!IN_LEV_FIELD(test_x, test_y))
13161 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13163 test_element = Feld[test_x][test_y];
13165 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13166 2nd case: DONT_TOUCH style bad thing does not move away from good thing
13168 if ((DONT_RUN_INTO(bad_element) && bad_move_dir == test_dir[i]) ||
13169 (DONT_TOUCH(bad_element) && test_move_dir != test_dir[i]))
13171 /* good thing is player or penguin that does not move away */
13172 if (IS_PLAYER(test_x, test_y))
13174 struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
13176 if (bad_element == EL_ROBOT && player->is_moving)
13177 continue; /* robot does not kill player if he is moving */
13179 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13181 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
13182 continue; /* center and border element do not touch */
13190 else if (test_element == EL_PENGUIN)
13200 if (kill_x != -1 || kill_y != -1)
13202 if (IS_PLAYER(kill_x, kill_y))
13204 struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
13206 if (player->shield_deadly_time_left > 0 &&
13207 !IS_INDESTRUCTIBLE(bad_element))
13208 Bang(bad_x, bad_y);
13209 else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
13210 KillPlayer(player);
13213 Bang(kill_x, kill_y);
13217 void TestIfGoodThingGetsHitByBadThing(int bad_x, int bad_y, int bad_move_dir)
13219 int bad_element = Feld[bad_x][bad_y];
13220 int dx = (bad_move_dir == MV_LEFT ? -1 : bad_move_dir == MV_RIGHT ? +1 : 0);
13221 int dy = (bad_move_dir == MV_UP ? -1 : bad_move_dir == MV_DOWN ? +1 : 0);
13222 int test_x = bad_x + dx, test_y = bad_y + dy;
13223 int test_move_dir, test_element;
13224 int kill_x = -1, kill_y = -1;
13226 if (!IN_LEV_FIELD(test_x, test_y))
13230 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13232 test_element = Feld[test_x][test_y];
13234 if (test_move_dir != bad_move_dir)
13236 /* good thing can be player or penguin that does not move away */
13237 if (IS_PLAYER(test_x, test_y))
13239 struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
13241 /* (note: in comparison to DONT_RUN_TO and DONT_TOUCH, also handle the
13242 player as being hit when he is moving towards the bad thing, because
13243 the "get hit by" condition would be lost after the player stops) */
13244 if (player->MovPos != 0 && player->MovDir == bad_move_dir)
13245 return; /* player moves away from bad thing */
13250 else if (test_element == EL_PENGUIN)
13257 if (kill_x != -1 || kill_y != -1)
13259 if (IS_PLAYER(kill_x, kill_y))
13261 struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
13263 if (player->shield_deadly_time_left > 0 &&
13264 !IS_INDESTRUCTIBLE(bad_element))
13265 Bang(bad_x, bad_y);
13266 else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
13267 KillPlayer(player);
13270 Bang(kill_x, kill_y);
13274 void TestIfPlayerTouchesBadThing(int x, int y)
13276 TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13279 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
13281 TestIfGoodThingHitsBadThing(x, y, move_dir);
13284 void TestIfBadThingTouchesPlayer(int x, int y)
13286 TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13289 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
13291 TestIfBadThingHitsGoodThing(x, y, move_dir);
13294 void TestIfFriendTouchesBadThing(int x, int y)
13296 TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13299 void TestIfBadThingTouchesFriend(int x, int y)
13301 TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13304 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
13306 int i, kill_x = bad_x, kill_y = bad_y;
13307 static int xy[4][2] =
13315 for (i = 0; i < NUM_DIRECTIONS; i++)
13319 x = bad_x + xy[i][0];
13320 y = bad_y + xy[i][1];
13321 if (!IN_LEV_FIELD(x, y))
13324 element = Feld[x][y];
13325 if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
13326 element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
13334 if (kill_x != bad_x || kill_y != bad_y)
13335 Bang(bad_x, bad_y);
13338 void KillPlayer(struct PlayerInfo *player)
13340 int jx = player->jx, jy = player->jy;
13342 if (!player->active)
13346 printf("::: 0: killed == %d, active == %d, reanimated == %d\n",
13347 player->killed, player->active, player->reanimated);
13350 /* the following code was introduced to prevent an infinite loop when calling
13352 -> CheckTriggeredElementChangeExt()
13353 -> ExecuteCustomElementAction()
13355 -> (infinitely repeating the above sequence of function calls)
13356 which occurs when killing the player while having a CE with the setting
13357 "kill player X when explosion of <player X>"; the solution using a new
13358 field "player->killed" was chosen for backwards compatibility, although
13359 clever use of the fields "player->active" etc. would probably also work */
13361 if (player->killed)
13365 player->killed = TRUE;
13367 /* remove accessible field at the player's position */
13368 Feld[jx][jy] = EL_EMPTY;
13370 /* deactivate shield (else Bang()/Explode() would not work right) */
13371 player->shield_normal_time_left = 0;
13372 player->shield_deadly_time_left = 0;
13375 printf("::: 1: killed == %d, active == %d, reanimated == %d\n",
13376 player->killed, player->active, player->reanimated);
13382 printf("::: 2: killed == %d, active == %d, reanimated == %d\n",
13383 player->killed, player->active, player->reanimated);
13386 if (player->reanimated) /* killed player may have been reanimated */
13387 player->killed = player->reanimated = FALSE;
13389 BuryPlayer(player);
13392 static void KillPlayerUnlessEnemyProtected(int x, int y)
13394 if (!PLAYER_ENEMY_PROTECTED(x, y))
13395 KillPlayer(PLAYERINFO(x, y));
13398 static void KillPlayerUnlessExplosionProtected(int x, int y)
13400 if (!PLAYER_EXPLOSION_PROTECTED(x, y))
13401 KillPlayer(PLAYERINFO(x, y));
13404 void BuryPlayer(struct PlayerInfo *player)
13406 int jx = player->jx, jy = player->jy;
13408 if (!player->active)
13411 PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
13412 PlayLevelSound(jx, jy, SND_GAME_LOSING);
13414 player->GameOver = TRUE;
13415 RemovePlayer(player);
13418 void RemovePlayer(struct PlayerInfo *player)
13420 int jx = player->jx, jy = player->jy;
13421 int i, found = FALSE;
13423 player->present = FALSE;
13424 player->active = FALSE;
13426 if (!ExplodeField[jx][jy])
13427 StorePlayer[jx][jy] = 0;
13429 if (player->is_moving)
13430 TEST_DrawLevelField(player->last_jx, player->last_jy);
13432 for (i = 0; i < MAX_PLAYERS; i++)
13433 if (stored_player[i].active)
13437 AllPlayersGone = TRUE;
13443 static void setFieldForSnapping(int x, int y, int element, int direction)
13445 struct ElementInfo *ei = &element_info[element];
13446 int direction_bit = MV_DIR_TO_BIT(direction);
13447 int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
13448 int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
13449 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
13451 Feld[x][y] = EL_ELEMENT_SNAPPING;
13452 MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
13454 ResetGfxAnimation(x, y);
13456 GfxElement[x][y] = element;
13457 GfxAction[x][y] = action;
13458 GfxDir[x][y] = direction;
13459 GfxFrame[x][y] = -1;
13463 =============================================================================
13464 checkDiagonalPushing()
13465 -----------------------------------------------------------------------------
13466 check if diagonal input device direction results in pushing of object
13467 (by checking if the alternative direction is walkable, diggable, ...)
13468 =============================================================================
13471 static boolean checkDiagonalPushing(struct PlayerInfo *player,
13472 int x, int y, int real_dx, int real_dy)
13474 int jx, jy, dx, dy, xx, yy;
13476 if (real_dx == 0 || real_dy == 0) /* no diagonal direction => push */
13479 /* diagonal direction: check alternative direction */
13484 xx = jx + (dx == 0 ? real_dx : 0);
13485 yy = jy + (dy == 0 ? real_dy : 0);
13487 return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
13491 =============================================================================
13493 -----------------------------------------------------------------------------
13494 x, y: field next to player (non-diagonal) to try to dig to
13495 real_dx, real_dy: direction as read from input device (can be diagonal)
13496 =============================================================================
13499 static int DigField(struct PlayerInfo *player,
13500 int oldx, int oldy, int x, int y,
13501 int real_dx, int real_dy, int mode)
13503 boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
13504 boolean player_was_pushing = player->is_pushing;
13505 boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
13506 boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
13507 int jx = oldx, jy = oldy;
13508 int dx = x - jx, dy = y - jy;
13509 int nextx = x + dx, nexty = y + dy;
13510 int move_direction = (dx == -1 ? MV_LEFT :
13511 dx == +1 ? MV_RIGHT :
13513 dy == +1 ? MV_DOWN : MV_NONE);
13514 int opposite_direction = MV_DIR_OPPOSITE(move_direction);
13515 int dig_side = MV_DIR_OPPOSITE(move_direction);
13516 int old_element = Feld[jx][jy];
13517 int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
13520 if (is_player) /* function can also be called by EL_PENGUIN */
13522 if (player->MovPos == 0)
13524 player->is_digging = FALSE;
13525 player->is_collecting = FALSE;
13528 if (player->MovPos == 0) /* last pushing move finished */
13529 player->is_pushing = FALSE;
13531 if (mode == DF_NO_PUSH) /* player just stopped pushing */
13533 player->is_switching = FALSE;
13534 player->push_delay = -1;
13536 return MP_NO_ACTION;
13540 if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
13541 old_element = Back[jx][jy];
13543 /* in case of element dropped at player position, check background */
13544 else if (Back[jx][jy] != EL_EMPTY &&
13545 game.engine_version >= VERSION_IDENT(2,2,0,0))
13546 old_element = Back[jx][jy];
13548 if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
13549 return MP_NO_ACTION; /* field has no opening in this direction */
13551 if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
13552 return MP_NO_ACTION; /* field has no opening in this direction */
13554 if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
13558 Feld[jx][jy] = player->artwork_element;
13559 InitMovingField(jx, jy, MV_DOWN);
13560 Store[jx][jy] = EL_ACID;
13561 ContinueMoving(jx, jy);
13562 BuryPlayer(player);
13564 return MP_DONT_RUN_INTO;
13567 if (player_can_move && DONT_RUN_INTO(element))
13569 TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
13571 return MP_DONT_RUN_INTO;
13574 if (IS_MOVING(x, y) || IS_PLAYER(x, y))
13575 return MP_NO_ACTION;
13577 collect_count = element_info[element].collect_count_initial;
13579 if (!is_player && !IS_COLLECTIBLE(element)) /* penguin cannot collect it */
13580 return MP_NO_ACTION;
13582 if (game.engine_version < VERSION_IDENT(2,2,0,0))
13583 player_can_move = player_can_move_or_snap;
13585 if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
13586 game.engine_version >= VERSION_IDENT(2,2,0,0))
13588 CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
13589 player->index_bit, dig_side);
13590 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13591 player->index_bit, dig_side);
13593 if (element == EL_DC_LANDMINE)
13596 if (Feld[x][y] != element) /* field changed by snapping */
13599 return MP_NO_ACTION;
13602 if (player->gravity && is_player && !player->is_auto_moving &&
13603 canFallDown(player) && move_direction != MV_DOWN &&
13604 !canMoveToValidFieldWithGravity(jx, jy, move_direction))
13605 return MP_NO_ACTION; /* player cannot walk here due to gravity */
13607 if (player_can_move &&
13608 IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
13610 int sound_element = SND_ELEMENT(element);
13611 int sound_action = ACTION_WALKING;
13613 if (IS_RND_GATE(element))
13615 if (!player->key[RND_GATE_NR(element)])
13616 return MP_NO_ACTION;
13618 else if (IS_RND_GATE_GRAY(element))
13620 if (!player->key[RND_GATE_GRAY_NR(element)])
13621 return MP_NO_ACTION;
13623 else if (IS_RND_GATE_GRAY_ACTIVE(element))
13625 if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
13626 return MP_NO_ACTION;
13628 else if (element == EL_EXIT_OPEN ||
13629 element == EL_EM_EXIT_OPEN ||
13630 element == EL_EM_EXIT_OPENING ||
13631 element == EL_STEEL_EXIT_OPEN ||
13632 element == EL_EM_STEEL_EXIT_OPEN ||
13633 element == EL_EM_STEEL_EXIT_OPENING ||
13634 element == EL_SP_EXIT_OPEN ||
13635 element == EL_SP_EXIT_OPENING)
13637 sound_action = ACTION_PASSING; /* player is passing exit */
13639 else if (element == EL_EMPTY)
13641 sound_action = ACTION_MOVING; /* nothing to walk on */
13644 /* play sound from background or player, whatever is available */
13645 if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
13646 PlayLevelSoundElementAction(x, y, sound_element, sound_action);
13648 PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
13650 else if (player_can_move &&
13651 IS_PASSABLE(element) && canPassField(x, y, move_direction))
13653 if (!ACCESS_FROM(element, opposite_direction))
13654 return MP_NO_ACTION; /* field not accessible from this direction */
13656 if (CAN_MOVE(element)) /* only fixed elements can be passed! */
13657 return MP_NO_ACTION;
13659 if (IS_EM_GATE(element))
13661 if (!player->key[EM_GATE_NR(element)])
13662 return MP_NO_ACTION;
13664 else if (IS_EM_GATE_GRAY(element))
13666 if (!player->key[EM_GATE_GRAY_NR(element)])
13667 return MP_NO_ACTION;
13669 else if (IS_EM_GATE_GRAY_ACTIVE(element))
13671 if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
13672 return MP_NO_ACTION;
13674 else if (IS_EMC_GATE(element))
13676 if (!player->key[EMC_GATE_NR(element)])
13677 return MP_NO_ACTION;
13679 else if (IS_EMC_GATE_GRAY(element))
13681 if (!player->key[EMC_GATE_GRAY_NR(element)])
13682 return MP_NO_ACTION;
13684 else if (IS_EMC_GATE_GRAY_ACTIVE(element))
13686 if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
13687 return MP_NO_ACTION;
13689 else if (element == EL_DC_GATE_WHITE ||
13690 element == EL_DC_GATE_WHITE_GRAY ||
13691 element == EL_DC_GATE_WHITE_GRAY_ACTIVE)
13693 if (player->num_white_keys == 0)
13694 return MP_NO_ACTION;
13696 player->num_white_keys--;
13698 else if (IS_SP_PORT(element))
13700 if (element == EL_SP_GRAVITY_PORT_LEFT ||
13701 element == EL_SP_GRAVITY_PORT_RIGHT ||
13702 element == EL_SP_GRAVITY_PORT_UP ||
13703 element == EL_SP_GRAVITY_PORT_DOWN)
13704 player->gravity = !player->gravity;
13705 else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
13706 element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
13707 element == EL_SP_GRAVITY_ON_PORT_UP ||
13708 element == EL_SP_GRAVITY_ON_PORT_DOWN)
13709 player->gravity = TRUE;
13710 else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
13711 element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
13712 element == EL_SP_GRAVITY_OFF_PORT_UP ||
13713 element == EL_SP_GRAVITY_OFF_PORT_DOWN)
13714 player->gravity = FALSE;
13717 /* automatically move to the next field with double speed */
13718 player->programmed_action = move_direction;
13720 if (player->move_delay_reset_counter == 0)
13722 player->move_delay_reset_counter = 2; /* two double speed steps */
13724 DOUBLE_PLAYER_SPEED(player);
13727 PlayLevelSoundAction(x, y, ACTION_PASSING);
13729 else if (player_can_move_or_snap && IS_DIGGABLE(element))
13733 if (mode != DF_SNAP)
13735 GfxElement[x][y] = GFX_ELEMENT(element);
13736 player->is_digging = TRUE;
13739 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
13741 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
13742 player->index_bit, dig_side);
13744 if (mode == DF_SNAP)
13746 if (level.block_snap_field)
13747 setFieldForSnapping(x, y, element, move_direction);
13749 TestIfElementTouchesCustomElement(x, y); /* for empty space */
13751 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13752 player->index_bit, dig_side);
13755 else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
13759 if (is_player && mode != DF_SNAP)
13761 GfxElement[x][y] = element;
13762 player->is_collecting = TRUE;
13765 if (element == EL_SPEED_PILL)
13767 player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
13769 else if (element == EL_EXTRA_TIME && level.time > 0)
13771 TimeLeft += level.extra_time;
13773 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
13775 DisplayGameControlValues();
13777 else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
13779 player->shield_normal_time_left += level.shield_normal_time;
13780 if (element == EL_SHIELD_DEADLY)
13781 player->shield_deadly_time_left += level.shield_deadly_time;
13783 else if (element == EL_DYNAMITE ||
13784 element == EL_EM_DYNAMITE ||
13785 element == EL_SP_DISK_RED)
13787 if (player->inventory_size < MAX_INVENTORY_SIZE)
13788 player->inventory_element[player->inventory_size++] = element;
13790 DrawGameDoorValues();
13792 else if (element == EL_DYNABOMB_INCREASE_NUMBER)
13794 player->dynabomb_count++;
13795 player->dynabombs_left++;
13797 else if (element == EL_DYNABOMB_INCREASE_SIZE)
13799 player->dynabomb_size++;
13801 else if (element == EL_DYNABOMB_INCREASE_POWER)
13803 player->dynabomb_xl = TRUE;
13805 else if (IS_KEY(element))
13807 player->key[KEY_NR(element)] = TRUE;
13809 DrawGameDoorValues();
13811 else if (element == EL_DC_KEY_WHITE)
13813 player->num_white_keys++;
13815 /* display white keys? */
13816 /* DrawGameDoorValues(); */
13818 else if (IS_ENVELOPE(element))
13820 player->show_envelope = element;
13822 else if (element == EL_EMC_LENSES)
13824 game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
13826 RedrawAllInvisibleElementsForLenses();
13828 else if (element == EL_EMC_MAGNIFIER)
13830 game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
13832 RedrawAllInvisibleElementsForMagnifier();
13834 else if (IS_DROPPABLE(element) ||
13835 IS_THROWABLE(element)) /* can be collected and dropped */
13839 if (collect_count == 0)
13840 player->inventory_infinite_element = element;
13842 for (i = 0; i < collect_count; i++)
13843 if (player->inventory_size < MAX_INVENTORY_SIZE)
13844 player->inventory_element[player->inventory_size++] = element;
13846 DrawGameDoorValues();
13848 else if (collect_count > 0)
13850 local_player->gems_still_needed -= collect_count;
13851 if (local_player->gems_still_needed < 0)
13852 local_player->gems_still_needed = 0;
13854 game.snapshot.collected_item = TRUE;
13856 game_panel_controls[GAME_PANEL_GEMS].value = local_player->gems_still_needed;
13858 DisplayGameControlValues();
13861 RaiseScoreElement(element);
13862 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
13865 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
13866 player->index_bit, dig_side);
13868 if (mode == DF_SNAP)
13870 if (level.block_snap_field)
13871 setFieldForSnapping(x, y, element, move_direction);
13873 TestIfElementTouchesCustomElement(x, y); /* for empty space */
13875 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13876 player->index_bit, dig_side);
13879 else if (player_can_move_or_snap && IS_PUSHABLE(element))
13881 if (mode == DF_SNAP && element != EL_BD_ROCK)
13882 return MP_NO_ACTION;
13884 if (CAN_FALL(element) && dy)
13885 return MP_NO_ACTION;
13887 if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
13888 !(element == EL_SPRING && level.use_spring_bug))
13889 return MP_NO_ACTION;
13891 if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
13892 ((move_direction & MV_VERTICAL &&
13893 ((element_info[element].move_pattern & MV_LEFT &&
13894 IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
13895 (element_info[element].move_pattern & MV_RIGHT &&
13896 IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
13897 (move_direction & MV_HORIZONTAL &&
13898 ((element_info[element].move_pattern & MV_UP &&
13899 IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
13900 (element_info[element].move_pattern & MV_DOWN &&
13901 IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
13902 return MP_NO_ACTION;
13904 /* do not push elements already moving away faster than player */
13905 if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
13906 ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
13907 return MP_NO_ACTION;
13909 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
13911 if (player->push_delay_value == -1 || !player_was_pushing)
13912 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13914 else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
13916 if (player->push_delay_value == -1)
13917 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13919 else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
13921 if (!player->is_pushing)
13922 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13925 player->is_pushing = TRUE;
13926 player->is_active = TRUE;
13928 if (!(IN_LEV_FIELD(nextx, nexty) &&
13929 (IS_FREE(nextx, nexty) ||
13930 (IS_SB_ELEMENT(element) &&
13931 Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY) ||
13932 (IS_CUSTOM_ELEMENT(element) &&
13933 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty)))))
13934 return MP_NO_ACTION;
13936 if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
13937 return MP_NO_ACTION;
13939 if (player->push_delay == -1) /* new pushing; restart delay */
13940 player->push_delay = 0;
13942 if (player->push_delay < player->push_delay_value &&
13943 !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
13944 element != EL_SPRING && element != EL_BALLOON)
13946 /* make sure that there is no move delay before next try to push */
13947 if (game.engine_version >= VERSION_IDENT(3,0,7,1))
13948 player->move_delay = 0;
13950 return MP_NO_ACTION;
13953 if (IS_CUSTOM_ELEMENT(element) &&
13954 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty))
13956 if (!DigFieldByCE(nextx, nexty, element))
13957 return MP_NO_ACTION;
13960 if (IS_SB_ELEMENT(element))
13962 if (element == EL_SOKOBAN_FIELD_FULL)
13964 Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
13965 local_player->sokobanfields_still_needed++;
13968 if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
13970 Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
13971 local_player->sokobanfields_still_needed--;
13974 Feld[x][y] = EL_SOKOBAN_OBJECT;
13976 if (Back[x][y] == Back[nextx][nexty])
13977 PlayLevelSoundAction(x, y, ACTION_PUSHING);
13978 else if (Back[x][y] != 0)
13979 PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
13982 PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
13985 if (local_player->sokobanfields_still_needed == 0 &&
13986 (game.emulation == EMU_SOKOBAN || level.auto_exit_sokoban))
13988 PlayerWins(player);
13990 PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
13994 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
13996 InitMovingField(x, y, move_direction);
13997 GfxAction[x][y] = ACTION_PUSHING;
13999 if (mode == DF_SNAP)
14000 ContinueMoving(x, y);
14002 MovPos[x][y] = (dx != 0 ? dx : dy);
14004 Pushed[x][y] = TRUE;
14005 Pushed[nextx][nexty] = TRUE;
14007 if (game.engine_version < VERSION_IDENT(2,2,0,7))
14008 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14010 player->push_delay_value = -1; /* get new value later */
14012 /* check for element change _after_ element has been pushed */
14013 if (game.use_change_when_pushing_bug)
14015 CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
14016 player->index_bit, dig_side);
14017 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
14018 player->index_bit, dig_side);
14021 else if (IS_SWITCHABLE(element))
14023 if (PLAYER_SWITCHING(player, x, y))
14025 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14026 player->index_bit, dig_side);
14031 player->is_switching = TRUE;
14032 player->switch_x = x;
14033 player->switch_y = y;
14035 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
14037 if (element == EL_ROBOT_WHEEL)
14039 Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
14043 game.robot_wheel_active = TRUE;
14045 TEST_DrawLevelField(x, y);
14047 else if (element == EL_SP_TERMINAL)
14051 SCAN_PLAYFIELD(xx, yy)
14053 if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
14057 else if (Feld[xx][yy] == EL_SP_TERMINAL)
14059 Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
14061 ResetGfxAnimation(xx, yy);
14062 TEST_DrawLevelField(xx, yy);
14066 else if (IS_BELT_SWITCH(element))
14068 ToggleBeltSwitch(x, y);
14070 else if (element == EL_SWITCHGATE_SWITCH_UP ||
14071 element == EL_SWITCHGATE_SWITCH_DOWN ||
14072 element == EL_DC_SWITCHGATE_SWITCH_UP ||
14073 element == EL_DC_SWITCHGATE_SWITCH_DOWN)
14075 ToggleSwitchgateSwitch(x, y);
14077 else if (element == EL_LIGHT_SWITCH ||
14078 element == EL_LIGHT_SWITCH_ACTIVE)
14080 ToggleLightSwitch(x, y);
14082 else if (element == EL_TIMEGATE_SWITCH ||
14083 element == EL_DC_TIMEGATE_SWITCH)
14085 ActivateTimegateSwitch(x, y);
14087 else if (element == EL_BALLOON_SWITCH_LEFT ||
14088 element == EL_BALLOON_SWITCH_RIGHT ||
14089 element == EL_BALLOON_SWITCH_UP ||
14090 element == EL_BALLOON_SWITCH_DOWN ||
14091 element == EL_BALLOON_SWITCH_NONE ||
14092 element == EL_BALLOON_SWITCH_ANY)
14094 game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT ? MV_LEFT :
14095 element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
14096 element == EL_BALLOON_SWITCH_UP ? MV_UP :
14097 element == EL_BALLOON_SWITCH_DOWN ? MV_DOWN :
14098 element == EL_BALLOON_SWITCH_NONE ? MV_NONE :
14101 else if (element == EL_LAMP)
14103 Feld[x][y] = EL_LAMP_ACTIVE;
14104 local_player->lights_still_needed--;
14106 ResetGfxAnimation(x, y);
14107 TEST_DrawLevelField(x, y);
14109 else if (element == EL_TIME_ORB_FULL)
14111 Feld[x][y] = EL_TIME_ORB_EMPTY;
14113 if (level.time > 0 || level.use_time_orb_bug)
14115 TimeLeft += level.time_orb_time;
14116 game.no_time_limit = FALSE;
14118 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
14120 DisplayGameControlValues();
14123 ResetGfxAnimation(x, y);
14124 TEST_DrawLevelField(x, y);
14126 else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
14127 element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14131 game.ball_state = !game.ball_state;
14133 SCAN_PLAYFIELD(xx, yy)
14135 int e = Feld[xx][yy];
14137 if (game.ball_state)
14139 if (e == EL_EMC_MAGIC_BALL)
14140 CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
14141 else if (e == EL_EMC_MAGIC_BALL_SWITCH)
14142 CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
14146 if (e == EL_EMC_MAGIC_BALL_ACTIVE)
14147 CreateField(xx, yy, EL_EMC_MAGIC_BALL);
14148 else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14149 CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
14154 CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14155 player->index_bit, dig_side);
14157 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14158 player->index_bit, dig_side);
14160 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14161 player->index_bit, dig_side);
14167 if (!PLAYER_SWITCHING(player, x, y))
14169 player->is_switching = TRUE;
14170 player->switch_x = x;
14171 player->switch_y = y;
14173 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
14174 player->index_bit, dig_side);
14175 CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14176 player->index_bit, dig_side);
14178 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
14179 player->index_bit, dig_side);
14180 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14181 player->index_bit, dig_side);
14184 CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
14185 player->index_bit, dig_side);
14186 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14187 player->index_bit, dig_side);
14189 return MP_NO_ACTION;
14192 player->push_delay = -1;
14194 if (is_player) /* function can also be called by EL_PENGUIN */
14196 if (Feld[x][y] != element) /* really digged/collected something */
14198 player->is_collecting = !player->is_digging;
14199 player->is_active = TRUE;
14206 static boolean DigFieldByCE(int x, int y, int digging_element)
14208 int element = Feld[x][y];
14210 if (!IS_FREE(x, y))
14212 int action = (IS_DIGGABLE(element) ? ACTION_DIGGING :
14213 IS_COLLECTIBLE(element) ? ACTION_COLLECTING :
14216 /* no element can dig solid indestructible elements */
14217 if (IS_INDESTRUCTIBLE(element) &&
14218 !IS_DIGGABLE(element) &&
14219 !IS_COLLECTIBLE(element))
14222 if (AmoebaNr[x][y] &&
14223 (element == EL_AMOEBA_FULL ||
14224 element == EL_BD_AMOEBA ||
14225 element == EL_AMOEBA_GROWING))
14227 AmoebaCnt[AmoebaNr[x][y]]--;
14228 AmoebaCnt2[AmoebaNr[x][y]]--;
14231 if (IS_MOVING(x, y))
14232 RemoveMovingField(x, y);
14236 TEST_DrawLevelField(x, y);
14239 /* if digged element was about to explode, prevent the explosion */
14240 ExplodeField[x][y] = EX_TYPE_NONE;
14242 PlayLevelSoundAction(x, y, action);
14245 Store[x][y] = EL_EMPTY;
14247 /* this makes it possible to leave the removed element again */
14248 if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element)))
14249 Store[x][y] = element;
14254 static boolean SnapField(struct PlayerInfo *player, int dx, int dy)
14256 int jx = player->jx, jy = player->jy;
14257 int x = jx + dx, y = jy + dy;
14258 int snap_direction = (dx == -1 ? MV_LEFT :
14259 dx == +1 ? MV_RIGHT :
14261 dy == +1 ? MV_DOWN : MV_NONE);
14262 boolean can_continue_snapping = (level.continuous_snapping &&
14263 WasJustFalling[x][y] < CHECK_DELAY_FALLING);
14265 if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
14268 if (!player->active || !IN_LEV_FIELD(x, y))
14276 if (player->MovPos == 0)
14277 player->is_pushing = FALSE;
14279 player->is_snapping = FALSE;
14281 if (player->MovPos == 0)
14283 player->is_moving = FALSE;
14284 player->is_digging = FALSE;
14285 player->is_collecting = FALSE;
14291 /* prevent snapping with already pressed snap key when not allowed */
14292 if (player->is_snapping && !can_continue_snapping)
14295 player->MovDir = snap_direction;
14297 if (player->MovPos == 0)
14299 player->is_moving = FALSE;
14300 player->is_digging = FALSE;
14301 player->is_collecting = FALSE;
14304 player->is_dropping = FALSE;
14305 player->is_dropping_pressed = FALSE;
14306 player->drop_pressed_delay = 0;
14308 if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
14311 player->is_snapping = TRUE;
14312 player->is_active = TRUE;
14314 if (player->MovPos == 0)
14316 player->is_moving = FALSE;
14317 player->is_digging = FALSE;
14318 player->is_collecting = FALSE;
14321 if (player->MovPos != 0) /* prevent graphic bugs in versions < 2.2.0 */
14322 TEST_DrawLevelField(player->last_jx, player->last_jy);
14324 TEST_DrawLevelField(x, y);
14329 static boolean DropElement(struct PlayerInfo *player)
14331 int old_element, new_element;
14332 int dropx = player->jx, dropy = player->jy;
14333 int drop_direction = player->MovDir;
14334 int drop_side = drop_direction;
14335 int drop_element = get_next_dropped_element(player);
14337 /* do not drop an element on top of another element; when holding drop key
14338 pressed without moving, dropped element must move away before the next
14339 element can be dropped (this is especially important if the next element
14340 is dynamite, which can be placed on background for historical reasons) */
14341 if (PLAYER_DROPPING(player, dropx, dropy) && Feld[dropx][dropy] != EL_EMPTY)
14344 if (IS_THROWABLE(drop_element))
14346 dropx += GET_DX_FROM_DIR(drop_direction);
14347 dropy += GET_DY_FROM_DIR(drop_direction);
14349 if (!IN_LEV_FIELD(dropx, dropy))
14353 old_element = Feld[dropx][dropy]; /* old element at dropping position */
14354 new_element = drop_element; /* default: no change when dropping */
14356 /* check if player is active, not moving and ready to drop */
14357 if (!player->active || player->MovPos || player->drop_delay > 0)
14360 /* check if player has anything that can be dropped */
14361 if (new_element == EL_UNDEFINED)
14364 /* only set if player has anything that can be dropped */
14365 player->is_dropping_pressed = TRUE;
14367 /* check if drop key was pressed long enough for EM style dynamite */
14368 if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
14371 /* check if anything can be dropped at the current position */
14372 if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
14375 /* collected custom elements can only be dropped on empty fields */
14376 if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
14379 if (old_element != EL_EMPTY)
14380 Back[dropx][dropy] = old_element; /* store old element on this field */
14382 ResetGfxAnimation(dropx, dropy);
14383 ResetRandomAnimationValue(dropx, dropy);
14385 if (player->inventory_size > 0 ||
14386 player->inventory_infinite_element != EL_UNDEFINED)
14388 if (player->inventory_size > 0)
14390 player->inventory_size--;
14392 DrawGameDoorValues();
14394 if (new_element == EL_DYNAMITE)
14395 new_element = EL_DYNAMITE_ACTIVE;
14396 else if (new_element == EL_EM_DYNAMITE)
14397 new_element = EL_EM_DYNAMITE_ACTIVE;
14398 else if (new_element == EL_SP_DISK_RED)
14399 new_element = EL_SP_DISK_RED_ACTIVE;
14402 Feld[dropx][dropy] = new_element;
14404 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
14405 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
14406 el2img(Feld[dropx][dropy]), 0);
14408 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
14410 /* needed if previous element just changed to "empty" in the last frame */
14411 ChangeCount[dropx][dropy] = 0; /* allow at least one more change */
14413 CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
14414 player->index_bit, drop_side);
14415 CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
14417 player->index_bit, drop_side);
14419 TestIfElementTouchesCustomElement(dropx, dropy);
14421 else /* player is dropping a dyna bomb */
14423 player->dynabombs_left--;
14425 Feld[dropx][dropy] = new_element;
14427 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
14428 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
14429 el2img(Feld[dropx][dropy]), 0);
14431 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
14434 if (Feld[dropx][dropy] == new_element) /* uninitialized unless CE change */
14435 InitField_WithBug1(dropx, dropy, FALSE);
14437 new_element = Feld[dropx][dropy]; /* element might have changed */
14439 if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
14440 element_info[new_element].move_pattern == MV_WHEN_DROPPED)
14442 if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
14443 MovDir[dropx][dropy] = drop_direction;
14445 ChangeCount[dropx][dropy] = 0; /* allow at least one more change */
14447 /* do not cause impact style collision by dropping elements that can fall */
14448 CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
14451 player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
14452 player->is_dropping = TRUE;
14454 player->drop_pressed_delay = 0;
14455 player->is_dropping_pressed = FALSE;
14457 player->drop_x = dropx;
14458 player->drop_y = dropy;
14463 /* ------------------------------------------------------------------------- */
14464 /* game sound playing functions */
14465 /* ------------------------------------------------------------------------- */
14467 static int *loop_sound_frame = NULL;
14468 static int *loop_sound_volume = NULL;
14470 void InitPlayLevelSound()
14472 int num_sounds = getSoundListSize();
14474 checked_free(loop_sound_frame);
14475 checked_free(loop_sound_volume);
14477 loop_sound_frame = checked_calloc(num_sounds * sizeof(int));
14478 loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
14481 static void PlayLevelSound(int x, int y, int nr)
14483 int sx = SCREENX(x), sy = SCREENY(y);
14484 int volume, stereo_position;
14485 int max_distance = 8;
14486 int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
14488 if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
14489 (!setup.sound_loops && IS_LOOP_SOUND(nr)))
14492 if (!IN_LEV_FIELD(x, y) ||
14493 sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
14494 sy < -max_distance || sy >= SCR_FIELDY + max_distance)
14497 volume = SOUND_MAX_VOLUME;
14499 if (!IN_SCR_FIELD(sx, sy))
14501 int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
14502 int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
14504 volume -= volume * (dx > dy ? dx : dy) / max_distance;
14507 stereo_position = (SOUND_MAX_LEFT +
14508 (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
14509 (SCR_FIELDX + 2 * max_distance));
14511 if (IS_LOOP_SOUND(nr))
14513 /* This assures that quieter loop sounds do not overwrite louder ones,
14514 while restarting sound volume comparison with each new game frame. */
14516 if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
14519 loop_sound_volume[nr] = volume;
14520 loop_sound_frame[nr] = FrameCounter;
14523 PlaySoundExt(nr, volume, stereo_position, type);
14526 static void PlayLevelSoundNearest(int x, int y, int sound_action)
14528 PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
14529 x > LEVELX(BX2) ? LEVELX(BX2) : x,
14530 y < LEVELY(BY1) ? LEVELY(BY1) :
14531 y > LEVELY(BY2) ? LEVELY(BY2) : y,
14535 static void PlayLevelSoundAction(int x, int y, int action)
14537 PlayLevelSoundElementAction(x, y, Feld[x][y], action);
14540 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
14542 int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
14544 if (sound_effect != SND_UNDEFINED)
14545 PlayLevelSound(x, y, sound_effect);
14548 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
14551 int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
14553 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14554 PlayLevelSound(x, y, sound_effect);
14557 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
14559 int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
14561 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14562 PlayLevelSound(x, y, sound_effect);
14565 static void StopLevelSoundActionIfLoop(int x, int y, int action)
14567 int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
14569 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14570 StopSound(sound_effect);
14573 static int getLevelMusicNr()
14575 if (levelset.music[level_nr] != MUS_UNDEFINED)
14576 return levelset.music[level_nr]; /* from config file */
14578 return MAP_NOCONF_MUSIC(level_nr); /* from music dir */
14581 static void FadeLevelSounds()
14586 static void FadeLevelMusic()
14588 int music_nr = getLevelMusicNr();
14589 char *curr_music = getCurrentlyPlayingMusicFilename();
14590 char *next_music = getMusicInfoEntryFilename(music_nr);
14592 if (!strEqual(curr_music, next_music))
14596 void FadeLevelSoundsAndMusic()
14602 static void PlayLevelMusic()
14604 int music_nr = getLevelMusicNr();
14605 char *curr_music = getCurrentlyPlayingMusicFilename();
14606 char *next_music = getMusicInfoEntryFilename(music_nr);
14608 if (!strEqual(curr_music, next_music))
14609 PlayMusic(music_nr);
14612 void PlayLevelSound_EM(int xx, int yy, int element_em, int sample)
14614 int element = (element_em > -1 ? map_element_EM_to_RND(element_em) : 0);
14615 int offset = (BorderElement == EL_STEELWALL ? 1 : 0);
14616 int x = xx - 1 - offset;
14617 int y = yy - 1 - offset;
14622 PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
14626 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14630 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14634 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14638 PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
14642 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14646 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14649 case SAMPLE_android_clone:
14650 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14653 case SAMPLE_android_move:
14654 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14657 case SAMPLE_spring:
14658 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14662 PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
14666 PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
14669 case SAMPLE_eater_eat:
14670 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14674 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14677 case SAMPLE_collect:
14678 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
14681 case SAMPLE_diamond:
14682 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14685 case SAMPLE_squash:
14686 /* !!! CHECK THIS !!! */
14688 PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
14690 PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
14694 case SAMPLE_wonderfall:
14695 PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
14699 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14703 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14707 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14711 PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
14715 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14719 PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
14722 case SAMPLE_wonder:
14723 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14727 PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
14730 case SAMPLE_exit_open:
14731 PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
14734 case SAMPLE_exit_leave:
14735 PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
14738 case SAMPLE_dynamite:
14739 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14743 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14747 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
14751 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14755 PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
14759 PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
14763 PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
14767 PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
14772 void PlayLevelSound_SP(int xx, int yy, int element_sp, int action_sp)
14774 int element = map_element_SP_to_RND(element_sp);
14775 int action = map_action_SP_to_RND(action_sp);
14776 int offset = (setup.sp_show_border_elements ? 0 : 1);
14777 int x = xx - offset;
14778 int y = yy - offset;
14780 PlayLevelSoundElementAction(x, y, element, action);
14783 void PlayLevelSound_MM(int xx, int yy, int element_mm, int action_mm)
14785 int element = map_element_MM_to_RND(element_mm);
14786 int action = map_action_MM_to_RND(action_mm);
14788 int x = xx - offset;
14789 int y = yy - offset;
14791 if (!IS_MM_ELEMENT(element))
14792 element = EL_MM_DEFAULT;
14794 PlayLevelSoundElementAction(x, y, element, action);
14797 void PlaySound_MM(int sound_mm)
14799 int sound = map_sound_MM_to_RND(sound_mm);
14801 if (sound == SND_UNDEFINED)
14807 void PlaySoundLoop_MM(int sound_mm)
14809 int sound = map_sound_MM_to_RND(sound_mm);
14811 if (sound == SND_UNDEFINED)
14814 PlaySoundLoop(sound);
14817 void StopSound_MM(int sound_mm)
14819 int sound = map_sound_MM_to_RND(sound_mm);
14821 if (sound == SND_UNDEFINED)
14827 void RaiseScore(int value)
14829 local_player->score += value;
14831 game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
14833 DisplayGameControlValues();
14836 void RaiseScoreElement(int element)
14841 case EL_BD_DIAMOND:
14842 case EL_EMERALD_YELLOW:
14843 case EL_EMERALD_RED:
14844 case EL_EMERALD_PURPLE:
14845 case EL_SP_INFOTRON:
14846 RaiseScore(level.score[SC_EMERALD]);
14849 RaiseScore(level.score[SC_DIAMOND]);
14852 RaiseScore(level.score[SC_CRYSTAL]);
14855 RaiseScore(level.score[SC_PEARL]);
14858 case EL_BD_BUTTERFLY:
14859 case EL_SP_ELECTRON:
14860 RaiseScore(level.score[SC_BUG]);
14863 case EL_BD_FIREFLY:
14864 case EL_SP_SNIKSNAK:
14865 RaiseScore(level.score[SC_SPACESHIP]);
14868 case EL_DARK_YAMYAM:
14869 RaiseScore(level.score[SC_YAMYAM]);
14872 RaiseScore(level.score[SC_ROBOT]);
14875 RaiseScore(level.score[SC_PACMAN]);
14878 RaiseScore(level.score[SC_NUT]);
14881 case EL_EM_DYNAMITE:
14882 case EL_SP_DISK_RED:
14883 case EL_DYNABOMB_INCREASE_NUMBER:
14884 case EL_DYNABOMB_INCREASE_SIZE:
14885 case EL_DYNABOMB_INCREASE_POWER:
14886 RaiseScore(level.score[SC_DYNAMITE]);
14888 case EL_SHIELD_NORMAL:
14889 case EL_SHIELD_DEADLY:
14890 RaiseScore(level.score[SC_SHIELD]);
14892 case EL_EXTRA_TIME:
14893 RaiseScore(level.extra_time_score);
14907 case EL_DC_KEY_WHITE:
14908 RaiseScore(level.score[SC_KEY]);
14911 RaiseScore(element_info[element].collect_score);
14916 void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message)
14918 if (skip_request || Request(message, REQ_ASK | REQ_STAY_CLOSED))
14920 /* closing door required in case of envelope style request dialogs */
14922 CloseDoor(DOOR_CLOSE_1);
14924 #if defined(NETWORK_AVALIABLE)
14925 if (options.network)
14926 SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
14931 FadeSkipNextFadeIn();
14933 SetGameStatus(GAME_MODE_MAIN);
14938 else /* continue playing the game */
14940 if (tape.playing && tape.deactivate_display)
14941 TapeDeactivateDisplayOff(TRUE);
14943 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
14945 if (tape.playing && tape.deactivate_display)
14946 TapeDeactivateDisplayOn();
14950 void RequestQuitGame(boolean ask_if_really_quit)
14952 boolean quick_quit = (!ask_if_really_quit || level_editor_test_game);
14953 boolean skip_request = AllPlayersGone || quick_quit;
14955 RequestQuitGameExt(skip_request, quick_quit,
14956 "Do you really want to quit the game?");
14960 /* ------------------------------------------------------------------------- */
14961 /* random generator functions */
14962 /* ------------------------------------------------------------------------- */
14964 unsigned int InitEngineRandom_RND(int seed)
14966 game.num_random_calls = 0;
14968 return InitEngineRandom(seed);
14971 unsigned int RND(int max)
14975 game.num_random_calls++;
14977 return GetEngineRandom(max);
14984 /* ------------------------------------------------------------------------- */
14985 /* game engine snapshot handling functions */
14986 /* ------------------------------------------------------------------------- */
14988 struct EngineSnapshotInfo
14990 /* runtime values for custom element collect score */
14991 int collect_score[NUM_CUSTOM_ELEMENTS];
14993 /* runtime values for group element choice position */
14994 int choice_pos[NUM_GROUP_ELEMENTS];
14996 /* runtime values for belt position animations */
14997 int belt_graphic[4][NUM_BELT_PARTS];
14998 int belt_anim_mode[4][NUM_BELT_PARTS];
15001 static struct EngineSnapshotInfo engine_snapshot_rnd;
15002 static char *snapshot_level_identifier = NULL;
15003 static int snapshot_level_nr = -1;
15005 static void SaveEngineSnapshotValues_RND()
15007 static int belt_base_active_element[4] =
15009 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
15010 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
15011 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
15012 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
15016 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
15018 int element = EL_CUSTOM_START + i;
15020 engine_snapshot_rnd.collect_score[i] = element_info[element].collect_score;
15023 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15025 int element = EL_GROUP_START + i;
15027 engine_snapshot_rnd.choice_pos[i] = element_info[element].group->choice_pos;
15030 for (i = 0; i < 4; i++)
15032 for (j = 0; j < NUM_BELT_PARTS; j++)
15034 int element = belt_base_active_element[i] + j;
15035 int graphic = el2img(element);
15036 int anim_mode = graphic_info[graphic].anim_mode;
15038 engine_snapshot_rnd.belt_graphic[i][j] = graphic;
15039 engine_snapshot_rnd.belt_anim_mode[i][j] = anim_mode;
15044 static void LoadEngineSnapshotValues_RND()
15046 unsigned int num_random_calls = game.num_random_calls;
15049 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
15051 int element = EL_CUSTOM_START + i;
15053 element_info[element].collect_score = engine_snapshot_rnd.collect_score[i];
15056 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15058 int element = EL_GROUP_START + i;
15060 element_info[element].group->choice_pos = engine_snapshot_rnd.choice_pos[i];
15063 for (i = 0; i < 4; i++)
15065 for (j = 0; j < NUM_BELT_PARTS; j++)
15067 int graphic = engine_snapshot_rnd.belt_graphic[i][j];
15068 int anim_mode = engine_snapshot_rnd.belt_anim_mode[i][j];
15070 graphic_info[graphic].anim_mode = anim_mode;
15074 if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15076 InitRND(tape.random_seed);
15077 for (i = 0; i < num_random_calls; i++)
15081 if (game.num_random_calls != num_random_calls)
15083 Error(ERR_INFO, "number of random calls out of sync");
15084 Error(ERR_INFO, "number of random calls should be %d", num_random_calls);
15085 Error(ERR_INFO, "number of random calls is %d", game.num_random_calls);
15086 Error(ERR_EXIT, "this should not happen -- please debug");
15090 void FreeEngineSnapshotSingle()
15092 FreeSnapshotSingle();
15094 setString(&snapshot_level_identifier, NULL);
15095 snapshot_level_nr = -1;
15098 void FreeEngineSnapshotList()
15100 FreeSnapshotList();
15103 ListNode *SaveEngineSnapshotBuffers()
15105 ListNode *buffers = NULL;
15107 /* copy some special values to a structure better suited for the snapshot */
15109 if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15110 SaveEngineSnapshotValues_RND();
15111 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15112 SaveEngineSnapshotValues_EM();
15113 if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15114 SaveEngineSnapshotValues_SP(&buffers);
15115 if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15116 SaveEngineSnapshotValues_MM(&buffers);
15118 /* save values stored in special snapshot structure */
15120 if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15121 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
15122 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15123 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
15124 if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15125 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_sp));
15126 if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15127 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_mm));
15129 /* save further RND engine values */
15131 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(stored_player));
15132 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(game));
15133 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(tape));
15135 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ZX));
15136 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ZY));
15137 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExitX));
15138 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExitY));
15140 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(FrameCounter));
15141 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeFrames));
15142 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimePlayed));
15143 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeLeft));
15144 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TapeTime));
15146 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovDir));
15147 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovPos));
15148 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenGfxPos));
15150 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScrollStepSize));
15152 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AllPlayersGone));
15154 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt));
15155 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2));
15157 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Feld));
15158 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovPos));
15159 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDir));
15160 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDelay));
15161 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeDelay));
15162 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangePage));
15163 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CustomValue));
15164 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store));
15165 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store2));
15166 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(StorePlayer));
15167 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Back));
15168 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaNr));
15169 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustMoving));
15170 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustFalling));
15171 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckCollision));
15172 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckImpact));
15173 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Stop));
15174 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Pushed));
15176 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeCount));
15177 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeEvent));
15179 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodePhase));
15180 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeDelay));
15181 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeField));
15183 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(RunnerVisit));
15184 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(PlayerVisit));
15186 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxFrame));
15187 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxRandom));
15188 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxElement));
15189 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxAction));
15190 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxDir));
15192 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_x));
15193 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_y));
15196 ListNode *node = engine_snapshot_list_rnd;
15199 while (node != NULL)
15201 num_bytes += ((struct EngineSnapshotNodeInfo *)node->content)->size;
15206 printf("::: size of engine snapshot: %d bytes\n", num_bytes);
15212 void SaveEngineSnapshotSingle()
15214 ListNode *buffers = SaveEngineSnapshotBuffers();
15216 /* finally save all snapshot buffers to single snapshot */
15217 SaveSnapshotSingle(buffers);
15219 /* save level identification information */
15220 setString(&snapshot_level_identifier, leveldir_current->identifier);
15221 snapshot_level_nr = level_nr;
15224 boolean CheckSaveEngineSnapshotToList()
15226 boolean save_snapshot =
15227 ((game.snapshot.mode == SNAPSHOT_MODE_EVERY_STEP) ||
15228 (game.snapshot.mode == SNAPSHOT_MODE_EVERY_MOVE &&
15229 game.snapshot.changed_action) ||
15230 (game.snapshot.mode == SNAPSHOT_MODE_EVERY_COLLECT &&
15231 game.snapshot.collected_item));
15233 game.snapshot.changed_action = FALSE;
15234 game.snapshot.collected_item = FALSE;
15235 game.snapshot.save_snapshot = save_snapshot;
15237 return save_snapshot;
15240 void SaveEngineSnapshotToList()
15242 if (game.snapshot.mode == SNAPSHOT_MODE_OFF ||
15246 ListNode *buffers = SaveEngineSnapshotBuffers();
15248 /* finally save all snapshot buffers to snapshot list */
15249 SaveSnapshotToList(buffers);
15252 void SaveEngineSnapshotToListInitial()
15254 FreeEngineSnapshotList();
15256 SaveEngineSnapshotToList();
15259 void LoadEngineSnapshotValues()
15261 /* restore special values from snapshot structure */
15263 if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15264 LoadEngineSnapshotValues_RND();
15265 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15266 LoadEngineSnapshotValues_EM();
15267 if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15268 LoadEngineSnapshotValues_SP();
15269 if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15270 LoadEngineSnapshotValues_MM();
15273 void LoadEngineSnapshotSingle()
15275 LoadSnapshotSingle();
15277 LoadEngineSnapshotValues();
15280 void LoadEngineSnapshot_Undo(int steps)
15282 LoadSnapshotFromList_Older(steps);
15284 LoadEngineSnapshotValues();
15287 void LoadEngineSnapshot_Redo(int steps)
15289 LoadSnapshotFromList_Newer(steps);
15291 LoadEngineSnapshotValues();
15294 boolean CheckEngineSnapshotSingle()
15296 return (strEqual(snapshot_level_identifier, leveldir_current->identifier) &&
15297 snapshot_level_nr == level_nr);
15300 boolean CheckEngineSnapshotList()
15302 return CheckSnapshotList();
15306 /* ---------- new game button stuff ---------------------------------------- */
15313 boolean *setup_value;
15314 boolean allowed_on_tape;
15316 } gamebutton_info[NUM_GAME_BUTTONS] =
15319 IMG_GFX_GAME_BUTTON_STOP, &game.button.stop,
15320 GAME_CTRL_ID_STOP, NULL,
15324 IMG_GFX_GAME_BUTTON_PAUSE, &game.button.pause,
15325 GAME_CTRL_ID_PAUSE, NULL,
15329 IMG_GFX_GAME_BUTTON_PLAY, &game.button.play,
15330 GAME_CTRL_ID_PLAY, NULL,
15334 IMG_GFX_GAME_BUTTON_UNDO, &game.button.undo,
15335 GAME_CTRL_ID_UNDO, NULL,
15339 IMG_GFX_GAME_BUTTON_REDO, &game.button.redo,
15340 GAME_CTRL_ID_REDO, NULL,
15344 IMG_GFX_GAME_BUTTON_SAVE, &game.button.save,
15345 GAME_CTRL_ID_SAVE, NULL,
15349 IMG_GFX_GAME_BUTTON_PAUSE2, &game.button.pause2,
15350 GAME_CTRL_ID_PAUSE2, NULL,
15354 IMG_GFX_GAME_BUTTON_LOAD, &game.button.load,
15355 GAME_CTRL_ID_LOAD, NULL,
15359 IMG_GFX_GAME_BUTTON_PANEL_STOP, &game.button.panel_stop,
15360 GAME_CTRL_ID_PANEL_STOP, NULL,
15364 IMG_GFX_GAME_BUTTON_PANEL_PAUSE, &game.button.panel_pause,
15365 GAME_CTRL_ID_PANEL_PAUSE, NULL,
15366 FALSE, "pause game"
15369 IMG_GFX_GAME_BUTTON_PANEL_PLAY, &game.button.panel_play,
15370 GAME_CTRL_ID_PANEL_PLAY, NULL,
15374 IMG_GFX_GAME_BUTTON_SOUND_MUSIC, &game.button.sound_music,
15375 SOUND_CTRL_ID_MUSIC, &setup.sound_music,
15376 TRUE, "background music on/off"
15379 IMG_GFX_GAME_BUTTON_SOUND_LOOPS, &game.button.sound_loops,
15380 SOUND_CTRL_ID_LOOPS, &setup.sound_loops,
15381 TRUE, "sound loops on/off"
15384 IMG_GFX_GAME_BUTTON_SOUND_SIMPLE, &game.button.sound_simple,
15385 SOUND_CTRL_ID_SIMPLE, &setup.sound_simple,
15386 TRUE, "normal sounds on/off"
15389 IMG_GFX_GAME_BUTTON_PANEL_SOUND_MUSIC, &game.button.panel_sound_music,
15390 SOUND_CTRL_ID_PANEL_MUSIC, &setup.sound_music,
15391 FALSE, "background music on/off"
15394 IMG_GFX_GAME_BUTTON_PANEL_SOUND_LOOPS, &game.button.panel_sound_loops,
15395 SOUND_CTRL_ID_PANEL_LOOPS, &setup.sound_loops,
15396 FALSE, "sound loops on/off"
15399 IMG_GFX_GAME_BUTTON_PANEL_SOUND_SIMPLE, &game.button.panel_sound_simple,
15400 SOUND_CTRL_ID_PANEL_SIMPLE, &setup.sound_simple,
15401 FALSE, "normal sounds on/off"
15405 void CreateGameButtons()
15409 for (i = 0; i < NUM_GAME_BUTTONS; i++)
15411 struct GraphicInfo *gfx = &graphic_info[gamebutton_info[i].graphic];
15412 struct XY *pos = gamebutton_info[i].pos;
15413 struct GadgetInfo *gi;
15416 unsigned int event_mask;
15417 boolean allowed_on_tape = gamebutton_info[i].allowed_on_tape;
15418 boolean on_tape = (tape.show_game_buttons && allowed_on_tape);
15419 int base_x = (on_tape ? VX : DX);
15420 int base_y = (on_tape ? VY : DY);
15421 int gd_x = gfx->src_x;
15422 int gd_y = gfx->src_y;
15423 int gd_xp = gfx->src_x + gfx->pressed_xoffset;
15424 int gd_yp = gfx->src_y + gfx->pressed_yoffset;
15425 int gd_xa = gfx->src_x + gfx->active_xoffset;
15426 int gd_ya = gfx->src_y + gfx->active_yoffset;
15427 int gd_xap = gfx->src_x + gfx->active_xoffset + gfx->pressed_xoffset;
15428 int gd_yap = gfx->src_y + gfx->active_yoffset + gfx->pressed_yoffset;
15431 if (gfx->bitmap == NULL)
15433 game_gadget[id] = NULL;
15438 if (id == GAME_CTRL_ID_STOP ||
15439 id == GAME_CTRL_ID_PANEL_STOP ||
15440 id == GAME_CTRL_ID_PLAY ||
15441 id == GAME_CTRL_ID_PANEL_PLAY ||
15442 id == GAME_CTRL_ID_SAVE ||
15443 id == GAME_CTRL_ID_LOAD)
15445 button_type = GD_TYPE_NORMAL_BUTTON;
15447 event_mask = GD_EVENT_RELEASED;
15449 else if (id == GAME_CTRL_ID_UNDO ||
15450 id == GAME_CTRL_ID_REDO)
15452 button_type = GD_TYPE_NORMAL_BUTTON;
15454 event_mask = GD_EVENT_PRESSED | GD_EVENT_REPEATED;
15458 button_type = GD_TYPE_CHECK_BUTTON;
15459 checked = (gamebutton_info[i].setup_value != NULL ?
15460 *gamebutton_info[i].setup_value : FALSE);
15461 event_mask = GD_EVENT_PRESSED;
15464 gi = CreateGadget(GDI_CUSTOM_ID, id,
15465 GDI_INFO_TEXT, gamebutton_info[i].infotext,
15466 GDI_X, base_x + GDI_ACTIVE_POS(pos->x),
15467 GDI_Y, base_y + GDI_ACTIVE_POS(pos->y),
15468 GDI_WIDTH, gfx->width,
15469 GDI_HEIGHT, gfx->height,
15470 GDI_TYPE, button_type,
15471 GDI_STATE, GD_BUTTON_UNPRESSED,
15472 GDI_CHECKED, checked,
15473 GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
15474 GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
15475 GDI_ALT_DESIGN_UNPRESSED, gfx->bitmap, gd_xa, gd_ya,
15476 GDI_ALT_DESIGN_PRESSED, gfx->bitmap, gd_xap, gd_yap,
15477 GDI_DIRECT_DRAW, FALSE,
15478 GDI_EVENT_MASK, event_mask,
15479 GDI_CALLBACK_ACTION, HandleGameButtons,
15483 Error(ERR_EXIT, "cannot create gadget");
15485 game_gadget[id] = gi;
15489 void FreeGameButtons()
15493 for (i = 0; i < NUM_GAME_BUTTONS; i++)
15494 FreeGadget(game_gadget[i]);
15497 static void UnmapGameButtonsAtSamePosition(int id)
15501 for (i = 0; i < NUM_GAME_BUTTONS; i++)
15503 gamebutton_info[i].pos->x == gamebutton_info[id].pos->x &&
15504 gamebutton_info[i].pos->y == gamebutton_info[id].pos->y)
15505 UnmapGadget(game_gadget[i]);
15508 static void UnmapGameButtonsAtSamePosition_All()
15510 if (setup.show_snapshot_buttons)
15512 UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_SAVE);
15513 UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE2);
15514 UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_LOAD);
15518 UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_STOP);
15519 UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE);
15520 UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PLAY);
15522 UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_STOP);
15523 UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_PAUSE);
15524 UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_PLAY);
15528 static void MapGameButtonsAtSamePosition(int id)
15532 for (i = 0; i < NUM_GAME_BUTTONS; i++)
15534 gamebutton_info[i].pos->x == gamebutton_info[id].pos->x &&
15535 gamebutton_info[i].pos->y == gamebutton_info[id].pos->y)
15536 MapGadget(game_gadget[i]);
15538 UnmapGameButtonsAtSamePosition_All();
15541 void MapUndoRedoButtons()
15543 UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
15544 UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
15546 MapGadget(game_gadget[GAME_CTRL_ID_UNDO]);
15547 MapGadget(game_gadget[GAME_CTRL_ID_REDO]);
15549 ModifyGadget(game_gadget[GAME_CTRL_ID_PAUSE2], GDI_CHECKED, TRUE, GDI_END);
15552 void UnmapUndoRedoButtons()
15554 UnmapGadget(game_gadget[GAME_CTRL_ID_UNDO]);
15555 UnmapGadget(game_gadget[GAME_CTRL_ID_REDO]);
15557 MapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
15558 MapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
15560 ModifyGadget(game_gadget[GAME_CTRL_ID_PAUSE2], GDI_CHECKED, FALSE, GDI_END);
15563 void MapGameButtonsExt(boolean on_tape)
15567 for (i = 0; i < NUM_GAME_BUTTONS; i++)
15568 if ((!on_tape || gamebutton_info[i].allowed_on_tape) &&
15569 i != GAME_CTRL_ID_UNDO &&
15570 i != GAME_CTRL_ID_REDO)
15571 MapGadget(game_gadget[i]);
15573 UnmapGameButtonsAtSamePosition_All();
15575 RedrawGameButtons();
15578 void UnmapGameButtonsExt(boolean on_tape)
15582 for (i = 0; i < NUM_GAME_BUTTONS; i++)
15583 if (!on_tape || gamebutton_info[i].allowed_on_tape)
15584 UnmapGadget(game_gadget[i]);
15587 void RedrawGameButtonsExt(boolean on_tape)
15591 for (i = 0; i < NUM_GAME_BUTTONS; i++)
15592 if (!on_tape || gamebutton_info[i].allowed_on_tape)
15593 RedrawGadget(game_gadget[i]);
15595 // RedrawGadget() may have set REDRAW_ALL if buttons are defined off-area
15596 redraw_mask &= ~REDRAW_ALL;
15599 void SetGadgetState(struct GadgetInfo *gi, boolean state)
15604 gi->checked = state;
15607 void RedrawSoundButtonGadget(int id)
15609 int id2 = (id == SOUND_CTRL_ID_MUSIC ? SOUND_CTRL_ID_PANEL_MUSIC :
15610 id == SOUND_CTRL_ID_LOOPS ? SOUND_CTRL_ID_PANEL_LOOPS :
15611 id == SOUND_CTRL_ID_SIMPLE ? SOUND_CTRL_ID_PANEL_SIMPLE :
15612 id == SOUND_CTRL_ID_PANEL_MUSIC ? SOUND_CTRL_ID_MUSIC :
15613 id == SOUND_CTRL_ID_PANEL_LOOPS ? SOUND_CTRL_ID_LOOPS :
15614 id == SOUND_CTRL_ID_PANEL_SIMPLE ? SOUND_CTRL_ID_SIMPLE :
15617 SetGadgetState(game_gadget[id2], *gamebutton_info[id2].setup_value);
15618 RedrawGadget(game_gadget[id2]);
15621 void MapGameButtons()
15623 MapGameButtonsExt(FALSE);
15626 void UnmapGameButtons()
15628 UnmapGameButtonsExt(FALSE);
15631 void RedrawGameButtons()
15633 RedrawGameButtonsExt(FALSE);
15636 void MapGameButtonsOnTape()
15638 MapGameButtonsExt(TRUE);
15641 void UnmapGameButtonsOnTape()
15643 UnmapGameButtonsExt(TRUE);
15646 void RedrawGameButtonsOnTape()
15648 RedrawGameButtonsExt(TRUE);
15651 void GameUndoRedoExt()
15653 ClearPlayerAction();
15655 tape.pausing = TRUE;
15658 UpdateAndDisplayGameControlValues();
15660 DrawCompleteVideoDisplay();
15661 DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
15662 DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
15663 DrawVideoDisplay(VIDEO_STATE_1STEP(tape.single_step), 0);
15668 void GameUndo(int steps)
15670 if (!CheckEngineSnapshotList())
15673 LoadEngineSnapshot_Undo(steps);
15678 void GameRedo(int steps)
15680 if (!CheckEngineSnapshotList())
15683 LoadEngineSnapshot_Redo(steps);
15688 static void HandleGameButtonsExt(int id, int button)
15690 static boolean game_undo_executed = FALSE;
15691 int steps = BUTTON_STEPSIZE(button);
15692 boolean handle_game_buttons =
15693 (game_status == GAME_MODE_PLAYING ||
15694 (game_status == GAME_MODE_MAIN && tape.show_game_buttons));
15696 if (!handle_game_buttons)
15701 case GAME_CTRL_ID_STOP:
15702 case GAME_CTRL_ID_PANEL_STOP:
15703 if (game_status == GAME_MODE_MAIN)
15709 RequestQuitGame(TRUE);
15713 case GAME_CTRL_ID_PAUSE:
15714 case GAME_CTRL_ID_PAUSE2:
15715 case GAME_CTRL_ID_PANEL_PAUSE:
15716 if (options.network && game_status == GAME_MODE_PLAYING)
15718 #if defined(NETWORK_AVALIABLE)
15720 SendToServer_ContinuePlaying();
15722 SendToServer_PausePlaying();
15726 TapeTogglePause(TAPE_TOGGLE_MANUAL);
15728 game_undo_executed = FALSE;
15732 case GAME_CTRL_ID_PLAY:
15733 case GAME_CTRL_ID_PANEL_PLAY:
15734 if (game_status == GAME_MODE_MAIN)
15736 StartGameActions(options.network, setup.autorecord, level.random_seed);
15738 else if (tape.pausing)
15740 #if defined(NETWORK_AVALIABLE)
15741 if (options.network)
15742 SendToServer_ContinuePlaying();
15745 TapeTogglePause(TAPE_TOGGLE_MANUAL | TAPE_TOGGLE_PLAY_PAUSE);
15749 case GAME_CTRL_ID_UNDO:
15750 // Important: When using "save snapshot when collecting an item" mode,
15751 // load last (current) snapshot for first "undo" after pressing "pause"
15752 // (else the last-but-one snapshot would be loaded, because the snapshot
15753 // pointer already points to the last snapshot when pressing "pause",
15754 // which is fine for "every step/move" mode, but not for "every collect")
15755 if (game.snapshot.mode == SNAPSHOT_MODE_EVERY_COLLECT &&
15756 !game_undo_executed)
15759 game_undo_executed = TRUE;
15764 case GAME_CTRL_ID_REDO:
15768 case GAME_CTRL_ID_SAVE:
15772 case GAME_CTRL_ID_LOAD:
15776 case SOUND_CTRL_ID_MUSIC:
15777 case SOUND_CTRL_ID_PANEL_MUSIC:
15778 if (setup.sound_music)
15780 setup.sound_music = FALSE;
15784 else if (audio.music_available)
15786 setup.sound = setup.sound_music = TRUE;
15788 SetAudioMode(setup.sound);
15790 if (game_status == GAME_MODE_PLAYING)
15794 RedrawSoundButtonGadget(id);
15798 case SOUND_CTRL_ID_LOOPS:
15799 case SOUND_CTRL_ID_PANEL_LOOPS:
15800 if (setup.sound_loops)
15801 setup.sound_loops = FALSE;
15802 else if (audio.loops_available)
15804 setup.sound = setup.sound_loops = TRUE;
15806 SetAudioMode(setup.sound);
15809 RedrawSoundButtonGadget(id);
15813 case SOUND_CTRL_ID_SIMPLE:
15814 case SOUND_CTRL_ID_PANEL_SIMPLE:
15815 if (setup.sound_simple)
15816 setup.sound_simple = FALSE;
15817 else if (audio.sound_available)
15819 setup.sound = setup.sound_simple = TRUE;
15821 SetAudioMode(setup.sound);
15824 RedrawSoundButtonGadget(id);
15833 static void HandleGameButtons(struct GadgetInfo *gi)
15835 HandleGameButtonsExt(gi->custom_id, gi->event.button);
15838 void HandleSoundButtonKeys(Key key)
15840 if (key == setup.shortcut.sound_simple)
15841 ClickOnGadget(game_gadget[SOUND_CTRL_ID_SIMPLE], MB_LEFTBUTTON);
15842 else if (key == setup.shortcut.sound_loops)
15843 ClickOnGadget(game_gadget[SOUND_CTRL_ID_LOOPS], MB_LEFTBUTTON);
15844 else if (key == setup.shortcut.sound_music)
15845 ClickOnGadget(game_gadget[SOUND_CTRL_ID_MUSIC], MB_LEFTBUTTON);