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
30 #define USE_NEW_AMOEBA_CODE FALSE
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)
65 #define MP_NO_ACTION 0
68 #define MP_DONT_RUN_INTO (MP_MOVING | MP_ACTION)
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() (game.panel.active == FALSE)
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 GAME_CTRL_ID_TOUCH_STOP 11
1020 #define GAME_CTRL_ID_TOUCH_PAUSE 12
1021 #define SOUND_CTRL_ID_MUSIC 13
1022 #define SOUND_CTRL_ID_LOOPS 14
1023 #define SOUND_CTRL_ID_SIMPLE 15
1024 #define SOUND_CTRL_ID_PANEL_MUSIC 16
1025 #define SOUND_CTRL_ID_PANEL_LOOPS 17
1026 #define SOUND_CTRL_ID_PANEL_SIMPLE 18
1028 #define NUM_GAME_BUTTONS 19
1031 // forward declaration for internal use
1033 static void CreateField(int, int, int);
1035 static void ResetGfxAnimation(int, int);
1037 static void SetPlayerWaiting(struct PlayerInfo *, boolean);
1038 static void AdvanceFrameAndPlayerCounters(int);
1040 static boolean MovePlayerOneStep(struct PlayerInfo *, int, int, int, int);
1041 static boolean MovePlayer(struct PlayerInfo *, int, int);
1042 static void ScrollPlayer(struct PlayerInfo *, int);
1043 static void ScrollScreen(struct PlayerInfo *, int);
1045 static int DigField(struct PlayerInfo *, int, int, int, int, int, int, int);
1046 static boolean DigFieldByCE(int, int, int);
1047 static boolean SnapField(struct PlayerInfo *, int, int);
1048 static boolean DropElement(struct PlayerInfo *);
1050 static void InitBeltMovement(void);
1051 static void CloseAllOpenTimegates(void);
1052 static void CheckGravityMovement(struct PlayerInfo *);
1053 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *);
1054 static void KillPlayerUnlessEnemyProtected(int, int);
1055 static void KillPlayerUnlessExplosionProtected(int, int);
1057 static void TestIfPlayerTouchesCustomElement(int, int);
1058 static void TestIfElementTouchesCustomElement(int, int);
1059 static void TestIfElementHitsCustomElement(int, int, int);
1061 static void HandleElementChange(int, int, int);
1062 static void ExecuteCustomElementAction(int, int, int, int);
1063 static boolean ChangeElement(int, int, int, int);
1065 static boolean CheckTriggeredElementChangeExt(int, int, int, int, int,int,int);
1066 #define CheckTriggeredElementChange(x, y, e, ev) \
1067 CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY,CH_SIDE_ANY, -1)
1068 #define CheckTriggeredElementChangeByPlayer(x, y, e, ev, p, s) \
1069 CheckTriggeredElementChangeExt(x, y, e, ev, p, s, -1)
1070 #define CheckTriggeredElementChangeBySide(x, y, e, ev, s) \
1071 CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, s, -1)
1072 #define CheckTriggeredElementChangeByPage(x, y, e, ev, p) \
1073 CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY, CH_SIDE_ANY, p)
1075 static boolean CheckElementChangeExt(int, int, int, int, int, int, int);
1076 #define CheckElementChange(x, y, e, te, ev) \
1077 CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, CH_SIDE_ANY)
1078 #define CheckElementChangeByPlayer(x, y, e, ev, p, s) \
1079 CheckElementChangeExt(x, y, e, EL_EMPTY, ev, p, s)
1080 #define CheckElementChangeBySide(x, y, e, te, ev, s) \
1081 CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, s)
1083 static void PlayLevelSound(int, int, int);
1084 static void PlayLevelSoundNearest(int, int, int);
1085 static void PlayLevelSoundAction(int, int, int);
1086 static void PlayLevelSoundElementAction(int, int, int, int);
1087 static void PlayLevelSoundElementActionIfLoop(int, int, int, int);
1088 static void PlayLevelSoundActionIfLoop(int, int, int);
1089 static void StopLevelSoundActionIfLoop(int, int, int);
1090 static void PlayLevelMusic(void);
1091 static void FadeLevelSoundsAndMusic(void);
1093 static void HandleGameButtons(struct GadgetInfo *);
1095 int AmoebeNachbarNr(int, int);
1096 void AmoebeUmwandeln(int, int);
1097 void ContinueMoving(int, int);
1098 void Bang(int, int);
1099 void InitMovDir(int, int);
1100 void InitAmoebaNr(int, int);
1101 int NewHiScore(int);
1103 void TestIfGoodThingHitsBadThing(int, int, int);
1104 void TestIfBadThingHitsGoodThing(int, int, int);
1105 void TestIfPlayerTouchesBadThing(int, int);
1106 void TestIfPlayerRunsIntoBadThing(int, int, int);
1107 void TestIfBadThingTouchesPlayer(int, int);
1108 void TestIfBadThingRunsIntoPlayer(int, int, int);
1109 void TestIfFriendTouchesBadThing(int, int);
1110 void TestIfBadThingTouchesFriend(int, int);
1111 void TestIfBadThingTouchesOtherBadThing(int, int);
1112 void TestIfGoodThingGetsHitByBadThing(int, int, int);
1114 void KillPlayer(struct PlayerInfo *);
1115 void BuryPlayer(struct PlayerInfo *);
1116 void RemovePlayer(struct PlayerInfo *);
1117 void ExitPlayer(struct PlayerInfo *);
1119 static int getInvisibleActiveFromInvisibleElement(int);
1120 static int getInvisibleFromInvisibleActiveElement(int);
1122 static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
1124 // for detection of endless loops, caused by custom element programming
1125 // (using maximal playfield width x 10 is just a rough approximation)
1126 #define MAX_ELEMENT_CHANGE_RECURSION_DEPTH (MAX_PLAYFIELD_WIDTH * 10)
1128 #define RECURSION_LOOP_DETECTION_START(e, rc) \
1130 if (recursion_loop_detected) \
1133 if (recursion_loop_depth > MAX_ELEMENT_CHANGE_RECURSION_DEPTH) \
1135 recursion_loop_detected = TRUE; \
1136 recursion_loop_element = (e); \
1139 recursion_loop_depth++; \
1142 #define RECURSION_LOOP_DETECTION_END() \
1144 recursion_loop_depth--; \
1147 static int recursion_loop_depth;
1148 static boolean recursion_loop_detected;
1149 static boolean recursion_loop_element;
1151 static int map_player_action[MAX_PLAYERS];
1154 // ----------------------------------------------------------------------------
1155 // definition of elements that automatically change to other elements after
1156 // a specified time, eventually calling a function when changing
1157 // ----------------------------------------------------------------------------
1159 // forward declaration for changer functions
1160 static void InitBuggyBase(int, int);
1161 static void WarnBuggyBase(int, int);
1163 static void InitTrap(int, int);
1164 static void ActivateTrap(int, int);
1165 static void ChangeActiveTrap(int, int);
1167 static void InitRobotWheel(int, int);
1168 static void RunRobotWheel(int, int);
1169 static void StopRobotWheel(int, int);
1171 static void InitTimegateWheel(int, int);
1172 static void RunTimegateWheel(int, int);
1174 static void InitMagicBallDelay(int, int);
1175 static void ActivateMagicBall(int, int);
1177 struct ChangingElementInfo
1182 void (*pre_change_function)(int x, int y);
1183 void (*change_function)(int x, int y);
1184 void (*post_change_function)(int x, int y);
1187 static struct ChangingElementInfo change_delay_list[] =
1222 EL_STEEL_EXIT_OPENING,
1230 EL_STEEL_EXIT_CLOSING,
1231 EL_STEEL_EXIT_CLOSED,
1254 EL_EM_STEEL_EXIT_OPENING,
1255 EL_EM_STEEL_EXIT_OPEN,
1262 EL_EM_STEEL_EXIT_CLOSING,
1286 EL_SWITCHGATE_OPENING,
1294 EL_SWITCHGATE_CLOSING,
1295 EL_SWITCHGATE_CLOSED,
1302 EL_TIMEGATE_OPENING,
1310 EL_TIMEGATE_CLOSING,
1319 EL_ACID_SPLASH_LEFT,
1327 EL_ACID_SPLASH_RIGHT,
1336 EL_SP_BUGGY_BASE_ACTIVATING,
1343 EL_SP_BUGGY_BASE_ACTIVATING,
1344 EL_SP_BUGGY_BASE_ACTIVE,
1351 EL_SP_BUGGY_BASE_ACTIVE,
1375 EL_ROBOT_WHEEL_ACTIVE,
1383 EL_TIMEGATE_SWITCH_ACTIVE,
1391 EL_DC_TIMEGATE_SWITCH_ACTIVE,
1392 EL_DC_TIMEGATE_SWITCH,
1399 EL_EMC_MAGIC_BALL_ACTIVE,
1400 EL_EMC_MAGIC_BALL_ACTIVE,
1407 EL_EMC_SPRING_BUMPER_ACTIVE,
1408 EL_EMC_SPRING_BUMPER,
1415 EL_DIAGONAL_SHRINKING,
1423 EL_DIAGONAL_GROWING,
1444 int push_delay_fixed, push_delay_random;
1448 { EL_SPRING, 0, 0 },
1449 { EL_BALLOON, 0, 0 },
1451 { EL_SOKOBAN_OBJECT, 2, 0 },
1452 { EL_SOKOBAN_FIELD_FULL, 2, 0 },
1453 { EL_SATELLITE, 2, 0 },
1454 { EL_SP_DISK_YELLOW, 2, 0 },
1456 { EL_UNDEFINED, 0, 0 },
1464 move_stepsize_list[] =
1466 { EL_AMOEBA_DROP, 2 },
1467 { EL_AMOEBA_DROPPING, 2 },
1468 { EL_QUICKSAND_FILLING, 1 },
1469 { EL_QUICKSAND_EMPTYING, 1 },
1470 { EL_QUICKSAND_FAST_FILLING, 2 },
1471 { EL_QUICKSAND_FAST_EMPTYING, 2 },
1472 { EL_MAGIC_WALL_FILLING, 2 },
1473 { EL_MAGIC_WALL_EMPTYING, 2 },
1474 { EL_BD_MAGIC_WALL_FILLING, 2 },
1475 { EL_BD_MAGIC_WALL_EMPTYING, 2 },
1476 { EL_DC_MAGIC_WALL_FILLING, 2 },
1477 { EL_DC_MAGIC_WALL_EMPTYING, 2 },
1479 { EL_UNDEFINED, 0 },
1487 collect_count_list[] =
1490 { EL_BD_DIAMOND, 1 },
1491 { EL_EMERALD_YELLOW, 1 },
1492 { EL_EMERALD_RED, 1 },
1493 { EL_EMERALD_PURPLE, 1 },
1495 { EL_SP_INFOTRON, 1 },
1499 { EL_UNDEFINED, 0 },
1507 access_direction_list[] =
1509 { EL_TUBE_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1510 { EL_TUBE_VERTICAL, MV_UP | MV_DOWN },
1511 { EL_TUBE_HORIZONTAL, MV_LEFT | MV_RIGHT },
1512 { EL_TUBE_VERTICAL_LEFT, MV_LEFT | MV_UP | MV_DOWN },
1513 { EL_TUBE_VERTICAL_RIGHT, MV_RIGHT | MV_UP | MV_DOWN },
1514 { EL_TUBE_HORIZONTAL_UP, MV_LEFT | MV_RIGHT | MV_UP },
1515 { EL_TUBE_HORIZONTAL_DOWN, MV_LEFT | MV_RIGHT | MV_DOWN },
1516 { EL_TUBE_LEFT_UP, MV_LEFT | MV_UP },
1517 { EL_TUBE_LEFT_DOWN, MV_LEFT | MV_DOWN },
1518 { EL_TUBE_RIGHT_UP, MV_RIGHT | MV_UP },
1519 { EL_TUBE_RIGHT_DOWN, MV_RIGHT | MV_DOWN },
1521 { EL_SP_PORT_LEFT, MV_RIGHT },
1522 { EL_SP_PORT_RIGHT, MV_LEFT },
1523 { EL_SP_PORT_UP, MV_DOWN },
1524 { EL_SP_PORT_DOWN, MV_UP },
1525 { EL_SP_PORT_HORIZONTAL, MV_LEFT | MV_RIGHT },
1526 { EL_SP_PORT_VERTICAL, MV_UP | MV_DOWN },
1527 { EL_SP_PORT_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1528 { EL_SP_GRAVITY_PORT_LEFT, MV_RIGHT },
1529 { EL_SP_GRAVITY_PORT_RIGHT, MV_LEFT },
1530 { EL_SP_GRAVITY_PORT_UP, MV_DOWN },
1531 { EL_SP_GRAVITY_PORT_DOWN, MV_UP },
1532 { EL_SP_GRAVITY_ON_PORT_LEFT, MV_RIGHT },
1533 { EL_SP_GRAVITY_ON_PORT_RIGHT, MV_LEFT },
1534 { EL_SP_GRAVITY_ON_PORT_UP, MV_DOWN },
1535 { EL_SP_GRAVITY_ON_PORT_DOWN, MV_UP },
1536 { EL_SP_GRAVITY_OFF_PORT_LEFT, MV_RIGHT },
1537 { EL_SP_GRAVITY_OFF_PORT_RIGHT, MV_LEFT },
1538 { EL_SP_GRAVITY_OFF_PORT_UP, MV_DOWN },
1539 { EL_SP_GRAVITY_OFF_PORT_DOWN, MV_UP },
1541 { EL_UNDEFINED, MV_NONE }
1544 static boolean trigger_events[MAX_NUM_ELEMENTS][NUM_CHANGE_EVENTS];
1546 #define IS_AUTO_CHANGING(e) (element_info[e].has_change_event[CE_DELAY])
1547 #define IS_JUST_CHANGING(x, y) (ChangeDelay[x][y] != 0)
1548 #define IS_CHANGING(x, y) (IS_AUTO_CHANGING(Feld[x][y]) || \
1549 IS_JUST_CHANGING(x, y))
1551 #define CE_PAGE(e, ce) (element_info[e].event_page[ce])
1553 // static variables for playfield scan mode (scanning forward or backward)
1554 static int playfield_scan_start_x = 0;
1555 static int playfield_scan_start_y = 0;
1556 static int playfield_scan_delta_x = 1;
1557 static int playfield_scan_delta_y = 1;
1559 #define SCAN_PLAYFIELD(x, y) for ((y) = playfield_scan_start_y; \
1560 (y) >= 0 && (y) <= lev_fieldy - 1; \
1561 (y) += playfield_scan_delta_y) \
1562 for ((x) = playfield_scan_start_x; \
1563 (x) >= 0 && (x) <= lev_fieldx - 1; \
1564 (x) += playfield_scan_delta_x)
1567 void DEBUG_SetMaximumDynamite(void)
1571 for (i = 0; i < MAX_INVENTORY_SIZE; i++)
1572 if (local_player->inventory_size < MAX_INVENTORY_SIZE)
1573 local_player->inventory_element[local_player->inventory_size++] =
1578 static void InitPlayfieldScanModeVars(void)
1580 if (game.use_reverse_scan_direction)
1582 playfield_scan_start_x = lev_fieldx - 1;
1583 playfield_scan_start_y = lev_fieldy - 1;
1585 playfield_scan_delta_x = -1;
1586 playfield_scan_delta_y = -1;
1590 playfield_scan_start_x = 0;
1591 playfield_scan_start_y = 0;
1593 playfield_scan_delta_x = 1;
1594 playfield_scan_delta_y = 1;
1598 static void InitPlayfieldScanMode(int mode)
1600 game.use_reverse_scan_direction =
1601 (mode == CA_ARG_SCAN_MODE_REVERSE ? TRUE : FALSE);
1603 InitPlayfieldScanModeVars();
1606 static int get_move_delay_from_stepsize(int move_stepsize)
1609 MIN(MAX(MOVE_STEPSIZE_MIN, move_stepsize), MOVE_STEPSIZE_MAX);
1611 // make sure that stepsize value is always a power of 2
1612 move_stepsize = (1 << log_2(move_stepsize));
1614 return TILEX / move_stepsize;
1617 static void SetPlayerMoveSpeed(struct PlayerInfo *player, int move_stepsize,
1620 int player_nr = player->index_nr;
1621 int move_delay = get_move_delay_from_stepsize(move_stepsize);
1622 boolean cannot_move = (move_stepsize == STEPSIZE_NOT_MOVING ? TRUE : FALSE);
1624 // do no immediately change move delay -- the player might just be moving
1625 player->move_delay_value_next = move_delay;
1627 // information if player can move must be set separately
1628 player->cannot_move = cannot_move;
1632 player->move_delay = game.initial_move_delay[player_nr];
1633 player->move_delay_value = game.initial_move_delay_value[player_nr];
1635 player->move_delay_value_next = -1;
1637 player->move_delay_reset_counter = 0;
1641 void GetPlayerConfig(void)
1643 GameFrameDelay = setup.game_frame_delay;
1645 if (!audio.sound_available)
1646 setup.sound_simple = FALSE;
1648 if (!audio.loops_available)
1649 setup.sound_loops = FALSE;
1651 if (!audio.music_available)
1652 setup.sound_music = FALSE;
1654 if (!video.fullscreen_available)
1655 setup.fullscreen = FALSE;
1657 setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
1659 SetAudioMode(setup.sound);
1662 int GetElementFromGroupElement(int element)
1664 if (IS_GROUP_ELEMENT(element))
1666 struct ElementGroupInfo *group = element_info[element].group;
1667 int last_anim_random_frame = gfx.anim_random_frame;
1670 if (group->choice_mode == ANIM_RANDOM)
1671 gfx.anim_random_frame = RND(group->num_elements_resolved);
1673 element_pos = getAnimationFrame(group->num_elements_resolved, 1,
1674 group->choice_mode, 0,
1677 if (group->choice_mode == ANIM_RANDOM)
1678 gfx.anim_random_frame = last_anim_random_frame;
1680 group->choice_pos++;
1682 element = group->element_resolved[element_pos];
1688 static void IncrementSokobanFieldsNeeded(void)
1690 if (level.sb_fields_needed)
1691 game.sokoban_fields_still_needed++;
1694 static void IncrementSokobanObjectsNeeded(void)
1696 if (level.sb_objects_needed)
1697 game.sokoban_objects_still_needed++;
1700 static void DecrementSokobanFieldsNeeded(void)
1702 if (game.sokoban_fields_still_needed > 0)
1703 game.sokoban_fields_still_needed--;
1706 static void DecrementSokobanObjectsNeeded(void)
1708 if (game.sokoban_objects_still_needed > 0)
1709 game.sokoban_objects_still_needed--;
1712 static void InitPlayerField(int x, int y, int element, boolean init_game)
1714 if (element == EL_SP_MURPHY)
1718 if (stored_player[0].present)
1720 Feld[x][y] = EL_SP_MURPHY_CLONE;
1726 stored_player[0].initial_element = element;
1727 stored_player[0].use_murphy = TRUE;
1729 if (!level.use_artwork_element[0])
1730 stored_player[0].artwork_element = EL_SP_MURPHY;
1733 Feld[x][y] = EL_PLAYER_1;
1739 struct PlayerInfo *player = &stored_player[Feld[x][y] - EL_PLAYER_1];
1740 int jx = player->jx, jy = player->jy;
1742 player->present = TRUE;
1744 player->block_last_field = (element == EL_SP_MURPHY ?
1745 level.sp_block_last_field :
1746 level.block_last_field);
1748 // ---------- initialize player's last field block delay ------------------
1750 // always start with reliable default value (no adjustment needed)
1751 player->block_delay_adjustment = 0;
1753 // special case 1: in Supaplex, Murphy blocks last field one more frame
1754 if (player->block_last_field && element == EL_SP_MURPHY)
1755 player->block_delay_adjustment = 1;
1757 // special case 2: in game engines before 3.1.1, blocking was different
1758 if (game.use_block_last_field_bug)
1759 player->block_delay_adjustment = (player->block_last_field ? -1 : 1);
1761 if (!network.enabled || player->connected_network)
1763 player->active = TRUE;
1765 // remove potentially duplicate players
1766 if (StorePlayer[jx][jy] == Feld[x][y])
1767 StorePlayer[jx][jy] = 0;
1769 StorePlayer[x][y] = Feld[x][y];
1771 #if DEBUG_INIT_PLAYER
1774 printf("- player element %d activated", player->element_nr);
1775 printf(" (local player is %d and currently %s)\n",
1776 local_player->element_nr,
1777 local_player->active ? "active" : "not active");
1782 Feld[x][y] = EL_EMPTY;
1784 player->jx = player->last_jx = x;
1785 player->jy = player->last_jy = y;
1790 int player_nr = GET_PLAYER_NR(element);
1791 struct PlayerInfo *player = &stored_player[player_nr];
1793 if (player->active && player->killed)
1794 player->reanimated = TRUE; // if player was just killed, reanimate him
1798 static void InitField(int x, int y, boolean init_game)
1800 int element = Feld[x][y];
1809 InitPlayerField(x, y, element, init_game);
1812 case EL_SOKOBAN_FIELD_PLAYER:
1813 element = Feld[x][y] = EL_PLAYER_1;
1814 InitField(x, y, init_game);
1816 element = Feld[x][y] = EL_SOKOBAN_FIELD_EMPTY;
1817 InitField(x, y, init_game);
1820 case EL_SOKOBAN_FIELD_EMPTY:
1821 IncrementSokobanFieldsNeeded();
1824 case EL_SOKOBAN_OBJECT:
1825 IncrementSokobanObjectsNeeded();
1829 if (x < lev_fieldx-1 && Feld[x+1][y] == EL_ACID)
1830 Feld[x][y] = EL_ACID_POOL_TOPLEFT;
1831 else if (x > 0 && Feld[x-1][y] == EL_ACID)
1832 Feld[x][y] = EL_ACID_POOL_TOPRIGHT;
1833 else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPLEFT)
1834 Feld[x][y] = EL_ACID_POOL_BOTTOMLEFT;
1835 else if (y > 0 && Feld[x][y-1] == EL_ACID)
1836 Feld[x][y] = EL_ACID_POOL_BOTTOM;
1837 else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPRIGHT)
1838 Feld[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
1847 case EL_SPACESHIP_RIGHT:
1848 case EL_SPACESHIP_UP:
1849 case EL_SPACESHIP_LEFT:
1850 case EL_SPACESHIP_DOWN:
1851 case EL_BD_BUTTERFLY:
1852 case EL_BD_BUTTERFLY_RIGHT:
1853 case EL_BD_BUTTERFLY_UP:
1854 case EL_BD_BUTTERFLY_LEFT:
1855 case EL_BD_BUTTERFLY_DOWN:
1857 case EL_BD_FIREFLY_RIGHT:
1858 case EL_BD_FIREFLY_UP:
1859 case EL_BD_FIREFLY_LEFT:
1860 case EL_BD_FIREFLY_DOWN:
1861 case EL_PACMAN_RIGHT:
1863 case EL_PACMAN_LEFT:
1864 case EL_PACMAN_DOWN:
1866 case EL_YAMYAM_LEFT:
1867 case EL_YAMYAM_RIGHT:
1869 case EL_YAMYAM_DOWN:
1870 case EL_DARK_YAMYAM:
1873 case EL_SP_SNIKSNAK:
1874 case EL_SP_ELECTRON:
1883 case EL_AMOEBA_FULL:
1888 case EL_AMOEBA_DROP:
1889 if (y == lev_fieldy - 1)
1891 Feld[x][y] = EL_AMOEBA_GROWING;
1892 Store[x][y] = EL_AMOEBA_WET;
1896 case EL_DYNAMITE_ACTIVE:
1897 case EL_SP_DISK_RED_ACTIVE:
1898 case EL_DYNABOMB_PLAYER_1_ACTIVE:
1899 case EL_DYNABOMB_PLAYER_2_ACTIVE:
1900 case EL_DYNABOMB_PLAYER_3_ACTIVE:
1901 case EL_DYNABOMB_PLAYER_4_ACTIVE:
1902 MovDelay[x][y] = 96;
1905 case EL_EM_DYNAMITE_ACTIVE:
1906 MovDelay[x][y] = 32;
1910 game.lights_still_needed++;
1914 game.friends_still_needed++;
1919 GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
1922 case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
1923 case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
1924 case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
1925 case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
1926 case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
1927 case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
1928 case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
1929 case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
1930 case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
1931 case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
1932 case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
1933 case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
1936 int belt_nr = getBeltNrFromBeltSwitchElement(Feld[x][y]);
1937 int belt_dir = getBeltDirFromBeltSwitchElement(Feld[x][y]);
1938 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Feld[x][y]);
1940 if (game.belt_dir_nr[belt_nr] == 3) // initial value
1942 game.belt_dir[belt_nr] = belt_dir;
1943 game.belt_dir_nr[belt_nr] = belt_dir_nr;
1945 else // more than one switch -- set it like the first switch
1947 Feld[x][y] = Feld[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
1952 case EL_LIGHT_SWITCH_ACTIVE:
1954 game.light_time_left = level.time_light * FRAMES_PER_SECOND;
1957 case EL_INVISIBLE_STEELWALL:
1958 case EL_INVISIBLE_WALL:
1959 case EL_INVISIBLE_SAND:
1960 if (game.light_time_left > 0 ||
1961 game.lenses_time_left > 0)
1962 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
1965 case EL_EMC_MAGIC_BALL:
1966 if (game.ball_active)
1967 Feld[x][y] = EL_EMC_MAGIC_BALL_ACTIVE;
1970 case EL_EMC_MAGIC_BALL_SWITCH:
1971 if (game.ball_active)
1972 Feld[x][y] = EL_EMC_MAGIC_BALL_SWITCH_ACTIVE;
1975 case EL_TRIGGER_PLAYER:
1976 case EL_TRIGGER_ELEMENT:
1977 case EL_TRIGGER_CE_VALUE:
1978 case EL_TRIGGER_CE_SCORE:
1980 case EL_ANY_ELEMENT:
1981 case EL_CURRENT_CE_VALUE:
1982 case EL_CURRENT_CE_SCORE:
1999 // reference elements should not be used on the playfield
2000 Feld[x][y] = EL_EMPTY;
2004 if (IS_CUSTOM_ELEMENT(element))
2006 if (CAN_MOVE(element))
2009 if (!element_info[element].use_last_ce_value || init_game)
2010 CustomValue[x][y] = GET_NEW_CE_VALUE(Feld[x][y]);
2012 else if (IS_GROUP_ELEMENT(element))
2014 Feld[x][y] = GetElementFromGroupElement(element);
2016 InitField(x, y, init_game);
2023 CheckTriggeredElementChange(x, y, element, CE_CREATION_OF_X);
2026 static void InitField_WithBug1(int x, int y, boolean init_game)
2028 InitField(x, y, init_game);
2030 // not needed to call InitMovDir() -- already done by InitField()!
2031 if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2032 CAN_MOVE(Feld[x][y]))
2036 static void InitField_WithBug2(int x, int y, boolean init_game)
2038 int old_element = Feld[x][y];
2040 InitField(x, y, init_game);
2042 // not needed to call InitMovDir() -- already done by InitField()!
2043 if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2044 CAN_MOVE(old_element) &&
2045 (old_element < EL_MOLE_LEFT || old_element > EL_MOLE_DOWN))
2048 /* this case is in fact a combination of not less than three bugs:
2049 first, it calls InitMovDir() for elements that can move, although this is
2050 already done by InitField(); then, it checks the element that was at this
2051 field _before_ the call to InitField() (which can change it); lastly, it
2052 was not called for "mole with direction" elements, which were treated as
2053 "cannot move" due to (fixed) wrong element initialization in "src/init.c"
2057 static int get_key_element_from_nr(int key_nr)
2059 int key_base_element = (key_nr >= STD_NUM_KEYS ? EL_EMC_KEY_5 - STD_NUM_KEYS :
2060 level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2061 EL_EM_KEY_1 : EL_KEY_1);
2063 return key_base_element + key_nr;
2066 static int get_next_dropped_element(struct PlayerInfo *player)
2068 return (player->inventory_size > 0 ?
2069 player->inventory_element[player->inventory_size - 1] :
2070 player->inventory_infinite_element != EL_UNDEFINED ?
2071 player->inventory_infinite_element :
2072 player->dynabombs_left > 0 ?
2073 EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
2077 static int get_inventory_element_from_pos(struct PlayerInfo *player, int pos)
2079 // pos >= 0: get element from bottom of the stack;
2080 // pos < 0: get element from top of the stack
2084 int min_inventory_size = -pos;
2085 int inventory_pos = player->inventory_size - min_inventory_size;
2086 int min_dynabombs_left = min_inventory_size - player->inventory_size;
2088 return (player->inventory_size >= min_inventory_size ?
2089 player->inventory_element[inventory_pos] :
2090 player->inventory_infinite_element != EL_UNDEFINED ?
2091 player->inventory_infinite_element :
2092 player->dynabombs_left >= min_dynabombs_left ?
2093 EL_DYNABOMB_PLAYER_1 + player->index_nr :
2098 int min_dynabombs_left = pos + 1;
2099 int min_inventory_size = pos + 1 - player->dynabombs_left;
2100 int inventory_pos = pos - player->dynabombs_left;
2102 return (player->inventory_infinite_element != EL_UNDEFINED ?
2103 player->inventory_infinite_element :
2104 player->dynabombs_left >= min_dynabombs_left ?
2105 EL_DYNABOMB_PLAYER_1 + player->index_nr :
2106 player->inventory_size >= min_inventory_size ?
2107 player->inventory_element[inventory_pos] :
2112 static int compareGamePanelOrderInfo(const void *object1, const void *object2)
2114 const struct GamePanelOrderInfo *gpo1 = (struct GamePanelOrderInfo *)object1;
2115 const struct GamePanelOrderInfo *gpo2 = (struct GamePanelOrderInfo *)object2;
2118 if (gpo1->sort_priority != gpo2->sort_priority)
2119 compare_result = gpo1->sort_priority - gpo2->sort_priority;
2121 compare_result = gpo1->nr - gpo2->nr;
2123 return compare_result;
2126 int getPlayerInventorySize(int player_nr)
2128 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2129 return game_em.ply[player_nr]->dynamite;
2130 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
2131 return game_sp.red_disk_count;
2133 return stored_player[player_nr].inventory_size;
2136 static void InitGameControlValues(void)
2140 for (i = 0; game_panel_controls[i].nr != -1; i++)
2142 struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2143 struct GamePanelOrderInfo *gpo = &game_panel_order[i];
2144 struct TextPosInfo *pos = gpc->pos;
2146 int type = gpc->type;
2150 Error(ERR_INFO, "'game_panel_controls' structure corrupted at %d", i);
2151 Error(ERR_EXIT, "this should not happen -- please debug");
2154 // force update of game controls after initialization
2155 gpc->value = gpc->last_value = -1;
2156 gpc->frame = gpc->last_frame = -1;
2157 gpc->gfx_frame = -1;
2159 // determine panel value width for later calculation of alignment
2160 if (type == TYPE_INTEGER || type == TYPE_STRING)
2162 pos->width = pos->size * getFontWidth(pos->font);
2163 pos->height = getFontHeight(pos->font);
2165 else if (type == TYPE_ELEMENT)
2167 pos->width = pos->size;
2168 pos->height = pos->size;
2171 // fill structure for game panel draw order
2173 gpo->sort_priority = pos->sort_priority;
2176 // sort game panel controls according to sort_priority and control number
2177 qsort(game_panel_order, NUM_GAME_PANEL_CONTROLS,
2178 sizeof(struct GamePanelOrderInfo), compareGamePanelOrderInfo);
2181 static void UpdatePlayfieldElementCount(void)
2183 boolean use_element_count = FALSE;
2186 // first check if it is needed at all to calculate playfield element count
2187 for (i = GAME_PANEL_ELEMENT_COUNT_1; i <= GAME_PANEL_ELEMENT_COUNT_8; i++)
2188 if (!PANEL_DEACTIVATED(game_panel_controls[i].pos))
2189 use_element_count = TRUE;
2191 if (!use_element_count)
2194 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2195 element_info[i].element_count = 0;
2197 SCAN_PLAYFIELD(x, y)
2199 element_info[Feld[x][y]].element_count++;
2202 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
2203 for (j = 0; j < MAX_NUM_ELEMENTS; j++)
2204 if (IS_IN_GROUP(j, i))
2205 element_info[EL_GROUP_START + i].element_count +=
2206 element_info[j].element_count;
2209 static void UpdateGameControlValues(void)
2212 int time = (game.LevelSolved ?
2213 game.LevelSolved_CountingTime :
2214 level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2216 level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2217 game_sp.time_played :
2218 level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2219 game_mm.energy_left :
2220 game.no_time_limit ? TimePlayed : TimeLeft);
2221 int score = (game.LevelSolved ?
2222 game.LevelSolved_CountingScore :
2223 level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2224 game_em.lev->score :
2225 level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2227 level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2230 int gems = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2231 game_em.lev->gems_needed :
2232 level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2233 game_sp.infotrons_still_needed :
2234 level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2235 game_mm.kettles_still_needed :
2236 game.gems_still_needed);
2237 int exit_closed = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2238 game_em.lev->gems_needed > 0 :
2239 level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2240 game_sp.infotrons_still_needed > 0 :
2241 level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2242 game_mm.kettles_still_needed > 0 ||
2243 game_mm.lights_still_needed > 0 :
2244 game.gems_still_needed > 0 ||
2245 game.sokoban_fields_still_needed > 0 ||
2246 game.sokoban_objects_still_needed > 0 ||
2247 game.lights_still_needed > 0);
2248 int health = (game.LevelSolved ?
2249 game.LevelSolved_CountingHealth :
2250 level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2251 MM_HEALTH(game_mm.laser_overload_value) :
2254 UpdatePlayfieldElementCount();
2256 // update game panel control values
2258 // used instead of "level_nr" (for network games)
2259 game_panel_controls[GAME_PANEL_LEVEL_NUMBER].value = levelset.level_nr;
2260 game_panel_controls[GAME_PANEL_GEMS].value = gems;
2262 game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value = 0;
2263 for (i = 0; i < MAX_NUM_KEYS; i++)
2264 game_panel_controls[GAME_PANEL_KEY_1 + i].value = EL_EMPTY;
2265 game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_EMPTY;
2266 game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value = 0;
2268 if (game.centered_player_nr == -1)
2270 for (i = 0; i < MAX_PLAYERS; i++)
2272 // only one player in Supaplex game engine
2273 if (level.game_engine_type == GAME_ENGINE_TYPE_SP && i > 0)
2276 for (k = 0; k < MAX_NUM_KEYS; k++)
2278 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2280 if (game_em.ply[i]->keys & (1 << k))
2281 game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2282 get_key_element_from_nr(k);
2284 else if (stored_player[i].key[k])
2285 game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2286 get_key_element_from_nr(k);
2289 game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2290 getPlayerInventorySize(i);
2292 if (stored_player[i].num_white_keys > 0)
2293 game_panel_controls[GAME_PANEL_KEY_WHITE].value =
2296 game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2297 stored_player[i].num_white_keys;
2302 int player_nr = game.centered_player_nr;
2304 for (k = 0; k < MAX_NUM_KEYS; k++)
2306 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2308 if (game_em.ply[player_nr]->keys & (1 << k))
2309 game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2310 get_key_element_from_nr(k);
2312 else if (stored_player[player_nr].key[k])
2313 game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2314 get_key_element_from_nr(k);
2317 game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2318 getPlayerInventorySize(player_nr);
2320 if (stored_player[player_nr].num_white_keys > 0)
2321 game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_DC_KEY_WHITE;
2323 game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2324 stored_player[player_nr].num_white_keys;
2327 for (i = 0; i < NUM_PANEL_INVENTORY; i++)
2329 game_panel_controls[GAME_PANEL_INVENTORY_FIRST_1 + i].value =
2330 get_inventory_element_from_pos(local_player, i);
2331 game_panel_controls[GAME_PANEL_INVENTORY_LAST_1 + i].value =
2332 get_inventory_element_from_pos(local_player, -i - 1);
2335 game_panel_controls[GAME_PANEL_SCORE].value = score;
2336 game_panel_controls[GAME_PANEL_HIGHSCORE].value = highscore[0].Score;
2338 game_panel_controls[GAME_PANEL_TIME].value = time;
2340 game_panel_controls[GAME_PANEL_TIME_HH].value = time / 3600;
2341 game_panel_controls[GAME_PANEL_TIME_MM].value = (time / 60) % 60;
2342 game_panel_controls[GAME_PANEL_TIME_SS].value = time % 60;
2344 if (level.time == 0)
2345 game_panel_controls[GAME_PANEL_TIME_ANIM].value = 100;
2347 game_panel_controls[GAME_PANEL_TIME_ANIM].value = time * 100 / level.time;
2349 game_panel_controls[GAME_PANEL_HEALTH].value = health;
2350 game_panel_controls[GAME_PANEL_HEALTH_ANIM].value = health;
2352 game_panel_controls[GAME_PANEL_FRAME].value = FrameCounter;
2354 game_panel_controls[GAME_PANEL_SHIELD_NORMAL].value =
2355 (local_player->shield_normal_time_left > 0 ? EL_SHIELD_NORMAL_ACTIVE :
2357 game_panel_controls[GAME_PANEL_SHIELD_NORMAL_TIME].value =
2358 local_player->shield_normal_time_left;
2359 game_panel_controls[GAME_PANEL_SHIELD_DEADLY].value =
2360 (local_player->shield_deadly_time_left > 0 ? EL_SHIELD_DEADLY_ACTIVE :
2362 game_panel_controls[GAME_PANEL_SHIELD_DEADLY_TIME].value =
2363 local_player->shield_deadly_time_left;
2365 game_panel_controls[GAME_PANEL_EXIT].value =
2366 (exit_closed ? EL_EXIT_CLOSED : EL_EXIT_OPEN);
2368 game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL].value =
2369 (game.ball_active ? EL_EMC_MAGIC_BALL_ACTIVE : EL_EMC_MAGIC_BALL);
2370 game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL_SWITCH].value =
2371 (game.ball_active ? EL_EMC_MAGIC_BALL_SWITCH_ACTIVE :
2372 EL_EMC_MAGIC_BALL_SWITCH);
2374 game_panel_controls[GAME_PANEL_LIGHT_SWITCH].value =
2375 (game.light_time_left > 0 ? EL_LIGHT_SWITCH_ACTIVE : EL_LIGHT_SWITCH);
2376 game_panel_controls[GAME_PANEL_LIGHT_SWITCH_TIME].value =
2377 game.light_time_left;
2379 game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH].value =
2380 (game.timegate_time_left > 0 ? EL_TIMEGATE_OPEN : EL_TIMEGATE_CLOSED);
2381 game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH_TIME].value =
2382 game.timegate_time_left;
2384 game_panel_controls[GAME_PANEL_SWITCHGATE_SWITCH].value =
2385 EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
2387 game_panel_controls[GAME_PANEL_EMC_LENSES].value =
2388 (game.lenses_time_left > 0 ? EL_EMC_LENSES : EL_EMPTY);
2389 game_panel_controls[GAME_PANEL_EMC_LENSES_TIME].value =
2390 game.lenses_time_left;
2392 game_panel_controls[GAME_PANEL_EMC_MAGNIFIER].value =
2393 (game.magnify_time_left > 0 ? EL_EMC_MAGNIFIER : EL_EMPTY);
2394 game_panel_controls[GAME_PANEL_EMC_MAGNIFIER_TIME].value =
2395 game.magnify_time_left;
2397 game_panel_controls[GAME_PANEL_BALLOON_SWITCH].value =
2398 (game.wind_direction == MV_LEFT ? EL_BALLOON_SWITCH_LEFT :
2399 game.wind_direction == MV_RIGHT ? EL_BALLOON_SWITCH_RIGHT :
2400 game.wind_direction == MV_UP ? EL_BALLOON_SWITCH_UP :
2401 game.wind_direction == MV_DOWN ? EL_BALLOON_SWITCH_DOWN :
2402 EL_BALLOON_SWITCH_NONE);
2404 game_panel_controls[GAME_PANEL_DYNABOMB_NUMBER].value =
2405 local_player->dynabomb_count;
2406 game_panel_controls[GAME_PANEL_DYNABOMB_SIZE].value =
2407 local_player->dynabomb_size;
2408 game_panel_controls[GAME_PANEL_DYNABOMB_POWER].value =
2409 (local_player->dynabomb_xl ? EL_DYNABOMB_INCREASE_POWER : EL_EMPTY);
2411 game_panel_controls[GAME_PANEL_PENGUINS].value =
2412 game.friends_still_needed;
2414 game_panel_controls[GAME_PANEL_SOKOBAN_OBJECTS].value =
2415 game.sokoban_objects_still_needed;
2416 game_panel_controls[GAME_PANEL_SOKOBAN_FIELDS].value =
2417 game.sokoban_fields_still_needed;
2419 game_panel_controls[GAME_PANEL_ROBOT_WHEEL].value =
2420 (game.robot_wheel_active ? EL_ROBOT_WHEEL_ACTIVE : EL_ROBOT_WHEEL);
2422 for (i = 0; i < NUM_BELTS; i++)
2424 game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1 + i].value =
2425 (game.belt_dir[i] != MV_NONE ? EL_CONVEYOR_BELT_1_MIDDLE_ACTIVE :
2426 EL_CONVEYOR_BELT_1_MIDDLE) + i;
2427 game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1_SWITCH + i].value =
2428 getBeltSwitchElementFromBeltNrAndBeltDir(i, game.belt_dir[i]);
2431 game_panel_controls[GAME_PANEL_MAGIC_WALL].value =
2432 (game.magic_wall_active ? EL_MAGIC_WALL_ACTIVE : EL_MAGIC_WALL);
2433 game_panel_controls[GAME_PANEL_MAGIC_WALL_TIME].value =
2434 game.magic_wall_time_left;
2436 game_panel_controls[GAME_PANEL_GRAVITY_STATE].value =
2437 local_player->gravity;
2439 for (i = 0; i < NUM_PANEL_GRAPHICS; i++)
2440 game_panel_controls[GAME_PANEL_GRAPHIC_1 + i].value = EL_GRAPHIC_1 + i;
2442 for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2443 game_panel_controls[GAME_PANEL_ELEMENT_1 + i].value =
2444 (IS_DRAWABLE_ELEMENT(game.panel.element[i].id) ?
2445 game.panel.element[i].id : EL_UNDEFINED);
2447 for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2448 game_panel_controls[GAME_PANEL_ELEMENT_COUNT_1 + i].value =
2449 (IS_VALID_ELEMENT(game.panel.element_count[i].id) ?
2450 element_info[game.panel.element_count[i].id].element_count : 0);
2452 for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2453 game_panel_controls[GAME_PANEL_CE_SCORE_1 + i].value =
2454 (IS_CUSTOM_ELEMENT(game.panel.ce_score[i].id) ?
2455 element_info[game.panel.ce_score[i].id].collect_score : 0);
2457 for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2458 game_panel_controls[GAME_PANEL_CE_SCORE_1_ELEMENT + i].value =
2459 (IS_CUSTOM_ELEMENT(game.panel.ce_score_element[i].id) ?
2460 element_info[game.panel.ce_score_element[i].id].collect_score :
2463 game_panel_controls[GAME_PANEL_PLAYER_NAME].value = 0;
2464 game_panel_controls[GAME_PANEL_LEVEL_NAME].value = 0;
2465 game_panel_controls[GAME_PANEL_LEVEL_AUTHOR].value = 0;
2467 // update game panel control frames
2469 for (i = 0; game_panel_controls[i].nr != -1; i++)
2471 struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2473 if (gpc->type == TYPE_ELEMENT)
2475 if (gpc->value != EL_UNDEFINED && gpc->value != EL_EMPTY)
2477 int last_anim_random_frame = gfx.anim_random_frame;
2478 int element = gpc->value;
2479 int graphic = el2panelimg(element);
2481 if (gpc->value != gpc->last_value)
2484 gpc->gfx_random = INIT_GFX_RANDOM();
2490 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2491 IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2492 gpc->gfx_random = INIT_GFX_RANDOM();
2495 if (ANIM_MODE(graphic) == ANIM_RANDOM)
2496 gfx.anim_random_frame = gpc->gfx_random;
2498 if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
2499 gpc->gfx_frame = element_info[element].collect_score;
2501 gpc->frame = getGraphicAnimationFrame(el2panelimg(gpc->value),
2504 if (ANIM_MODE(graphic) == ANIM_RANDOM)
2505 gfx.anim_random_frame = last_anim_random_frame;
2508 else if (gpc->type == TYPE_GRAPHIC)
2510 if (gpc->graphic != IMG_UNDEFINED)
2512 int last_anim_random_frame = gfx.anim_random_frame;
2513 int graphic = gpc->graphic;
2515 if (gpc->value != gpc->last_value)
2518 gpc->gfx_random = INIT_GFX_RANDOM();
2524 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2525 IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2526 gpc->gfx_random = INIT_GFX_RANDOM();
2529 if (ANIM_MODE(graphic) == ANIM_RANDOM)
2530 gfx.anim_random_frame = gpc->gfx_random;
2532 gpc->frame = getGraphicAnimationFrame(graphic, gpc->gfx_frame);
2534 if (ANIM_MODE(graphic) == ANIM_RANDOM)
2535 gfx.anim_random_frame = last_anim_random_frame;
2541 static void DisplayGameControlValues(void)
2543 boolean redraw_panel = FALSE;
2546 for (i = 0; game_panel_controls[i].nr != -1; i++)
2548 struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2550 if (PANEL_DEACTIVATED(gpc->pos))
2553 if (gpc->value == gpc->last_value &&
2554 gpc->frame == gpc->last_frame)
2557 redraw_panel = TRUE;
2563 // copy default game door content to main double buffer
2565 // !!! CHECK AGAIN !!!
2566 SetPanelBackground();
2567 // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
2568 DrawBackground(DX, DY, DXSIZE, DYSIZE);
2570 // redraw game control buttons
2571 RedrawGameButtons();
2573 SetGameStatus(GAME_MODE_PSEUDO_PANEL);
2575 for (i = 0; i < NUM_GAME_PANEL_CONTROLS; i++)
2577 int nr = game_panel_order[i].nr;
2578 struct GamePanelControlInfo *gpc = &game_panel_controls[nr];
2579 struct TextPosInfo *pos = gpc->pos;
2580 int type = gpc->type;
2581 int value = gpc->value;
2582 int frame = gpc->frame;
2583 int size = pos->size;
2584 int font = pos->font;
2585 boolean draw_masked = pos->draw_masked;
2586 int mask_mode = (draw_masked ? BLIT_MASKED : BLIT_OPAQUE);
2588 if (PANEL_DEACTIVATED(pos))
2591 gpc->last_value = value;
2592 gpc->last_frame = frame;
2594 if (type == TYPE_INTEGER)
2596 if (nr == GAME_PANEL_LEVEL_NUMBER ||
2597 nr == GAME_PANEL_TIME)
2599 boolean use_dynamic_size = (size == -1 ? TRUE : FALSE);
2601 if (use_dynamic_size) // use dynamic number of digits
2603 int value_change = (nr == GAME_PANEL_LEVEL_NUMBER ? 100 : 1000);
2604 int size1 = (nr == GAME_PANEL_LEVEL_NUMBER ? 2 : 3);
2605 int size2 = size1 + 1;
2606 int font1 = pos->font;
2607 int font2 = pos->font_alt;
2609 size = (value < value_change ? size1 : size2);
2610 font = (value < value_change ? font1 : font2);
2614 // correct text size if "digits" is zero or less
2616 size = strlen(int2str(value, size));
2618 // dynamically correct text alignment
2619 pos->width = size * getFontWidth(font);
2621 DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2622 int2str(value, size), font, mask_mode);
2624 else if (type == TYPE_ELEMENT)
2626 int element, graphic;
2630 int dst_x = PANEL_XPOS(pos);
2631 int dst_y = PANEL_YPOS(pos);
2633 if (value != EL_UNDEFINED && value != EL_EMPTY)
2636 graphic = el2panelimg(value);
2638 // printf("::: %d, '%s' [%d]\n", element, EL_NAME(element), size);
2640 if (element >= EL_GRAPHIC_1 && element <= EL_GRAPHIC_8 && size == 0)
2643 getSizedGraphicSource(graphic, frame, size, &src_bitmap,
2646 width = graphic_info[graphic].width * size / TILESIZE;
2647 height = graphic_info[graphic].height * size / TILESIZE;
2650 BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2653 BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2657 else if (type == TYPE_GRAPHIC)
2659 int graphic = gpc->graphic;
2660 int graphic_active = gpc->graphic_active;
2664 int dst_x = PANEL_XPOS(pos);
2665 int dst_y = PANEL_YPOS(pos);
2666 boolean skip = (pos->class == get_hash_from_key("mm_engine_only") &&
2667 level.game_engine_type != GAME_ENGINE_TYPE_MM);
2669 if (graphic != IMG_UNDEFINED && !skip)
2671 if (pos->style == STYLE_REVERSE)
2672 value = 100 - value;
2674 getGraphicSource(graphic_active, frame, &src_bitmap, &src_x, &src_y);
2676 if (pos->direction & MV_HORIZONTAL)
2678 width = graphic_info[graphic_active].width * value / 100;
2679 height = graphic_info[graphic_active].height;
2681 if (pos->direction == MV_LEFT)
2683 src_x += graphic_info[graphic_active].width - width;
2684 dst_x += graphic_info[graphic_active].width - width;
2689 width = graphic_info[graphic_active].width;
2690 height = graphic_info[graphic_active].height * value / 100;
2692 if (pos->direction == MV_UP)
2694 src_y += graphic_info[graphic_active].height - height;
2695 dst_y += graphic_info[graphic_active].height - height;
2700 BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2703 BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2706 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
2708 if (pos->direction & MV_HORIZONTAL)
2710 if (pos->direction == MV_RIGHT)
2717 dst_x = PANEL_XPOS(pos);
2720 width = graphic_info[graphic].width - width;
2724 if (pos->direction == MV_DOWN)
2731 dst_y = PANEL_YPOS(pos);
2734 height = graphic_info[graphic].height - height;
2738 BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2741 BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2745 else if (type == TYPE_STRING)
2747 boolean active = (value != 0);
2748 char *state_normal = "off";
2749 char *state_active = "on";
2750 char *state = (active ? state_active : state_normal);
2751 char *s = (nr == GAME_PANEL_GRAVITY_STATE ? state :
2752 nr == GAME_PANEL_PLAYER_NAME ? setup.player_name :
2753 nr == GAME_PANEL_LEVEL_NAME ? level.name :
2754 nr == GAME_PANEL_LEVEL_AUTHOR ? level.author : NULL);
2756 if (nr == GAME_PANEL_GRAVITY_STATE)
2758 int font1 = pos->font; // (used for normal state)
2759 int font2 = pos->font_alt; // (used for active state)
2761 font = (active ? font2 : font1);
2770 // don't truncate output if "chars" is zero or less
2773 // dynamically correct text alignment
2774 pos->width = size * getFontWidth(font);
2777 s_cut = getStringCopyN(s, size);
2779 DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2780 s_cut, font, mask_mode);
2786 redraw_mask |= REDRAW_DOOR_1;
2789 SetGameStatus(GAME_MODE_PLAYING);
2792 void UpdateAndDisplayGameControlValues(void)
2794 if (tape.deactivate_display)
2797 UpdateGameControlValues();
2798 DisplayGameControlValues();
2802 static void UpdateGameDoorValues(void)
2804 UpdateGameControlValues();
2808 void DrawGameDoorValues(void)
2810 DisplayGameControlValues();
2814 // ============================================================================
2816 // ----------------------------------------------------------------------------
2817 // initialize game engine due to level / tape version number
2818 // ============================================================================
2820 static void InitGameEngine(void)
2822 int i, j, k, l, x, y;
2824 // set game engine from tape file when re-playing, else from level file
2825 game.engine_version = (tape.playing ? tape.engine_version :
2826 level.game_version);
2828 // set single or multi-player game mode (needed for re-playing tapes)
2829 game.team_mode = setup.team_mode;
2833 int num_players = 0;
2835 for (i = 0; i < MAX_PLAYERS; i++)
2836 if (tape.player_participates[i])
2839 // multi-player tapes contain input data for more than one player
2840 game.team_mode = (num_players > 1);
2843 // --------------------------------------------------------------------------
2844 // set flags for bugs and changes according to active game engine version
2845 // --------------------------------------------------------------------------
2849 Fixed property "can fall" for run-time element "EL_AMOEBA_DROPPING"
2851 Bug was introduced in version:
2854 Bug was fixed in version:
2858 In version 2.0.1, a new run-time element "EL_AMOEBA_DROPPING" was added,
2859 but the property "can fall" was missing, which caused some levels to be
2860 unsolvable. This was fixed in version 4.1.4.2.
2862 Affected levels/tapes:
2863 An example for a tape that was fixed by this bugfix is tape 029 from the
2864 level set "rnd_sam_bateman".
2865 The wrong behaviour will still be used for all levels or tapes that were
2866 created/recorded with it. An example for this is tape 023 from the level
2867 set "rnd_gerhard_haeusler", which was recorded with a buggy game engine.
2870 boolean use_amoeba_dropping_cannot_fall_bug =
2871 ((game.engine_version >= VERSION_IDENT(2,0,1,0) &&
2872 game.engine_version <= VERSION_IDENT(4,1,4,1)) ||
2874 tape.game_version >= VERSION_IDENT(2,0,1,0) &&
2875 tape.game_version <= VERSION_IDENT(4,1,4,1)));
2878 Summary of bugfix/change:
2879 Fixed move speed of elements entering or leaving magic wall.
2881 Fixed/changed in version:
2885 Before 2.0.1, move speed of elements entering or leaving magic wall was
2886 twice as fast as it is now.
2887 Since 2.0.1, this is set to a lower value by using move_stepsize_list[].
2889 Affected levels/tapes:
2890 The first condition is generally needed for all levels/tapes before version
2891 2.0.1, which might use the old behaviour before it was changed; known tapes
2892 that are affected: Tape 014 from the level set "rnd_conor_mancone".
2893 The second condition is an exception from the above case and is needed for
2894 the special case of tapes recorded with game (not engine!) version 2.0.1 or
2895 above, but before it was known that this change would break tapes like the
2896 above and was fixed in 4.1.4.2, so that the changed behaviour was active
2897 although the engine version while recording maybe was before 2.0.1. There
2898 are a lot of tapes that are affected by this exception, like tape 006 from
2899 the level set "rnd_conor_mancone".
2902 boolean use_old_move_stepsize_for_magic_wall =
2903 (game.engine_version < VERSION_IDENT(2,0,1,0) &&
2905 tape.game_version >= VERSION_IDENT(2,0,1,0) &&
2906 tape.game_version < VERSION_IDENT(4,1,4,2)));
2909 Summary of bugfix/change:
2910 Fixed handling for custom elements that change when pushed by the player.
2912 Fixed/changed in version:
2916 Before 3.1.0, custom elements that "change when pushing" changed directly
2917 after the player started pushing them (until then handled in "DigField()").
2918 Since 3.1.0, these custom elements are not changed until the "pushing"
2919 move of the element is finished (now handled in "ContinueMoving()").
2921 Affected levels/tapes:
2922 The first condition is generally needed for all levels/tapes before version
2923 3.1.0, which might use the old behaviour before it was changed; known tapes
2924 that are affected are some tapes from the level set "Walpurgis Gardens" by
2926 The second condition is an exception from the above case and is needed for
2927 the special case of tapes recorded with game (not engine!) version 3.1.0 or
2928 above (including some development versions of 3.1.0), but before it was
2929 known that this change would break tapes like the above and was fixed in
2930 3.1.1, so that the changed behaviour was active although the engine version
2931 while recording maybe was before 3.1.0. There is at least one tape that is
2932 affected by this exception, which is the tape for the one-level set "Bug
2933 Machine" by Juergen Bonhagen.
2936 game.use_change_when_pushing_bug =
2937 (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2939 tape.game_version >= VERSION_IDENT(3,1,0,0) &&
2940 tape.game_version < VERSION_IDENT(3,1,1,0)));
2943 Summary of bugfix/change:
2944 Fixed handling for blocking the field the player leaves when moving.
2946 Fixed/changed in version:
2950 Before 3.1.1, when "block last field when moving" was enabled, the field
2951 the player is leaving when moving was blocked for the time of the move,
2952 and was directly unblocked afterwards. This resulted in the last field
2953 being blocked for exactly one less than the number of frames of one player
2954 move. Additionally, even when blocking was disabled, the last field was
2955 blocked for exactly one frame.
2956 Since 3.1.1, due to changes in player movement handling, the last field
2957 is not blocked at all when blocking is disabled. When blocking is enabled,
2958 the last field is blocked for exactly the number of frames of one player
2959 move. Additionally, if the player is Murphy, the hero of Supaplex, the
2960 last field is blocked for exactly one more than the number of frames of
2963 Affected levels/tapes:
2964 (!!! yet to be determined -- probably many !!!)
2967 game.use_block_last_field_bug =
2968 (game.engine_version < VERSION_IDENT(3,1,1,0));
2970 /* various special flags and settings for native Emerald Mine game engine */
2972 game_em.use_single_button =
2973 (game.engine_version > VERSION_IDENT(4,0,0,2));
2975 game_em.use_snap_key_bug =
2976 (game.engine_version < VERSION_IDENT(4,0,1,0));
2978 game_em.use_old_explosions =
2979 (game.engine_version < VERSION_IDENT(4,1,4,2));
2981 // --------------------------------------------------------------------------
2983 // set maximal allowed number of custom element changes per game frame
2984 game.max_num_changes_per_frame = 1;
2986 // default scan direction: scan playfield from top/left to bottom/right
2987 InitPlayfieldScanMode(CA_ARG_SCAN_MODE_NORMAL);
2989 // dynamically adjust element properties according to game engine version
2990 InitElementPropertiesEngine(game.engine_version);
2992 // ---------- initialize special element properties -------------------------
2994 // "EL_AMOEBA_DROPPING" missed property "can fall" between 2.0.1 and 4.1.4.1
2995 if (use_amoeba_dropping_cannot_fall_bug)
2996 SET_PROPERTY(EL_AMOEBA_DROPPING, EP_CAN_FALL, FALSE);
2999 printf("level %d: level version == %06d\n", level_nr, level.game_version);
3000 printf(" tape version == %06d [%s] [file: %06d]\n",
3001 tape.engine_version, (tape.playing ? "PLAYING" : "RECORDING"),
3003 printf(" => game.engine_version == %06d\n", game.engine_version);
3006 // ---------- initialize player's initial move delay ------------------------
3008 // dynamically adjust player properties according to level information
3009 for (i = 0; i < MAX_PLAYERS; i++)
3010 game.initial_move_delay_value[i] =
3011 get_move_delay_from_stepsize(level.initial_player_stepsize[i]);
3013 // dynamically adjust player properties according to game engine version
3014 for (i = 0; i < MAX_PLAYERS; i++)
3015 game.initial_move_delay[i] =
3016 (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
3017 game.initial_move_delay_value[i] : 0);
3019 // ---------- initialize player's initial push delay ------------------------
3021 // dynamically adjust player properties according to game engine version
3022 game.initial_push_delay_value =
3023 (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
3025 // ---------- initialize changing elements ----------------------------------
3027 // initialize changing elements information
3028 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3030 struct ElementInfo *ei = &element_info[i];
3032 // this pointer might have been changed in the level editor
3033 ei->change = &ei->change_page[0];
3035 if (!IS_CUSTOM_ELEMENT(i))
3037 ei->change->target_element = EL_EMPTY_SPACE;
3038 ei->change->delay_fixed = 0;
3039 ei->change->delay_random = 0;
3040 ei->change->delay_frames = 1;
3043 for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3045 ei->has_change_event[j] = FALSE;
3047 ei->event_page_nr[j] = 0;
3048 ei->event_page[j] = &ei->change_page[0];
3052 // add changing elements from pre-defined list
3053 for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
3055 struct ChangingElementInfo *ch_delay = &change_delay_list[i];
3056 struct ElementInfo *ei = &element_info[ch_delay->element];
3058 ei->change->target_element = ch_delay->target_element;
3059 ei->change->delay_fixed = ch_delay->change_delay;
3061 ei->change->pre_change_function = ch_delay->pre_change_function;
3062 ei->change->change_function = ch_delay->change_function;
3063 ei->change->post_change_function = ch_delay->post_change_function;
3065 ei->change->can_change = TRUE;
3066 ei->change->can_change_or_has_action = TRUE;
3068 ei->has_change_event[CE_DELAY] = TRUE;
3070 SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
3071 SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE_OR_HAS_ACTION, TRUE);
3074 // ---------- initialize internal run-time variables ------------------------
3076 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3078 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3080 for (j = 0; j < ei->num_change_pages; j++)
3082 ei->change_page[j].can_change_or_has_action =
3083 (ei->change_page[j].can_change |
3084 ei->change_page[j].has_action);
3088 // add change events from custom element configuration
3089 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3091 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3093 for (j = 0; j < ei->num_change_pages; j++)
3095 if (!ei->change_page[j].can_change_or_has_action)
3098 for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3100 // only add event page for the first page found with this event
3101 if (ei->change_page[j].has_event[k] && !(ei->has_change_event[k]))
3103 ei->has_change_event[k] = TRUE;
3105 ei->event_page_nr[k] = j;
3106 ei->event_page[k] = &ei->change_page[j];
3112 // ---------- initialize reference elements in change conditions ------------
3114 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3116 int element = EL_CUSTOM_START + i;
3117 struct ElementInfo *ei = &element_info[element];
3119 for (j = 0; j < ei->num_change_pages; j++)
3121 int trigger_element = ei->change_page[j].initial_trigger_element;
3123 if (trigger_element >= EL_PREV_CE_8 &&
3124 trigger_element <= EL_NEXT_CE_8)
3125 trigger_element = RESOLVED_REFERENCE_ELEMENT(element, trigger_element);
3127 ei->change_page[j].trigger_element = trigger_element;
3131 // ---------- initialize run-time trigger player and element ----------------
3133 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3135 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3137 for (j = 0; j < ei->num_change_pages; j++)
3139 ei->change_page[j].actual_trigger_element = EL_EMPTY;
3140 ei->change_page[j].actual_trigger_player = EL_EMPTY;
3141 ei->change_page[j].actual_trigger_player_bits = CH_PLAYER_NONE;
3142 ei->change_page[j].actual_trigger_side = CH_SIDE_NONE;
3143 ei->change_page[j].actual_trigger_ce_value = 0;
3144 ei->change_page[j].actual_trigger_ce_score = 0;
3148 // ---------- initialize trigger events -------------------------------------
3150 // initialize trigger events information
3151 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3152 for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3153 trigger_events[i][j] = FALSE;
3155 // add trigger events from element change event properties
3156 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3158 struct ElementInfo *ei = &element_info[i];
3160 for (j = 0; j < ei->num_change_pages; j++)
3162 if (!ei->change_page[j].can_change_or_has_action)
3165 if (ei->change_page[j].has_event[CE_BY_OTHER_ACTION])
3167 int trigger_element = ei->change_page[j].trigger_element;
3169 for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3171 if (ei->change_page[j].has_event[k])
3173 if (IS_GROUP_ELEMENT(trigger_element))
3175 struct ElementGroupInfo *group =
3176 element_info[trigger_element].group;
3178 for (l = 0; l < group->num_elements_resolved; l++)
3179 trigger_events[group->element_resolved[l]][k] = TRUE;
3181 else if (trigger_element == EL_ANY_ELEMENT)
3182 for (l = 0; l < MAX_NUM_ELEMENTS; l++)
3183 trigger_events[l][k] = TRUE;
3185 trigger_events[trigger_element][k] = TRUE;
3192 // ---------- initialize push delay -----------------------------------------
3194 // initialize push delay values to default
3195 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3197 if (!IS_CUSTOM_ELEMENT(i))
3199 // set default push delay values (corrected since version 3.0.7-1)
3200 if (game.engine_version < VERSION_IDENT(3,0,7,1))
3202 element_info[i].push_delay_fixed = 2;
3203 element_info[i].push_delay_random = 8;
3207 element_info[i].push_delay_fixed = 8;
3208 element_info[i].push_delay_random = 8;
3213 // set push delay value for certain elements from pre-defined list
3214 for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
3216 int e = push_delay_list[i].element;
3218 element_info[e].push_delay_fixed = push_delay_list[i].push_delay_fixed;
3219 element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
3222 // set push delay value for Supaplex elements for newer engine versions
3223 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
3225 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3227 if (IS_SP_ELEMENT(i))
3229 // set SP push delay to just enough to push under a falling zonk
3230 int delay = (game.engine_version >= VERSION_IDENT(3,1,1,0) ? 8 : 6);
3232 element_info[i].push_delay_fixed = delay;
3233 element_info[i].push_delay_random = 0;
3238 // ---------- initialize move stepsize --------------------------------------
3240 // initialize move stepsize values to default
3241 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3242 if (!IS_CUSTOM_ELEMENT(i))
3243 element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
3245 // set move stepsize value for certain elements from pre-defined list
3246 for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
3248 int e = move_stepsize_list[i].element;
3250 element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
3252 // set move stepsize value for certain elements for older engine versions
3253 if (use_old_move_stepsize_for_magic_wall)
3255 if (e == EL_MAGIC_WALL_FILLING ||
3256 e == EL_MAGIC_WALL_EMPTYING ||
3257 e == EL_BD_MAGIC_WALL_FILLING ||
3258 e == EL_BD_MAGIC_WALL_EMPTYING)
3259 element_info[e].move_stepsize *= 2;
3263 // ---------- initialize collect score --------------------------------------
3265 // initialize collect score values for custom elements from initial value
3266 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3267 if (IS_CUSTOM_ELEMENT(i))
3268 element_info[i].collect_score = element_info[i].collect_score_initial;
3270 // ---------- initialize collect count --------------------------------------
3272 // initialize collect count values for non-custom elements
3273 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3274 if (!IS_CUSTOM_ELEMENT(i))
3275 element_info[i].collect_count_initial = 0;
3277 // add collect count values for all elements from pre-defined list
3278 for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
3279 element_info[collect_count_list[i].element].collect_count_initial =
3280 collect_count_list[i].count;
3282 // ---------- initialize access direction -----------------------------------
3284 // initialize access direction values to default (access from every side)
3285 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3286 if (!IS_CUSTOM_ELEMENT(i))
3287 element_info[i].access_direction = MV_ALL_DIRECTIONS;
3289 // set access direction value for certain elements from pre-defined list
3290 for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
3291 element_info[access_direction_list[i].element].access_direction =
3292 access_direction_list[i].direction;
3294 // ---------- initialize explosion content ----------------------------------
3295 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3297 if (IS_CUSTOM_ELEMENT(i))
3300 for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
3302 // (content for EL_YAMYAM set at run-time with game.yamyam_content_nr)
3304 element_info[i].content.e[x][y] =
3305 (i == EL_PLAYER_1 ? EL_EMERALD_YELLOW :
3306 i == EL_PLAYER_2 ? EL_EMERALD_RED :
3307 i == EL_PLAYER_3 ? EL_EMERALD :
3308 i == EL_PLAYER_4 ? EL_EMERALD_PURPLE :
3309 i == EL_MOLE ? EL_EMERALD_RED :
3310 i == EL_PENGUIN ? EL_EMERALD_PURPLE :
3311 i == EL_BUG ? (x == 1 && y == 1 ? EL_DIAMOND : EL_EMERALD) :
3312 i == EL_BD_BUTTERFLY ? EL_BD_DIAMOND :
3313 i == EL_SP_ELECTRON ? EL_SP_INFOTRON :
3314 i == EL_AMOEBA_TO_DIAMOND ? level.amoeba_content :
3315 i == EL_WALL_EMERALD ? EL_EMERALD :
3316 i == EL_WALL_DIAMOND ? EL_DIAMOND :
3317 i == EL_WALL_BD_DIAMOND ? EL_BD_DIAMOND :
3318 i == EL_WALL_EMERALD_YELLOW ? EL_EMERALD_YELLOW :
3319 i == EL_WALL_EMERALD_RED ? EL_EMERALD_RED :
3320 i == EL_WALL_EMERALD_PURPLE ? EL_EMERALD_PURPLE :
3321 i == EL_WALL_PEARL ? EL_PEARL :
3322 i == EL_WALL_CRYSTAL ? EL_CRYSTAL :
3327 // ---------- initialize recursion detection --------------------------------
3328 recursion_loop_depth = 0;
3329 recursion_loop_detected = FALSE;
3330 recursion_loop_element = EL_UNDEFINED;
3332 // ---------- initialize graphics engine ------------------------------------
3333 game.scroll_delay_value =
3334 (game.forced_scroll_delay_value != -1 ? game.forced_scroll_delay_value :
3335 level.game_engine_type == GAME_ENGINE_TYPE_EM &&
3336 !setup.forced_scroll_delay ? 0 :
3337 setup.scroll_delay ? setup.scroll_delay_value : 0);
3338 game.scroll_delay_value =
3339 MIN(MAX(MIN_SCROLL_DELAY, game.scroll_delay_value), MAX_SCROLL_DELAY);
3341 // ---------- initialize game engine snapshots ------------------------------
3342 for (i = 0; i < MAX_PLAYERS; i++)
3343 game.snapshot.last_action[i] = 0;
3344 game.snapshot.changed_action = FALSE;
3345 game.snapshot.collected_item = FALSE;
3346 game.snapshot.mode =
3347 (strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_STEP) ?
3348 SNAPSHOT_MODE_EVERY_STEP :
3349 strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_MOVE) ?
3350 SNAPSHOT_MODE_EVERY_MOVE :
3351 strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_COLLECT) ?
3352 SNAPSHOT_MODE_EVERY_COLLECT : SNAPSHOT_MODE_OFF);
3353 game.snapshot.save_snapshot = FALSE;
3355 // ---------- initialize level time for Supaplex engine ---------------------
3356 // Supaplex levels with time limit currently unsupported -- should be added
3357 if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
3360 // ---------- initialize flags for handling game actions --------------------
3362 // set flags for game actions to default values
3363 game.use_key_actions = TRUE;
3364 game.use_mouse_actions = FALSE;
3366 // when using Mirror Magic game engine, handle mouse events only
3367 if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
3369 game.use_key_actions = FALSE;
3370 game.use_mouse_actions = TRUE;
3373 // check for custom elements with mouse click events
3374 if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
3376 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3378 int element = EL_CUSTOM_START + i;
3380 if (HAS_CHANGE_EVENT(element, CE_CLICKED_BY_MOUSE) ||
3381 HAS_CHANGE_EVENT(element, CE_PRESSED_BY_MOUSE) ||
3382 HAS_CHANGE_EVENT(element, CE_MOUSE_CLICKED_ON_X) ||
3383 HAS_CHANGE_EVENT(element, CE_MOUSE_PRESSED_ON_X))
3384 game.use_mouse_actions = TRUE;
3389 static int get_num_special_action(int element, int action_first,
3392 int num_special_action = 0;
3395 for (i = action_first; i <= action_last; i++)
3397 boolean found = FALSE;
3399 for (j = 0; j < NUM_DIRECTIONS; j++)
3400 if (el_act_dir2img(element, i, j) !=
3401 el_act_dir2img(element, ACTION_DEFAULT, j))
3405 num_special_action++;
3410 return num_special_action;
3414 // ============================================================================
3416 // ----------------------------------------------------------------------------
3417 // initialize and start new game
3418 // ============================================================================
3420 #if DEBUG_INIT_PLAYER
3421 static void DebugPrintPlayerStatus(char *message)
3428 printf("%s:\n", message);
3430 for (i = 0; i < MAX_PLAYERS; i++)
3432 struct PlayerInfo *player = &stored_player[i];
3434 printf("- player %d: present == %d, connected == %d [%d/%d], active == %d",
3438 player->connected_locally,
3439 player->connected_network,
3442 if (local_player == player)
3443 printf(" (local player)");
3452 int full_lev_fieldx = lev_fieldx + (BorderElement != EL_EMPTY ? 2 : 0);
3453 int full_lev_fieldy = lev_fieldy + (BorderElement != EL_EMPTY ? 2 : 0);
3454 int fade_mask = REDRAW_FIELD;
3456 boolean emulate_bd = TRUE; // unless non-BOULDERDASH elements found
3457 boolean emulate_sb = TRUE; // unless non-SOKOBAN elements found
3458 boolean emulate_sp = TRUE; // unless non-SUPAPLEX elements found
3459 int initial_move_dir = MV_DOWN;
3462 // required here to update video display before fading (FIX THIS)
3463 DrawMaskedBorder(REDRAW_DOOR_2);
3465 if (!game.restart_level)
3466 CloseDoor(DOOR_CLOSE_1);
3468 SetGameStatus(GAME_MODE_PLAYING);
3470 if (level_editor_test_game)
3471 FadeSkipNextFadeOut();
3473 FadeSetEnterScreen();
3476 fade_mask = REDRAW_ALL;
3478 FadeLevelSoundsAndMusic();
3480 ExpireSoundLoops(TRUE);
3484 if (level_editor_test_game)
3485 FadeSkipNextFadeIn();
3487 // needed if different viewport properties defined for playing
3488 ChangeViewportPropertiesIfNeeded();
3492 DrawCompleteVideoDisplay();
3494 OpenDoor(GetDoorState() | DOOR_NO_DELAY | DOOR_FORCE_REDRAW);
3497 InitGameControlValues();
3499 // initialize tape actions from game when recording tape
3502 tape.use_key_actions = game.use_key_actions;
3503 tape.use_mouse_actions = game.use_mouse_actions;
3506 // don't play tapes over network
3507 network_playing = (network.enabled && !tape.playing);
3509 for (i = 0; i < MAX_PLAYERS; i++)
3511 struct PlayerInfo *player = &stored_player[i];
3513 player->index_nr = i;
3514 player->index_bit = (1 << i);
3515 player->element_nr = EL_PLAYER_1 + i;
3517 player->present = FALSE;
3518 player->active = FALSE;
3519 player->mapped = FALSE;
3521 player->killed = FALSE;
3522 player->reanimated = FALSE;
3523 player->buried = FALSE;
3526 player->effective_action = 0;
3527 player->programmed_action = 0;
3528 player->snap_action = 0;
3530 player->mouse_action.lx = 0;
3531 player->mouse_action.ly = 0;
3532 player->mouse_action.button = 0;
3533 player->mouse_action.button_hint = 0;
3535 player->effective_mouse_action.lx = 0;
3536 player->effective_mouse_action.ly = 0;
3537 player->effective_mouse_action.button = 0;
3538 player->effective_mouse_action.button_hint = 0;
3540 for (j = 0; j < MAX_NUM_KEYS; j++)
3541 player->key[j] = FALSE;
3543 player->num_white_keys = 0;
3545 player->dynabomb_count = 0;
3546 player->dynabomb_size = 1;
3547 player->dynabombs_left = 0;
3548 player->dynabomb_xl = FALSE;
3550 player->MovDir = initial_move_dir;
3553 player->GfxDir = initial_move_dir;
3554 player->GfxAction = ACTION_DEFAULT;
3556 player->StepFrame = 0;
3558 player->initial_element = player->element_nr;
3559 player->artwork_element =
3560 (level.use_artwork_element[i] ? level.artwork_element[i] :
3561 player->element_nr);
3562 player->use_murphy = FALSE;
3564 player->block_last_field = FALSE; // initialized in InitPlayerField()
3565 player->block_delay_adjustment = 0; // initialized in InitPlayerField()
3567 player->gravity = level.initial_player_gravity[i];
3569 player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
3571 player->actual_frame_counter = 0;
3573 player->step_counter = 0;
3575 player->last_move_dir = initial_move_dir;
3577 player->is_active = FALSE;
3579 player->is_waiting = FALSE;
3580 player->is_moving = FALSE;
3581 player->is_auto_moving = FALSE;
3582 player->is_digging = FALSE;
3583 player->is_snapping = FALSE;
3584 player->is_collecting = FALSE;
3585 player->is_pushing = FALSE;
3586 player->is_switching = FALSE;
3587 player->is_dropping = FALSE;
3588 player->is_dropping_pressed = FALSE;
3590 player->is_bored = FALSE;
3591 player->is_sleeping = FALSE;
3593 player->was_waiting = TRUE;
3594 player->was_moving = FALSE;
3595 player->was_snapping = FALSE;
3596 player->was_dropping = FALSE;
3598 player->force_dropping = FALSE;
3600 player->frame_counter_bored = -1;
3601 player->frame_counter_sleeping = -1;
3603 player->anim_delay_counter = 0;
3604 player->post_delay_counter = 0;
3606 player->dir_waiting = initial_move_dir;
3607 player->action_waiting = ACTION_DEFAULT;
3608 player->last_action_waiting = ACTION_DEFAULT;
3609 player->special_action_bored = ACTION_DEFAULT;
3610 player->special_action_sleeping = ACTION_DEFAULT;
3612 player->switch_x = -1;
3613 player->switch_y = -1;
3615 player->drop_x = -1;
3616 player->drop_y = -1;
3618 player->show_envelope = 0;
3620 SetPlayerMoveSpeed(player, level.initial_player_stepsize[i], TRUE);
3622 player->push_delay = -1; // initialized when pushing starts
3623 player->push_delay_value = game.initial_push_delay_value;
3625 player->drop_delay = 0;
3626 player->drop_pressed_delay = 0;
3628 player->last_jx = -1;
3629 player->last_jy = -1;
3633 player->shield_normal_time_left = 0;
3634 player->shield_deadly_time_left = 0;
3636 player->inventory_infinite_element = EL_UNDEFINED;
3637 player->inventory_size = 0;
3639 if (level.use_initial_inventory[i])
3641 for (j = 0; j < level.initial_inventory_size[i]; j++)
3643 int element = level.initial_inventory_content[i][j];
3644 int collect_count = element_info[element].collect_count_initial;
3647 if (!IS_CUSTOM_ELEMENT(element))
3650 if (collect_count == 0)
3651 player->inventory_infinite_element = element;
3653 for (k = 0; k < collect_count; k++)
3654 if (player->inventory_size < MAX_INVENTORY_SIZE)
3655 player->inventory_element[player->inventory_size++] = element;
3659 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
3660 SnapField(player, 0, 0);
3662 map_player_action[i] = i;
3665 network_player_action_received = FALSE;
3667 // initial null action
3668 if (network_playing)
3669 SendToServer_MovePlayer(MV_NONE);
3674 TimeLeft = level.time;
3677 ScreenMovDir = MV_NONE;
3681 ScrollStepSize = 0; // will be correctly initialized by ScrollScreen()
3683 game.robot_wheel_x = -1;
3684 game.robot_wheel_y = -1;
3689 game.all_players_gone = FALSE;
3691 game.LevelSolved = FALSE;
3692 game.GameOver = FALSE;
3694 game.GamePlayed = !tape.playing;
3696 game.LevelSolved_GameWon = FALSE;
3697 game.LevelSolved_GameEnd = FALSE;
3698 game.LevelSolved_SaveTape = FALSE;
3699 game.LevelSolved_SaveScore = FALSE;
3701 game.LevelSolved_CountingTime = 0;
3702 game.LevelSolved_CountingScore = 0;
3703 game.LevelSolved_CountingHealth = 0;
3705 game.panel.active = TRUE;
3707 game.no_time_limit = (level.time == 0);
3709 game.yamyam_content_nr = 0;
3710 game.robot_wheel_active = FALSE;
3711 game.magic_wall_active = FALSE;
3712 game.magic_wall_time_left = 0;
3713 game.light_time_left = 0;
3714 game.timegate_time_left = 0;
3715 game.switchgate_pos = 0;
3716 game.wind_direction = level.wind_direction_initial;
3719 game.score_final = 0;
3721 game.health = MAX_HEALTH;
3722 game.health_final = MAX_HEALTH;
3724 game.gems_still_needed = level.gems_needed;
3725 game.sokoban_fields_still_needed = 0;
3726 game.sokoban_objects_still_needed = 0;
3727 game.lights_still_needed = 0;
3728 game.players_still_needed = 0;
3729 game.friends_still_needed = 0;
3731 game.lenses_time_left = 0;
3732 game.magnify_time_left = 0;
3734 game.ball_active = level.ball_active_initial;
3735 game.ball_content_nr = 0;
3737 game.explosions_delayed = TRUE;
3739 game.envelope_active = FALSE;
3741 for (i = 0; i < NUM_BELTS; i++)
3743 game.belt_dir[i] = MV_NONE;
3744 game.belt_dir_nr[i] = 3; // not moving, next moving left
3747 for (i = 0; i < MAX_NUM_AMOEBA; i++)
3748 AmoebaCnt[i] = AmoebaCnt2[i] = 0;
3750 #if DEBUG_INIT_PLAYER
3751 DebugPrintPlayerStatus("Player status at level initialization");
3754 SCAN_PLAYFIELD(x, y)
3756 Feld[x][y] = Last[x][y] = level.field[x][y];
3757 MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
3758 ChangeDelay[x][y] = 0;
3759 ChangePage[x][y] = -1;
3760 CustomValue[x][y] = 0; // initialized in InitField()
3761 Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
3763 WasJustMoving[x][y] = 0;
3764 WasJustFalling[x][y] = 0;
3765 CheckCollision[x][y] = 0;
3766 CheckImpact[x][y] = 0;
3768 Pushed[x][y] = FALSE;
3770 ChangeCount[x][y] = 0;
3771 ChangeEvent[x][y] = -1;
3773 ExplodePhase[x][y] = 0;
3774 ExplodeDelay[x][y] = 0;
3775 ExplodeField[x][y] = EX_TYPE_NONE;
3777 RunnerVisit[x][y] = 0;
3778 PlayerVisit[x][y] = 0;
3781 GfxRandom[x][y] = INIT_GFX_RANDOM();
3782 GfxElement[x][y] = EL_UNDEFINED;
3783 GfxAction[x][y] = ACTION_DEFAULT;
3784 GfxDir[x][y] = MV_NONE;
3785 GfxRedraw[x][y] = GFX_REDRAW_NONE;
3788 SCAN_PLAYFIELD(x, y)
3790 if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
3792 if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
3794 if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
3797 InitField(x, y, TRUE);
3799 ResetGfxAnimation(x, y);
3804 for (i = 0; i < MAX_PLAYERS; i++)
3806 struct PlayerInfo *player = &stored_player[i];
3808 // set number of special actions for bored and sleeping animation
3809 player->num_special_action_bored =
3810 get_num_special_action(player->artwork_element,
3811 ACTION_BORING_1, ACTION_BORING_LAST);
3812 player->num_special_action_sleeping =
3813 get_num_special_action(player->artwork_element,
3814 ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
3817 game.emulation = (emulate_bd ? EMU_BOULDERDASH :
3818 emulate_sb ? EMU_SOKOBAN :
3819 emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
3821 // initialize type of slippery elements
3822 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3824 if (!IS_CUSTOM_ELEMENT(i))
3826 // default: elements slip down either to the left or right randomly
3827 element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
3829 // SP style elements prefer to slip down on the left side
3830 if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
3831 element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3833 // BD style elements prefer to slip down on the left side
3834 if (game.emulation == EMU_BOULDERDASH)
3835 element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3839 // initialize explosion and ignition delay
3840 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3842 if (!IS_CUSTOM_ELEMENT(i))
3845 int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
3846 game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
3847 game.emulation == EMU_SUPAPLEX ? 3 : 2);
3848 int last_phase = (num_phase + 1) * delay;
3849 int half_phase = (num_phase / 2) * delay;
3851 element_info[i].explosion_delay = last_phase - 1;
3852 element_info[i].ignition_delay = half_phase;
3854 if (i == EL_BLACK_ORB)
3855 element_info[i].ignition_delay = 1;
3859 // correct non-moving belts to start moving left
3860 for (i = 0; i < NUM_BELTS; i++)
3861 if (game.belt_dir[i] == MV_NONE)
3862 game.belt_dir_nr[i] = 3; // not moving, next moving left
3864 #if USE_NEW_PLAYER_ASSIGNMENTS
3865 // use preferred player also in local single-player mode
3866 if (!network.enabled && !game.team_mode)
3868 int new_index_nr = setup.network_player_nr;
3870 if (new_index_nr >= 0 && new_index_nr < MAX_PLAYERS)
3872 for (i = 0; i < MAX_PLAYERS; i++)
3873 stored_player[i].connected_locally = FALSE;
3875 stored_player[new_index_nr].connected_locally = TRUE;
3879 for (i = 0; i < MAX_PLAYERS; i++)
3881 stored_player[i].connected = FALSE;
3883 // in network game mode, the local player might not be the first player
3884 if (stored_player[i].connected_locally)
3885 local_player = &stored_player[i];
3888 if (!network.enabled)
3889 local_player->connected = TRUE;
3893 for (i = 0; i < MAX_PLAYERS; i++)
3894 stored_player[i].connected = tape.player_participates[i];
3896 else if (network.enabled)
3898 // add team mode players connected over the network (needed for correct
3899 // assignment of player figures from level to locally playing players)
3901 for (i = 0; i < MAX_PLAYERS; i++)
3902 if (stored_player[i].connected_network)
3903 stored_player[i].connected = TRUE;
3905 else if (game.team_mode)
3907 // try to guess locally connected team mode players (needed for correct
3908 // assignment of player figures from level to locally playing players)
3910 for (i = 0; i < MAX_PLAYERS; i++)
3911 if (setup.input[i].use_joystick ||
3912 setup.input[i].key.left != KSYM_UNDEFINED)
3913 stored_player[i].connected = TRUE;
3916 #if DEBUG_INIT_PLAYER
3917 DebugPrintPlayerStatus("Player status after level initialization");
3920 #if DEBUG_INIT_PLAYER
3922 printf("Reassigning players ...\n");
3925 // check if any connected player was not found in playfield
3926 for (i = 0; i < MAX_PLAYERS; i++)
3928 struct PlayerInfo *player = &stored_player[i];
3930 if (player->connected && !player->present)
3932 struct PlayerInfo *field_player = NULL;
3934 #if DEBUG_INIT_PLAYER
3936 printf("- looking for field player for player %d ...\n", i + 1);
3939 // assign first free player found that is present in the playfield
3941 // first try: look for unmapped playfield player that is not connected
3942 for (j = 0; j < MAX_PLAYERS; j++)
3943 if (field_player == NULL &&
3944 stored_player[j].present &&
3945 !stored_player[j].mapped &&
3946 !stored_player[j].connected)
3947 field_player = &stored_player[j];
3949 // second try: look for *any* unmapped playfield player
3950 for (j = 0; j < MAX_PLAYERS; j++)
3951 if (field_player == NULL &&
3952 stored_player[j].present &&
3953 !stored_player[j].mapped)
3954 field_player = &stored_player[j];
3956 if (field_player != NULL)
3958 int jx = field_player->jx, jy = field_player->jy;
3960 #if DEBUG_INIT_PLAYER
3962 printf("- found player %d\n", field_player->index_nr + 1);
3965 player->present = FALSE;
3966 player->active = FALSE;
3968 field_player->present = TRUE;
3969 field_player->active = TRUE;
3972 player->initial_element = field_player->initial_element;
3973 player->artwork_element = field_player->artwork_element;
3975 player->block_last_field = field_player->block_last_field;
3976 player->block_delay_adjustment = field_player->block_delay_adjustment;
3979 StorePlayer[jx][jy] = field_player->element_nr;
3981 field_player->jx = field_player->last_jx = jx;
3982 field_player->jy = field_player->last_jy = jy;
3984 if (local_player == player)
3985 local_player = field_player;
3987 map_player_action[field_player->index_nr] = i;
3989 field_player->mapped = TRUE;
3991 #if DEBUG_INIT_PLAYER
3993 printf("- map_player_action[%d] == %d\n",
3994 field_player->index_nr + 1, i + 1);
3999 if (player->connected && player->present)
4000 player->mapped = TRUE;
4003 #if DEBUG_INIT_PLAYER
4004 DebugPrintPlayerStatus("Player status after player assignment (first stage)");
4009 // check if any connected player was not found in playfield
4010 for (i = 0; i < MAX_PLAYERS; i++)
4012 struct PlayerInfo *player = &stored_player[i];
4014 if (player->connected && !player->present)
4016 for (j = 0; j < MAX_PLAYERS; j++)
4018 struct PlayerInfo *field_player = &stored_player[j];
4019 int jx = field_player->jx, jy = field_player->jy;
4021 // assign first free player found that is present in the playfield
4022 if (field_player->present && !field_player->connected)
4024 player->present = TRUE;
4025 player->active = TRUE;
4027 field_player->present = FALSE;
4028 field_player->active = FALSE;
4030 player->initial_element = field_player->initial_element;
4031 player->artwork_element = field_player->artwork_element;
4033 player->block_last_field = field_player->block_last_field;
4034 player->block_delay_adjustment = field_player->block_delay_adjustment;
4036 StorePlayer[jx][jy] = player->element_nr;
4038 player->jx = player->last_jx = jx;
4039 player->jy = player->last_jy = jy;
4049 printf("::: local_player->present == %d\n", local_player->present);
4052 // set focus to local player for network games, else to all players
4053 game.centered_player_nr = (network_playing ? local_player->index_nr : -1);
4054 game.centered_player_nr_next = game.centered_player_nr;
4055 game.set_centered_player = FALSE;
4056 game.set_centered_player_wrap = FALSE;
4058 if (network_playing && tape.recording)
4060 // store client dependent player focus when recording network games
4061 tape.centered_player_nr_next = game.centered_player_nr_next;
4062 tape.set_centered_player = TRUE;
4067 // when playing a tape, eliminate all players who do not participate
4069 #if USE_NEW_PLAYER_ASSIGNMENTS
4071 if (!game.team_mode)
4073 for (i = 0; i < MAX_PLAYERS; i++)
4075 if (stored_player[i].active &&
4076 !tape.player_participates[map_player_action[i]])
4078 struct PlayerInfo *player = &stored_player[i];
4079 int jx = player->jx, jy = player->jy;
4081 #if DEBUG_INIT_PLAYER
4083 printf("Removing player %d at (%d, %d)\n", i + 1, jx, jy);
4086 player->active = FALSE;
4087 StorePlayer[jx][jy] = 0;
4088 Feld[jx][jy] = EL_EMPTY;
4095 for (i = 0; i < MAX_PLAYERS; i++)
4097 if (stored_player[i].active &&
4098 !tape.player_participates[i])
4100 struct PlayerInfo *player = &stored_player[i];
4101 int jx = player->jx, jy = player->jy;
4103 player->active = FALSE;
4104 StorePlayer[jx][jy] = 0;
4105 Feld[jx][jy] = EL_EMPTY;
4110 else if (!network.enabled && !game.team_mode) // && !tape.playing
4112 // when in single player mode, eliminate all but the local player
4114 for (i = 0; i < MAX_PLAYERS; i++)
4116 struct PlayerInfo *player = &stored_player[i];
4118 if (player->active && player != local_player)
4120 int jx = player->jx, jy = player->jy;
4122 player->active = FALSE;
4123 player->present = FALSE;
4125 StorePlayer[jx][jy] = 0;
4126 Feld[jx][jy] = EL_EMPTY;
4131 for (i = 0; i < MAX_PLAYERS; i++)
4132 if (stored_player[i].active)
4133 game.players_still_needed++;
4135 if (level.solved_by_one_player)
4136 game.players_still_needed = 1;
4138 // when recording the game, store which players take part in the game
4141 #if USE_NEW_PLAYER_ASSIGNMENTS
4142 for (i = 0; i < MAX_PLAYERS; i++)
4143 if (stored_player[i].connected)
4144 tape.player_participates[i] = TRUE;
4146 for (i = 0; i < MAX_PLAYERS; i++)
4147 if (stored_player[i].active)
4148 tape.player_participates[i] = TRUE;
4152 #if DEBUG_INIT_PLAYER
4153 DebugPrintPlayerStatus("Player status after player assignment (final stage)");
4156 if (BorderElement == EL_EMPTY)
4159 SBX_Right = lev_fieldx - SCR_FIELDX;
4161 SBY_Lower = lev_fieldy - SCR_FIELDY;
4166 SBX_Right = lev_fieldx - SCR_FIELDX + 1;
4168 SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
4171 if (full_lev_fieldx <= SCR_FIELDX)
4172 SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
4173 if (full_lev_fieldy <= SCR_FIELDY)
4174 SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
4176 if (EVEN(SCR_FIELDX) && full_lev_fieldx > SCR_FIELDX)
4178 if (EVEN(SCR_FIELDY) && full_lev_fieldy > SCR_FIELDY)
4181 // if local player not found, look for custom element that might create
4182 // the player (make some assumptions about the right custom element)
4183 if (!local_player->present)
4185 int start_x = 0, start_y = 0;
4186 int found_rating = 0;
4187 int found_element = EL_UNDEFINED;
4188 int player_nr = local_player->index_nr;
4190 SCAN_PLAYFIELD(x, y)
4192 int element = Feld[x][y];
4197 if (level.use_start_element[player_nr] &&
4198 level.start_element[player_nr] == element &&
4205 found_element = element;
4208 if (!IS_CUSTOM_ELEMENT(element))
4211 if (CAN_CHANGE(element))
4213 for (i = 0; i < element_info[element].num_change_pages; i++)
4215 // check for player created from custom element as single target
4216 content = element_info[element].change_page[i].target_element;
4217 is_player = ELEM_IS_PLAYER(content);
4219 if (is_player && (found_rating < 3 ||
4220 (found_rating == 3 && element < found_element)))
4226 found_element = element;
4231 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
4233 // check for player created from custom element as explosion content
4234 content = element_info[element].content.e[xx][yy];
4235 is_player = ELEM_IS_PLAYER(content);
4237 if (is_player && (found_rating < 2 ||
4238 (found_rating == 2 && element < found_element)))
4240 start_x = x + xx - 1;
4241 start_y = y + yy - 1;
4244 found_element = element;
4247 if (!CAN_CHANGE(element))
4250 for (i = 0; i < element_info[element].num_change_pages; i++)
4252 // check for player created from custom element as extended target
4254 element_info[element].change_page[i].target_content.e[xx][yy];
4256 is_player = ELEM_IS_PLAYER(content);
4258 if (is_player && (found_rating < 1 ||
4259 (found_rating == 1 && element < found_element)))
4261 start_x = x + xx - 1;
4262 start_y = y + yy - 1;
4265 found_element = element;
4271 scroll_x = SCROLL_POSITION_X(start_x);
4272 scroll_y = SCROLL_POSITION_Y(start_y);
4276 scroll_x = SCROLL_POSITION_X(local_player->jx);
4277 scroll_y = SCROLL_POSITION_Y(local_player->jy);
4280 // !!! FIX THIS (START) !!!
4281 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
4283 InitGameEngine_EM();
4285 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
4287 InitGameEngine_SP();
4289 else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4291 InitGameEngine_MM();
4295 DrawLevel(REDRAW_FIELD);
4298 // after drawing the level, correct some elements
4299 if (game.timegate_time_left == 0)
4300 CloseAllOpenTimegates();
4303 // blit playfield from scroll buffer to normal back buffer for fading in
4304 BlitScreenToBitmap(backbuffer);
4305 // !!! FIX THIS (END) !!!
4307 DrawMaskedBorder(fade_mask);
4312 // full screen redraw is required at this point in the following cases:
4313 // - special editor door undrawn when game was started from level editor
4314 // - drawing area (playfield) was changed and has to be removed completely
4315 redraw_mask = REDRAW_ALL;
4319 if (!game.restart_level)
4321 // copy default game door content to main double buffer
4323 // !!! CHECK AGAIN !!!
4324 SetPanelBackground();
4325 // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
4326 DrawBackground(DX, DY, DXSIZE, DYSIZE);
4329 SetPanelBackground();
4330 SetDrawBackgroundMask(REDRAW_DOOR_1);
4332 UpdateAndDisplayGameControlValues();
4334 if (!game.restart_level)
4340 CreateGameButtons();
4345 // copy actual game door content to door double buffer for OpenDoor()
4346 BlitBitmap(drawto, bitmap_db_door_1, DX, DY, DXSIZE, DYSIZE, 0, 0);
4348 OpenDoor(DOOR_OPEN_ALL);
4350 KeyboardAutoRepeatOffUnlessAutoplay();
4352 #if DEBUG_INIT_PLAYER
4353 DebugPrintPlayerStatus("Player status (final)");
4362 if (!game.restart_level && !tape.playing)
4364 LevelStats_incPlayed(level_nr);
4366 SaveLevelSetup_SeriesInfo();
4369 game.restart_level = FALSE;
4370 game.restart_game_message = NULL;
4371 game.request_active = FALSE;
4373 if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4374 InitGameActions_MM();
4376 SaveEngineSnapshotToListInitial();
4378 if (!game.restart_level)
4380 PlaySound(SND_GAME_STARTING);
4382 if (setup.sound_music)
4387 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y,
4388 int actual_player_x, int actual_player_y)
4390 // this is used for non-R'n'D game engines to update certain engine values
4392 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
4394 actual_player_x = correctLevelPosX_EM(actual_player_x);
4395 actual_player_y = correctLevelPosY_EM(actual_player_y);
4398 // needed to determine if sounds are played within the visible screen area
4399 scroll_x = actual_scroll_x;
4400 scroll_y = actual_scroll_y;
4402 // needed to get player position for "follow finger" playing input method
4403 local_player->jx = actual_player_x;
4404 local_player->jy = actual_player_y;
4407 void InitMovDir(int x, int y)
4409 int i, element = Feld[x][y];
4410 static int xy[4][2] =
4417 static int direction[3][4] =
4419 { MV_RIGHT, MV_UP, MV_LEFT, MV_DOWN },
4420 { MV_LEFT, MV_DOWN, MV_RIGHT, MV_UP },
4421 { MV_LEFT, MV_RIGHT, MV_UP, MV_DOWN }
4430 Feld[x][y] = EL_BUG;
4431 MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
4434 case EL_SPACESHIP_RIGHT:
4435 case EL_SPACESHIP_UP:
4436 case EL_SPACESHIP_LEFT:
4437 case EL_SPACESHIP_DOWN:
4438 Feld[x][y] = EL_SPACESHIP;
4439 MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
4442 case EL_BD_BUTTERFLY_RIGHT:
4443 case EL_BD_BUTTERFLY_UP:
4444 case EL_BD_BUTTERFLY_LEFT:
4445 case EL_BD_BUTTERFLY_DOWN:
4446 Feld[x][y] = EL_BD_BUTTERFLY;
4447 MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
4450 case EL_BD_FIREFLY_RIGHT:
4451 case EL_BD_FIREFLY_UP:
4452 case EL_BD_FIREFLY_LEFT:
4453 case EL_BD_FIREFLY_DOWN:
4454 Feld[x][y] = EL_BD_FIREFLY;
4455 MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
4458 case EL_PACMAN_RIGHT:
4460 case EL_PACMAN_LEFT:
4461 case EL_PACMAN_DOWN:
4462 Feld[x][y] = EL_PACMAN;
4463 MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
4466 case EL_YAMYAM_LEFT:
4467 case EL_YAMYAM_RIGHT:
4469 case EL_YAMYAM_DOWN:
4470 Feld[x][y] = EL_YAMYAM;
4471 MovDir[x][y] = direction[2][element - EL_YAMYAM_LEFT];
4474 case EL_SP_SNIKSNAK:
4475 MovDir[x][y] = MV_UP;
4478 case EL_SP_ELECTRON:
4479 MovDir[x][y] = MV_LEFT;
4486 Feld[x][y] = EL_MOLE;
4487 MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
4491 if (IS_CUSTOM_ELEMENT(element))
4493 struct ElementInfo *ei = &element_info[element];
4494 int move_direction_initial = ei->move_direction_initial;
4495 int move_pattern = ei->move_pattern;
4497 if (move_direction_initial == MV_START_PREVIOUS)
4499 if (MovDir[x][y] != MV_NONE)
4502 move_direction_initial = MV_START_AUTOMATIC;
4505 if (move_direction_initial == MV_START_RANDOM)
4506 MovDir[x][y] = 1 << RND(4);
4507 else if (move_direction_initial & MV_ANY_DIRECTION)
4508 MovDir[x][y] = move_direction_initial;
4509 else if (move_pattern == MV_ALL_DIRECTIONS ||
4510 move_pattern == MV_TURNING_LEFT ||
4511 move_pattern == MV_TURNING_RIGHT ||
4512 move_pattern == MV_TURNING_LEFT_RIGHT ||
4513 move_pattern == MV_TURNING_RIGHT_LEFT ||
4514 move_pattern == MV_TURNING_RANDOM)
4515 MovDir[x][y] = 1 << RND(4);
4516 else if (move_pattern == MV_HORIZONTAL)
4517 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
4518 else if (move_pattern == MV_VERTICAL)
4519 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
4520 else if (move_pattern & MV_ANY_DIRECTION)
4521 MovDir[x][y] = element_info[element].move_pattern;
4522 else if (move_pattern == MV_ALONG_LEFT_SIDE ||
4523 move_pattern == MV_ALONG_RIGHT_SIDE)
4525 // use random direction as default start direction
4526 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
4527 MovDir[x][y] = 1 << RND(4);
4529 for (i = 0; i < NUM_DIRECTIONS; i++)
4531 int x1 = x + xy[i][0];
4532 int y1 = y + xy[i][1];
4534 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4536 if (move_pattern == MV_ALONG_RIGHT_SIDE)
4537 MovDir[x][y] = direction[0][i];
4539 MovDir[x][y] = direction[1][i];
4548 MovDir[x][y] = 1 << RND(4);
4550 if (element != EL_BUG &&
4551 element != EL_SPACESHIP &&
4552 element != EL_BD_BUTTERFLY &&
4553 element != EL_BD_FIREFLY)
4556 for (i = 0; i < NUM_DIRECTIONS; i++)
4558 int x1 = x + xy[i][0];
4559 int y1 = y + xy[i][1];
4561 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4563 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
4565 MovDir[x][y] = direction[0][i];
4568 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
4569 element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4571 MovDir[x][y] = direction[1][i];
4580 GfxDir[x][y] = MovDir[x][y];
4583 void InitAmoebaNr(int x, int y)
4586 int group_nr = AmoebeNachbarNr(x, y);
4590 for (i = 1; i < MAX_NUM_AMOEBA; i++)
4592 if (AmoebaCnt[i] == 0)
4600 AmoebaNr[x][y] = group_nr;
4601 AmoebaCnt[group_nr]++;
4602 AmoebaCnt2[group_nr]++;
4605 static void LevelSolved(void)
4607 if (level.game_engine_type == GAME_ENGINE_TYPE_RND &&
4608 game.players_still_needed > 0)
4611 game.LevelSolved = TRUE;
4612 game.GameOver = TRUE;
4614 game.score_final = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
4615 game_em.lev->score :
4616 level.game_engine_type == GAME_ENGINE_TYPE_MM ?
4619 game.health_final = (level.game_engine_type == GAME_ENGINE_TYPE_MM ?
4620 MM_HEALTH(game_mm.laser_overload_value) :
4623 game.LevelSolved_CountingTime = (game.no_time_limit ? TimePlayed : TimeLeft);
4624 game.LevelSolved_CountingScore = game.score_final;
4625 game.LevelSolved_CountingHealth = game.health_final;
4630 static int time_count_steps;
4631 static int time, time_final;
4632 static int score, score_final;
4633 static int health, health_final;
4634 static int game_over_delay_1 = 0;
4635 static int game_over_delay_2 = 0;
4636 static int game_over_delay_3 = 0;
4637 int game_over_delay_value_1 = 50;
4638 int game_over_delay_value_2 = 25;
4639 int game_over_delay_value_3 = 50;
4641 if (!game.LevelSolved_GameWon)
4645 // do not start end game actions before the player stops moving (to exit)
4646 if (local_player->active && local_player->MovPos)
4649 game.LevelSolved_GameWon = TRUE;
4650 game.LevelSolved_SaveTape = tape.recording;
4651 game.LevelSolved_SaveScore = !tape.playing;
4655 LevelStats_incSolved(level_nr);
4657 SaveLevelSetup_SeriesInfo();
4660 if (tape.auto_play) // tape might already be stopped here
4661 tape.auto_play_level_solved = TRUE;
4665 game_over_delay_1 = 0;
4666 game_over_delay_2 = 0;
4667 game_over_delay_3 = game_over_delay_value_3;
4669 time = time_final = (game.no_time_limit ? TimePlayed : TimeLeft);
4670 score = score_final = game.score_final;
4671 health = health_final = game.health_final;
4673 if (level.score[SC_TIME_BONUS] > 0)
4678 score_final += TimeLeft * level.score[SC_TIME_BONUS];
4680 else if (game.no_time_limit && TimePlayed < 999)
4683 score_final += (999 - TimePlayed) * level.score[SC_TIME_BONUS];
4686 time_count_steps = MAX(1, ABS(time_final - time) / 100);
4688 game_over_delay_1 = game_over_delay_value_1;
4690 if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4693 score_final += health * level.score[SC_TIME_BONUS];
4695 game_over_delay_2 = game_over_delay_value_2;
4698 game.score_final = score_final;
4699 game.health_final = health_final;
4702 if (level_editor_test_game)
4705 score = score_final;
4707 game.LevelSolved_CountingTime = time;
4708 game.LevelSolved_CountingScore = score;
4710 game_panel_controls[GAME_PANEL_TIME].value = time;
4711 game_panel_controls[GAME_PANEL_SCORE].value = score;
4713 DisplayGameControlValues();
4716 if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
4718 // check if last player has left the level
4719 if (game.exit_x >= 0 &&
4722 int x = game.exit_x;
4723 int y = game.exit_y;
4724 int element = Feld[x][y];
4726 // close exit door after last player
4727 if ((game.all_players_gone &&
4728 (element == EL_EXIT_OPEN ||
4729 element == EL_SP_EXIT_OPEN ||
4730 element == EL_STEEL_EXIT_OPEN)) ||
4731 element == EL_EM_EXIT_OPEN ||
4732 element == EL_EM_STEEL_EXIT_OPEN)
4736 (element == EL_EXIT_OPEN ? EL_EXIT_CLOSING :
4737 element == EL_EM_EXIT_OPEN ? EL_EM_EXIT_CLOSING :
4738 element == EL_SP_EXIT_OPEN ? EL_SP_EXIT_CLOSING:
4739 element == EL_STEEL_EXIT_OPEN ? EL_STEEL_EXIT_CLOSING:
4740 EL_EM_STEEL_EXIT_CLOSING);
4742 PlayLevelSoundElementAction(x, y, element, ACTION_CLOSING);
4745 // player disappears
4746 DrawLevelField(x, y);
4749 for (i = 0; i < MAX_PLAYERS; i++)
4751 struct PlayerInfo *player = &stored_player[i];
4753 if (player->present)
4755 RemovePlayer(player);
4757 // player disappears
4758 DrawLevelField(player->jx, player->jy);
4763 PlaySound(SND_GAME_WINNING);
4766 if (game_over_delay_1 > 0)
4768 game_over_delay_1--;
4773 if (time != time_final)
4775 int time_to_go = ABS(time_final - time);
4776 int time_count_dir = (time < time_final ? +1 : -1);
4778 if (time_to_go < time_count_steps)
4779 time_count_steps = 1;
4781 time += time_count_steps * time_count_dir;
4782 score += time_count_steps * level.score[SC_TIME_BONUS];
4784 game.LevelSolved_CountingTime = time;
4785 game.LevelSolved_CountingScore = score;
4787 game_panel_controls[GAME_PANEL_TIME].value = time;
4788 game_panel_controls[GAME_PANEL_SCORE].value = score;
4790 DisplayGameControlValues();
4792 if (time == time_final)
4793 StopSound(SND_GAME_LEVELTIME_BONUS);
4794 else if (setup.sound_loops)
4795 PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4797 PlaySound(SND_GAME_LEVELTIME_BONUS);
4802 if (game_over_delay_2 > 0)
4804 game_over_delay_2--;
4809 if (health != health_final)
4811 int health_count_dir = (health < health_final ? +1 : -1);
4813 health += health_count_dir;
4814 score += level.score[SC_TIME_BONUS];
4816 game.LevelSolved_CountingHealth = health;
4817 game.LevelSolved_CountingScore = score;
4819 game_panel_controls[GAME_PANEL_HEALTH].value = health;
4820 game_panel_controls[GAME_PANEL_SCORE].value = score;
4822 DisplayGameControlValues();
4824 if (health == health_final)
4825 StopSound(SND_GAME_LEVELTIME_BONUS);
4826 else if (setup.sound_loops)
4827 PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4829 PlaySound(SND_GAME_LEVELTIME_BONUS);
4834 game.panel.active = FALSE;
4836 if (game_over_delay_3 > 0)
4838 game_over_delay_3--;
4848 // used instead of "level_nr" (needed for network games)
4849 int last_level_nr = levelset.level_nr;
4852 game.LevelSolved_GameEnd = TRUE;
4854 if (game.LevelSolved_SaveTape)
4856 // make sure that request dialog to save tape does not open door again
4857 if (!global.use_envelope_request)
4858 CloseDoor(DOOR_CLOSE_1);
4860 SaveTapeChecked_LevelSolved(tape.level_nr); // ask to save tape
4863 // if no tape is to be saved, close both doors simultaneously
4864 CloseDoor(DOOR_CLOSE_ALL);
4866 if (level_editor_test_game)
4868 SetGameStatus(GAME_MODE_MAIN);
4875 if (!game.LevelSolved_SaveScore)
4877 SetGameStatus(GAME_MODE_MAIN);
4884 if (level_nr == leveldir_current->handicap_level)
4886 leveldir_current->handicap_level++;
4888 SaveLevelSetup_SeriesInfo();
4891 if (setup.increment_levels &&
4892 level_nr < leveldir_current->last_level &&
4895 level_nr++; // advance to next level
4896 TapeErase(); // start with empty tape
4898 if (setup.auto_play_next_level)
4900 LoadLevel(level_nr);
4902 SaveLevelSetup_SeriesInfo();
4906 hi_pos = NewHiScore(last_level_nr);
4908 if (hi_pos >= 0 && !setup.skip_scores_after_game)
4910 SetGameStatus(GAME_MODE_SCORES);
4912 DrawHallOfFame(last_level_nr, hi_pos);
4914 else if (setup.auto_play_next_level && setup.increment_levels &&
4915 last_level_nr < leveldir_current->last_level &&
4918 StartGameActions(network.enabled, setup.autorecord, level.random_seed);
4922 SetGameStatus(GAME_MODE_MAIN);
4928 int NewHiScore(int level_nr)
4932 boolean one_score_entry_per_name = !program.many_scores_per_name;
4934 LoadScore(level_nr);
4936 if (strEqual(setup.player_name, EMPTY_PLAYER_NAME) ||
4937 game.score_final < highscore[MAX_SCORE_ENTRIES - 1].Score)
4940 for (k = 0; k < MAX_SCORE_ENTRIES; k++)
4942 if (game.score_final > highscore[k].Score)
4944 // player has made it to the hall of fame
4946 if (k < MAX_SCORE_ENTRIES - 1)
4948 int m = MAX_SCORE_ENTRIES - 1;
4950 if (one_score_entry_per_name)
4952 for (l = k; l < MAX_SCORE_ENTRIES; l++)
4953 if (strEqual(setup.player_name, highscore[l].Name))
4956 if (m == k) // player's new highscore overwrites his old one
4960 for (l = m; l > k; l--)
4962 strcpy(highscore[l].Name, highscore[l - 1].Name);
4963 highscore[l].Score = highscore[l - 1].Score;
4969 strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
4970 highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
4971 highscore[k].Score = game.score_final;
4976 else if (one_score_entry_per_name &&
4977 !strncmp(setup.player_name, highscore[k].Name,
4978 MAX_PLAYER_NAME_LEN))
4979 break; // player already there with a higher score
4983 SaveScore(level_nr);
4988 static int getElementMoveStepsizeExt(int x, int y, int direction)
4990 int element = Feld[x][y];
4991 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
4992 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
4993 int horiz_move = (dx != 0);
4994 int sign = (horiz_move ? dx : dy);
4995 int step = sign * element_info[element].move_stepsize;
4997 // special values for move stepsize for spring and things on conveyor belt
5000 if (CAN_FALL(element) &&
5001 y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
5002 step = sign * MOVE_STEPSIZE_NORMAL / 2;
5003 else if (element == EL_SPRING)
5004 step = sign * MOVE_STEPSIZE_NORMAL * 2;
5010 static int getElementMoveStepsize(int x, int y)
5012 return getElementMoveStepsizeExt(x, y, MovDir[x][y]);
5015 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
5017 if (player->GfxAction != action || player->GfxDir != dir)
5019 player->GfxAction = action;
5020 player->GfxDir = dir;
5022 player->StepFrame = 0;
5026 static void ResetGfxFrame(int x, int y)
5028 // profiling showed that "autotest" spends 10~20% of its time in this function
5029 if (DrawingDeactivatedField())
5032 int element = Feld[x][y];
5033 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
5035 if (graphic_info[graphic].anim_global_sync)
5036 GfxFrame[x][y] = FrameCounter;
5037 else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
5038 GfxFrame[x][y] = CustomValue[x][y];
5039 else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
5040 GfxFrame[x][y] = element_info[element].collect_score;
5041 else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
5042 GfxFrame[x][y] = ChangeDelay[x][y];
5045 static void ResetGfxAnimation(int x, int y)
5047 GfxAction[x][y] = ACTION_DEFAULT;
5048 GfxDir[x][y] = MovDir[x][y];
5051 ResetGfxFrame(x, y);
5054 static void ResetRandomAnimationValue(int x, int y)
5056 GfxRandom[x][y] = INIT_GFX_RANDOM();
5059 static void InitMovingField(int x, int y, int direction)
5061 int element = Feld[x][y];
5062 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
5063 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
5066 boolean is_moving_before, is_moving_after;
5068 // check if element was/is moving or being moved before/after mode change
5069 is_moving_before = (WasJustMoving[x][y] != 0);
5070 is_moving_after = (getElementMoveStepsizeExt(x, y, direction) != 0);
5072 // reset animation only for moving elements which change direction of moving
5073 // or which just started or stopped moving
5074 // (else CEs with property "can move" / "not moving" are reset each frame)
5075 if (is_moving_before != is_moving_after ||
5076 direction != MovDir[x][y])
5077 ResetGfxAnimation(x, y);
5079 MovDir[x][y] = direction;
5080 GfxDir[x][y] = direction;
5082 GfxAction[x][y] = (!is_moving_after ? ACTION_WAITING :
5083 direction == MV_DOWN && CAN_FALL(element) ?
5084 ACTION_FALLING : ACTION_MOVING);
5086 // this is needed for CEs with property "can move" / "not moving"
5088 if (is_moving_after)
5090 if (Feld[newx][newy] == EL_EMPTY)
5091 Feld[newx][newy] = EL_BLOCKED;
5093 MovDir[newx][newy] = MovDir[x][y];
5095 CustomValue[newx][newy] = CustomValue[x][y];
5097 GfxFrame[newx][newy] = GfxFrame[x][y];
5098 GfxRandom[newx][newy] = GfxRandom[x][y];
5099 GfxAction[newx][newy] = GfxAction[x][y];
5100 GfxDir[newx][newy] = GfxDir[x][y];
5104 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
5106 int direction = MovDir[x][y];
5107 int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0);
5108 int newy = y + (direction & MV_UP ? -1 : direction & MV_DOWN ? +1 : 0);
5114 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
5116 int oldx = x, oldy = y;
5117 int direction = MovDir[x][y];
5119 if (direction == MV_LEFT)
5121 else if (direction == MV_RIGHT)
5123 else if (direction == MV_UP)
5125 else if (direction == MV_DOWN)
5128 *comes_from_x = oldx;
5129 *comes_from_y = oldy;
5132 static int MovingOrBlocked2Element(int x, int y)
5134 int element = Feld[x][y];
5136 if (element == EL_BLOCKED)
5140 Blocked2Moving(x, y, &oldx, &oldy);
5141 return Feld[oldx][oldy];
5147 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
5149 // like MovingOrBlocked2Element(), but if element is moving
5150 // and (x,y) is the field the moving element is just leaving,
5151 // return EL_BLOCKED instead of the element value
5152 int element = Feld[x][y];
5154 if (IS_MOVING(x, y))
5156 if (element == EL_BLOCKED)
5160 Blocked2Moving(x, y, &oldx, &oldy);
5161 return Feld[oldx][oldy];
5170 static void RemoveField(int x, int y)
5172 Feld[x][y] = EL_EMPTY;
5178 CustomValue[x][y] = 0;
5181 ChangeDelay[x][y] = 0;
5182 ChangePage[x][y] = -1;
5183 Pushed[x][y] = FALSE;
5185 GfxElement[x][y] = EL_UNDEFINED;
5186 GfxAction[x][y] = ACTION_DEFAULT;
5187 GfxDir[x][y] = MV_NONE;
5190 static void RemoveMovingField(int x, int y)
5192 int oldx = x, oldy = y, newx = x, newy = y;
5193 int element = Feld[x][y];
5194 int next_element = EL_UNDEFINED;
5196 if (element != EL_BLOCKED && !IS_MOVING(x, y))
5199 if (IS_MOVING(x, y))
5201 Moving2Blocked(x, y, &newx, &newy);
5203 if (Feld[newx][newy] != EL_BLOCKED)
5205 // element is moving, but target field is not free (blocked), but
5206 // already occupied by something different (example: acid pool);
5207 // in this case, only remove the moving field, but not the target
5209 RemoveField(oldx, oldy);
5211 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5213 TEST_DrawLevelField(oldx, oldy);
5218 else if (element == EL_BLOCKED)
5220 Blocked2Moving(x, y, &oldx, &oldy);
5221 if (!IS_MOVING(oldx, oldy))
5225 if (element == EL_BLOCKED &&
5226 (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
5227 Feld[oldx][oldy] == EL_QUICKSAND_FAST_EMPTYING ||
5228 Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
5229 Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
5230 Feld[oldx][oldy] == EL_DC_MAGIC_WALL_EMPTYING ||
5231 Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
5232 next_element = get_next_element(Feld[oldx][oldy]);
5234 RemoveField(oldx, oldy);
5235 RemoveField(newx, newy);
5237 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5239 if (next_element != EL_UNDEFINED)
5240 Feld[oldx][oldy] = next_element;
5242 TEST_DrawLevelField(oldx, oldy);
5243 TEST_DrawLevelField(newx, newy);
5246 void DrawDynamite(int x, int y)
5248 int sx = SCREENX(x), sy = SCREENY(y);
5249 int graphic = el2img(Feld[x][y]);
5252 if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
5255 if (IS_WALKABLE_INSIDE(Back[x][y]))
5259 DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
5260 else if (Store[x][y])
5261 DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
5263 frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5265 if (Back[x][y] || Store[x][y])
5266 DrawGraphicThruMask(sx, sy, graphic, frame);
5268 DrawGraphic(sx, sy, graphic, frame);
5271 static void CheckDynamite(int x, int y)
5273 if (MovDelay[x][y] != 0) // dynamite is still waiting to explode
5277 if (MovDelay[x][y] != 0)
5280 PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5286 StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5291 static void setMinimalPlayerBoundaries(int *sx1, int *sy1, int *sx2, int *sy2)
5293 boolean num_checked_players = 0;
5296 for (i = 0; i < MAX_PLAYERS; i++)
5298 if (stored_player[i].active)
5300 int sx = stored_player[i].jx;
5301 int sy = stored_player[i].jy;
5303 if (num_checked_players == 0)
5310 *sx1 = MIN(*sx1, sx);
5311 *sy1 = MIN(*sy1, sy);
5312 *sx2 = MAX(*sx2, sx);
5313 *sy2 = MAX(*sy2, sy);
5316 num_checked_players++;
5321 static boolean checkIfAllPlayersFitToScreen_RND(void)
5323 int sx1 = 0, sy1 = 0, sx2 = 0, sy2 = 0;
5325 setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5327 return (sx2 - sx1 < SCR_FIELDX &&
5328 sy2 - sy1 < SCR_FIELDY);
5331 static void setScreenCenteredToAllPlayers(int *sx, int *sy)
5333 int sx1 = scroll_x, sy1 = scroll_y, sx2 = scroll_x, sy2 = scroll_y;
5335 setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5337 *sx = (sx1 + sx2) / 2;
5338 *sy = (sy1 + sy2) / 2;
5341 static void DrawRelocateScreen(int old_x, int old_y, int x, int y, int move_dir,
5342 boolean center_screen, boolean quick_relocation)
5344 unsigned int frame_delay_value_old = GetVideoFrameDelay();
5345 boolean ffwd_delay = (tape.playing && tape.fast_forward);
5346 boolean no_delay = (tape.warp_forward);
5347 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5348 int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5349 int new_scroll_x, new_scroll_y;
5351 if (level.lazy_relocation && IN_VIS_FIELD(SCREENX(x), SCREENY(y)))
5353 // case 1: quick relocation inside visible screen (without scrolling)
5360 if (!level.shifted_relocation || center_screen)
5362 // relocation _with_ centering of screen
5364 new_scroll_x = SCROLL_POSITION_X(x);
5365 new_scroll_y = SCROLL_POSITION_Y(y);
5369 // relocation _without_ centering of screen
5371 int center_scroll_x = SCROLL_POSITION_X(old_x);
5372 int center_scroll_y = SCROLL_POSITION_Y(old_y);
5373 int offset_x = x + (scroll_x - center_scroll_x);
5374 int offset_y = y + (scroll_y - center_scroll_y);
5376 // for new screen position, apply previous offset to center position
5377 new_scroll_x = SCROLL_POSITION_X(offset_x);
5378 new_scroll_y = SCROLL_POSITION_Y(offset_y);
5381 if (quick_relocation)
5383 // case 2: quick relocation (redraw without visible scrolling)
5385 scroll_x = new_scroll_x;
5386 scroll_y = new_scroll_y;
5393 // case 3: visible relocation (with scrolling to new position)
5395 ScrollScreen(NULL, SCROLL_GO_ON); // scroll last frame to full tile
5397 SetVideoFrameDelay(wait_delay_value);
5399 while (scroll_x != new_scroll_x || scroll_y != new_scroll_y)
5401 int dx = (new_scroll_x < scroll_x ? +1 : new_scroll_x > scroll_x ? -1 : 0);
5402 int dy = (new_scroll_y < scroll_y ? +1 : new_scroll_y > scroll_y ? -1 : 0);
5404 if (dx == 0 && dy == 0) // no scrolling needed at all
5410 // set values for horizontal/vertical screen scrolling (half tile size)
5411 int dir_x = (dx != 0 ? MV_HORIZONTAL : 0);
5412 int dir_y = (dy != 0 ? MV_VERTICAL : 0);
5413 int pos_x = dx * TILEX / 2;
5414 int pos_y = dy * TILEY / 2;
5415 int fx = getFieldbufferOffsetX_RND(dir_x, pos_x);
5416 int fy = getFieldbufferOffsetY_RND(dir_y, pos_y);
5418 ScrollLevel(dx, dy);
5421 // scroll in two steps of half tile size to make things smoother
5422 BlitScreenToBitmapExt_RND(window, fx, fy);
5424 // scroll second step to align at full tile size
5425 BlitScreenToBitmap(window);
5431 SetVideoFrameDelay(frame_delay_value_old);
5434 static void RelocatePlayer(int jx, int jy, int el_player_raw)
5436 int el_player = GET_PLAYER_ELEMENT(el_player_raw);
5437 int player_nr = GET_PLAYER_NR(el_player);
5438 struct PlayerInfo *player = &stored_player[player_nr];
5439 boolean ffwd_delay = (tape.playing && tape.fast_forward);
5440 boolean no_delay = (tape.warp_forward);
5441 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5442 int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5443 int old_jx = player->jx;
5444 int old_jy = player->jy;
5445 int old_element = Feld[old_jx][old_jy];
5446 int element = Feld[jx][jy];
5447 boolean player_relocated = (old_jx != jx || old_jy != jy);
5449 int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
5450 int move_dir_vert = (jy < old_jy ? MV_UP : jy > old_jy ? MV_DOWN : 0);
5451 int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
5452 int enter_side_vert = MV_DIR_OPPOSITE(move_dir_vert);
5453 int leave_side_horiz = move_dir_horiz;
5454 int leave_side_vert = move_dir_vert;
5455 int enter_side = enter_side_horiz | enter_side_vert;
5456 int leave_side = leave_side_horiz | leave_side_vert;
5458 if (player->buried) // do not reanimate dead player
5461 if (!player_relocated) // no need to relocate the player
5464 if (IS_PLAYER(jx, jy)) // player already placed at new position
5466 RemoveField(jx, jy); // temporarily remove newly placed player
5467 DrawLevelField(jx, jy);
5470 if (player->present)
5472 while (player->MovPos)
5474 ScrollPlayer(player, SCROLL_GO_ON);
5475 ScrollScreen(NULL, SCROLL_GO_ON);
5477 AdvanceFrameAndPlayerCounters(player->index_nr);
5481 BackToFront_WithFrameDelay(wait_delay_value);
5484 DrawPlayer(player); // needed here only to cleanup last field
5485 DrawLevelField(player->jx, player->jy); // remove player graphic
5487 player->is_moving = FALSE;
5490 if (IS_CUSTOM_ELEMENT(old_element))
5491 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
5493 player->index_bit, leave_side);
5495 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
5497 player->index_bit, leave_side);
5499 Feld[jx][jy] = el_player;
5500 InitPlayerField(jx, jy, el_player, TRUE);
5502 /* "InitPlayerField()" above sets Feld[jx][jy] to EL_EMPTY, but it may be
5503 possible that the relocation target field did not contain a player element,
5504 but a walkable element, to which the new player was relocated -- in this
5505 case, restore that (already initialized!) element on the player field */
5506 if (!ELEM_IS_PLAYER(element)) // player may be set on walkable element
5508 Feld[jx][jy] = element; // restore previously existing element
5511 // only visually relocate centered player
5512 DrawRelocateScreen(old_jx, old_jy, player->jx, player->jy, player->MovDir,
5513 FALSE, level.instant_relocation);
5515 TestIfPlayerTouchesBadThing(jx, jy);
5516 TestIfPlayerTouchesCustomElement(jx, jy);
5518 if (IS_CUSTOM_ELEMENT(element))
5519 CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
5520 player->index_bit, enter_side);
5522 CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
5523 player->index_bit, enter_side);
5525 if (player->is_switching)
5527 /* ensure that relocation while still switching an element does not cause
5528 a new element to be treated as also switched directly after relocation
5529 (this is important for teleporter switches that teleport the player to
5530 a place where another teleporter switch is in the same direction, which
5531 would then incorrectly be treated as immediately switched before the
5532 direction key that caused the switch was released) */
5534 player->switch_x += jx - old_jx;
5535 player->switch_y += jy - old_jy;
5539 static void Explode(int ex, int ey, int phase, int mode)
5545 // !!! eliminate this variable !!!
5546 int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
5548 if (game.explosions_delayed)
5550 ExplodeField[ex][ey] = mode;
5554 if (phase == EX_PHASE_START) // initialize 'Store[][]' field
5556 int center_element = Feld[ex][ey];
5557 int artwork_element, explosion_element; // set these values later
5559 // remove things displayed in background while burning dynamite
5560 if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
5563 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
5565 // put moving element to center field (and let it explode there)
5566 center_element = MovingOrBlocked2Element(ex, ey);
5567 RemoveMovingField(ex, ey);
5568 Feld[ex][ey] = center_element;
5571 // now "center_element" is finally determined -- set related values now
5572 artwork_element = center_element; // for custom player artwork
5573 explosion_element = center_element; // for custom player artwork
5575 if (IS_PLAYER(ex, ey))
5577 int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
5579 artwork_element = stored_player[player_nr].artwork_element;
5581 if (level.use_explosion_element[player_nr])
5583 explosion_element = level.explosion_element[player_nr];
5584 artwork_element = explosion_element;
5588 if (mode == EX_TYPE_NORMAL ||
5589 mode == EX_TYPE_CENTER ||
5590 mode == EX_TYPE_CROSS)
5591 PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
5593 last_phase = element_info[explosion_element].explosion_delay + 1;
5595 for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
5597 int xx = x - ex + 1;
5598 int yy = y - ey + 1;
5601 if (!IN_LEV_FIELD(x, y) ||
5602 (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
5603 (mode == EX_TYPE_CROSS && (x != ex && y != ey)))
5606 element = Feld[x][y];
5608 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
5610 element = MovingOrBlocked2Element(x, y);
5612 if (!IS_EXPLOSION_PROOF(element))
5613 RemoveMovingField(x, y);
5616 // indestructible elements can only explode in center (but not flames)
5617 if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
5618 mode == EX_TYPE_BORDER)) ||
5619 element == EL_FLAMES)
5622 /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
5623 behaviour, for example when touching a yamyam that explodes to rocks
5624 with active deadly shield, a rock is created under the player !!! */
5625 // (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8)
5627 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
5628 (game.engine_version < VERSION_IDENT(3,1,0,0) ||
5629 (x == ex && y == ey && mode != EX_TYPE_BORDER)))
5631 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
5634 if (IS_ACTIVE_BOMB(element))
5636 // re-activate things under the bomb like gate or penguin
5637 Feld[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
5644 // save walkable background elements while explosion on same tile
5645 if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
5646 (x != ex || y != ey || mode == EX_TYPE_BORDER))
5647 Back[x][y] = element;
5649 // ignite explodable elements reached by other explosion
5650 if (element == EL_EXPLOSION)
5651 element = Store2[x][y];
5653 if (AmoebaNr[x][y] &&
5654 (element == EL_AMOEBA_FULL ||
5655 element == EL_BD_AMOEBA ||
5656 element == EL_AMOEBA_GROWING))
5658 AmoebaCnt[AmoebaNr[x][y]]--;
5659 AmoebaCnt2[AmoebaNr[x][y]]--;
5664 if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
5666 int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
5668 Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
5670 if (PLAYERINFO(ex, ey)->use_murphy)
5671 Store[x][y] = EL_EMPTY;
5674 // !!! check this case -- currently needed for rnd_rado_negundo_v,
5675 // !!! levels 015 018 019 020 021 022 023 026 027 028 !!!
5676 else if (ELEM_IS_PLAYER(center_element))
5677 Store[x][y] = EL_EMPTY;
5678 else if (center_element == EL_YAMYAM)
5679 Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
5680 else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
5681 Store[x][y] = element_info[center_element].content.e[xx][yy];
5683 // needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
5684 // (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
5685 // otherwise) -- FIX THIS !!!
5686 else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
5687 Store[x][y] = element_info[element].content.e[1][1];
5689 else if (!CAN_EXPLODE(element))
5690 Store[x][y] = element_info[element].content.e[1][1];
5693 Store[x][y] = EL_EMPTY;
5695 if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
5696 center_element == EL_AMOEBA_TO_DIAMOND)
5697 Store2[x][y] = element;
5699 Feld[x][y] = EL_EXPLOSION;
5700 GfxElement[x][y] = artwork_element;
5702 ExplodePhase[x][y] = 1;
5703 ExplodeDelay[x][y] = last_phase;
5708 if (center_element == EL_YAMYAM)
5709 game.yamyam_content_nr =
5710 (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
5722 GfxFrame[x][y] = 0; // restart explosion animation
5724 last_phase = ExplodeDelay[x][y];
5726 ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
5728 // this can happen if the player leaves an explosion just in time
5729 if (GfxElement[x][y] == EL_UNDEFINED)
5730 GfxElement[x][y] = EL_EMPTY;
5732 border_element = Store2[x][y];
5733 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5734 border_element = StorePlayer[x][y];
5736 if (phase == element_info[border_element].ignition_delay ||
5737 phase == last_phase)
5739 boolean border_explosion = FALSE;
5741 if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
5742 !PLAYER_EXPLOSION_PROTECTED(x, y))
5744 KillPlayerUnlessExplosionProtected(x, y);
5745 border_explosion = TRUE;
5747 else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
5749 Feld[x][y] = Store2[x][y];
5752 border_explosion = TRUE;
5754 else if (border_element == EL_AMOEBA_TO_DIAMOND)
5756 AmoebeUmwandeln(x, y);
5758 border_explosion = TRUE;
5761 // if an element just explodes due to another explosion (chain-reaction),
5762 // do not immediately end the new explosion when it was the last frame of
5763 // the explosion (as it would be done in the following "if"-statement!)
5764 if (border_explosion && phase == last_phase)
5768 if (phase == last_phase)
5772 element = Feld[x][y] = Store[x][y];
5773 Store[x][y] = Store2[x][y] = 0;
5774 GfxElement[x][y] = EL_UNDEFINED;
5776 // player can escape from explosions and might therefore be still alive
5777 if (element >= EL_PLAYER_IS_EXPLODING_1 &&
5778 element <= EL_PLAYER_IS_EXPLODING_4)
5780 int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
5781 int explosion_element = EL_PLAYER_1 + player_nr;
5782 int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
5783 int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
5785 if (level.use_explosion_element[player_nr])
5786 explosion_element = level.explosion_element[player_nr];
5788 Feld[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
5789 element_info[explosion_element].content.e[xx][yy]);
5792 // restore probably existing indestructible background element
5793 if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
5794 element = Feld[x][y] = Back[x][y];
5797 MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
5798 GfxDir[x][y] = MV_NONE;
5799 ChangeDelay[x][y] = 0;
5800 ChangePage[x][y] = -1;
5802 CustomValue[x][y] = 0;
5804 InitField_WithBug2(x, y, FALSE);
5806 TEST_DrawLevelField(x, y);
5808 TestIfElementTouchesCustomElement(x, y);
5810 if (GFX_CRUMBLED(element))
5811 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
5813 if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
5814 StorePlayer[x][y] = 0;
5816 if (ELEM_IS_PLAYER(element))
5817 RelocatePlayer(x, y, element);
5819 else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
5821 int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
5822 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5825 TEST_DrawLevelFieldCrumbled(x, y);
5827 if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
5829 DrawLevelElement(x, y, Back[x][y]);
5830 DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
5832 else if (IS_WALKABLE_UNDER(Back[x][y]))
5834 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5835 DrawLevelElementThruMask(x, y, Back[x][y]);
5837 else if (!IS_WALKABLE_INSIDE(Back[x][y]))
5838 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5842 static void DynaExplode(int ex, int ey)
5845 int dynabomb_element = Feld[ex][ey];
5846 int dynabomb_size = 1;
5847 boolean dynabomb_xl = FALSE;
5848 struct PlayerInfo *player;
5849 static int xy[4][2] =
5857 if (IS_ACTIVE_BOMB(dynabomb_element))
5859 player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
5860 dynabomb_size = player->dynabomb_size;
5861 dynabomb_xl = player->dynabomb_xl;
5862 player->dynabombs_left++;
5865 Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
5867 for (i = 0; i < NUM_DIRECTIONS; i++)
5869 for (j = 1; j <= dynabomb_size; j++)
5871 int x = ex + j * xy[i][0];
5872 int y = ey + j * xy[i][1];
5875 if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
5878 element = Feld[x][y];
5880 // do not restart explosions of fields with active bombs
5881 if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
5884 Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
5886 if (element != EL_EMPTY && element != EL_EXPLOSION &&
5887 !IS_DIGGABLE(element) && !dynabomb_xl)
5893 void Bang(int x, int y)
5895 int element = MovingOrBlocked2Element(x, y);
5896 int explosion_type = EX_TYPE_NORMAL;
5898 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5900 struct PlayerInfo *player = PLAYERINFO(x, y);
5902 element = Feld[x][y] = player->initial_element;
5904 if (level.use_explosion_element[player->index_nr])
5906 int explosion_element = level.explosion_element[player->index_nr];
5908 if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
5909 explosion_type = EX_TYPE_CROSS;
5910 else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
5911 explosion_type = EX_TYPE_CENTER;
5919 case EL_BD_BUTTERFLY:
5922 case EL_DARK_YAMYAM:
5926 RaiseScoreElement(element);
5929 case EL_DYNABOMB_PLAYER_1_ACTIVE:
5930 case EL_DYNABOMB_PLAYER_2_ACTIVE:
5931 case EL_DYNABOMB_PLAYER_3_ACTIVE:
5932 case EL_DYNABOMB_PLAYER_4_ACTIVE:
5933 case EL_DYNABOMB_INCREASE_NUMBER:
5934 case EL_DYNABOMB_INCREASE_SIZE:
5935 case EL_DYNABOMB_INCREASE_POWER:
5936 explosion_type = EX_TYPE_DYNA;
5939 case EL_DC_LANDMINE:
5940 explosion_type = EX_TYPE_CENTER;
5945 case EL_LAMP_ACTIVE:
5946 case EL_AMOEBA_TO_DIAMOND:
5947 if (!IS_PLAYER(x, y)) // penguin and player may be at same field
5948 explosion_type = EX_TYPE_CENTER;
5952 if (element_info[element].explosion_type == EXPLODES_CROSS)
5953 explosion_type = EX_TYPE_CROSS;
5954 else if (element_info[element].explosion_type == EXPLODES_1X1)
5955 explosion_type = EX_TYPE_CENTER;
5959 if (explosion_type == EX_TYPE_DYNA)
5962 Explode(x, y, EX_PHASE_START, explosion_type);
5964 CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
5967 static void SplashAcid(int x, int y)
5969 if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
5970 (!IN_LEV_FIELD(x - 1, y - 2) ||
5971 !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
5972 Feld[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
5974 if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
5975 (!IN_LEV_FIELD(x + 1, y - 2) ||
5976 !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
5977 Feld[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
5979 PlayLevelSound(x, y, SND_ACID_SPLASHING);
5982 static void InitBeltMovement(void)
5984 static int belt_base_element[4] =
5986 EL_CONVEYOR_BELT_1_LEFT,
5987 EL_CONVEYOR_BELT_2_LEFT,
5988 EL_CONVEYOR_BELT_3_LEFT,
5989 EL_CONVEYOR_BELT_4_LEFT
5991 static int belt_base_active_element[4] =
5993 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
5994 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
5995 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
5996 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
6001 // set frame order for belt animation graphic according to belt direction
6002 for (i = 0; i < NUM_BELTS; i++)
6006 for (j = 0; j < NUM_BELT_PARTS; j++)
6008 int element = belt_base_active_element[belt_nr] + j;
6009 int graphic_1 = el2img(element);
6010 int graphic_2 = el2panelimg(element);
6012 if (game.belt_dir[i] == MV_LEFT)
6014 graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
6015 graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
6019 graphic_info[graphic_1].anim_mode |= ANIM_REVERSE;
6020 graphic_info[graphic_2].anim_mode |= ANIM_REVERSE;
6025 SCAN_PLAYFIELD(x, y)
6027 int element = Feld[x][y];
6029 for (i = 0; i < NUM_BELTS; i++)
6031 if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
6033 int e_belt_nr = getBeltNrFromBeltElement(element);
6036 if (e_belt_nr == belt_nr)
6038 int belt_part = Feld[x][y] - belt_base_element[belt_nr];
6040 Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
6047 static void ToggleBeltSwitch(int x, int y)
6049 static int belt_base_element[4] =
6051 EL_CONVEYOR_BELT_1_LEFT,
6052 EL_CONVEYOR_BELT_2_LEFT,
6053 EL_CONVEYOR_BELT_3_LEFT,
6054 EL_CONVEYOR_BELT_4_LEFT
6056 static int belt_base_active_element[4] =
6058 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
6059 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
6060 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
6061 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
6063 static int belt_base_switch_element[4] =
6065 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
6066 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
6067 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
6068 EL_CONVEYOR_BELT_4_SWITCH_LEFT
6070 static int belt_move_dir[4] =
6078 int element = Feld[x][y];
6079 int belt_nr = getBeltNrFromBeltSwitchElement(element);
6080 int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
6081 int belt_dir = belt_move_dir[belt_dir_nr];
6084 if (!IS_BELT_SWITCH(element))
6087 game.belt_dir_nr[belt_nr] = belt_dir_nr;
6088 game.belt_dir[belt_nr] = belt_dir;
6090 if (belt_dir_nr == 3)
6093 // set frame order for belt animation graphic according to belt direction
6094 for (i = 0; i < NUM_BELT_PARTS; i++)
6096 int element = belt_base_active_element[belt_nr] + i;
6097 int graphic_1 = el2img(element);
6098 int graphic_2 = el2panelimg(element);
6100 if (belt_dir == MV_LEFT)
6102 graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
6103 graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
6107 graphic_info[graphic_1].anim_mode |= ANIM_REVERSE;
6108 graphic_info[graphic_2].anim_mode |= ANIM_REVERSE;
6112 SCAN_PLAYFIELD(xx, yy)
6114 int element = Feld[xx][yy];
6116 if (IS_BELT_SWITCH(element))
6118 int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
6120 if (e_belt_nr == belt_nr)
6122 Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
6123 TEST_DrawLevelField(xx, yy);
6126 else if (IS_BELT(element) && belt_dir != MV_NONE)
6128 int e_belt_nr = getBeltNrFromBeltElement(element);
6130 if (e_belt_nr == belt_nr)
6132 int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
6134 Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
6135 TEST_DrawLevelField(xx, yy);
6138 else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
6140 int e_belt_nr = getBeltNrFromBeltActiveElement(element);
6142 if (e_belt_nr == belt_nr)
6144 int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
6146 Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
6147 TEST_DrawLevelField(xx, yy);
6153 static void ToggleSwitchgateSwitch(int x, int y)
6157 game.switchgate_pos = !game.switchgate_pos;
6159 SCAN_PLAYFIELD(xx, yy)
6161 int element = Feld[xx][yy];
6163 if (element == EL_SWITCHGATE_SWITCH_UP)
6165 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_DOWN;
6166 TEST_DrawLevelField(xx, yy);
6168 else if (element == EL_SWITCHGATE_SWITCH_DOWN)
6170 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP;
6171 TEST_DrawLevelField(xx, yy);
6173 else if (element == EL_DC_SWITCHGATE_SWITCH_UP)
6175 Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_DOWN;
6176 TEST_DrawLevelField(xx, yy);
6178 else if (element == EL_DC_SWITCHGATE_SWITCH_DOWN)
6180 Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP;
6181 TEST_DrawLevelField(xx, yy);
6183 else if (element == EL_SWITCHGATE_OPEN ||
6184 element == EL_SWITCHGATE_OPENING)
6186 Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
6188 PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
6190 else if (element == EL_SWITCHGATE_CLOSED ||
6191 element == EL_SWITCHGATE_CLOSING)
6193 Feld[xx][yy] = EL_SWITCHGATE_OPENING;
6195 PlayLevelSoundAction(xx, yy, ACTION_OPENING);
6200 static int getInvisibleActiveFromInvisibleElement(int element)
6202 return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
6203 element == EL_INVISIBLE_WALL ? EL_INVISIBLE_WALL_ACTIVE :
6204 element == EL_INVISIBLE_SAND ? EL_INVISIBLE_SAND_ACTIVE :
6208 static int getInvisibleFromInvisibleActiveElement(int element)
6210 return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
6211 element == EL_INVISIBLE_WALL_ACTIVE ? EL_INVISIBLE_WALL :
6212 element == EL_INVISIBLE_SAND_ACTIVE ? EL_INVISIBLE_SAND :
6216 static void RedrawAllLightSwitchesAndInvisibleElements(void)
6220 SCAN_PLAYFIELD(x, y)
6222 int element = Feld[x][y];
6224 if (element == EL_LIGHT_SWITCH &&
6225 game.light_time_left > 0)
6227 Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
6228 TEST_DrawLevelField(x, y);
6230 else if (element == EL_LIGHT_SWITCH_ACTIVE &&
6231 game.light_time_left == 0)
6233 Feld[x][y] = EL_LIGHT_SWITCH;
6234 TEST_DrawLevelField(x, y);
6236 else if (element == EL_EMC_DRIPPER &&
6237 game.light_time_left > 0)
6239 Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
6240 TEST_DrawLevelField(x, y);
6242 else if (element == EL_EMC_DRIPPER_ACTIVE &&
6243 game.light_time_left == 0)
6245 Feld[x][y] = EL_EMC_DRIPPER;
6246 TEST_DrawLevelField(x, y);
6248 else if (element == EL_INVISIBLE_STEELWALL ||
6249 element == EL_INVISIBLE_WALL ||
6250 element == EL_INVISIBLE_SAND)
6252 if (game.light_time_left > 0)
6253 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
6255 TEST_DrawLevelField(x, y);
6257 // uncrumble neighbour fields, if needed
6258 if (element == EL_INVISIBLE_SAND)
6259 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6261 else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6262 element == EL_INVISIBLE_WALL_ACTIVE ||
6263 element == EL_INVISIBLE_SAND_ACTIVE)
6265 if (game.light_time_left == 0)
6266 Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
6268 TEST_DrawLevelField(x, y);
6270 // re-crumble neighbour fields, if needed
6271 if (element == EL_INVISIBLE_SAND)
6272 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6277 static void RedrawAllInvisibleElementsForLenses(void)
6281 SCAN_PLAYFIELD(x, y)
6283 int element = Feld[x][y];
6285 if (element == EL_EMC_DRIPPER &&
6286 game.lenses_time_left > 0)
6288 Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
6289 TEST_DrawLevelField(x, y);
6291 else if (element == EL_EMC_DRIPPER_ACTIVE &&
6292 game.lenses_time_left == 0)
6294 Feld[x][y] = EL_EMC_DRIPPER;
6295 TEST_DrawLevelField(x, y);
6297 else if (element == EL_INVISIBLE_STEELWALL ||
6298 element == EL_INVISIBLE_WALL ||
6299 element == EL_INVISIBLE_SAND)
6301 if (game.lenses_time_left > 0)
6302 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
6304 TEST_DrawLevelField(x, y);
6306 // uncrumble neighbour fields, if needed
6307 if (element == EL_INVISIBLE_SAND)
6308 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6310 else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6311 element == EL_INVISIBLE_WALL_ACTIVE ||
6312 element == EL_INVISIBLE_SAND_ACTIVE)
6314 if (game.lenses_time_left == 0)
6315 Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
6317 TEST_DrawLevelField(x, y);
6319 // re-crumble neighbour fields, if needed
6320 if (element == EL_INVISIBLE_SAND)
6321 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6326 static void RedrawAllInvisibleElementsForMagnifier(void)
6330 SCAN_PLAYFIELD(x, y)
6332 int element = Feld[x][y];
6334 if (element == EL_EMC_FAKE_GRASS &&
6335 game.magnify_time_left > 0)
6337 Feld[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
6338 TEST_DrawLevelField(x, y);
6340 else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
6341 game.magnify_time_left == 0)
6343 Feld[x][y] = EL_EMC_FAKE_GRASS;
6344 TEST_DrawLevelField(x, y);
6346 else if (IS_GATE_GRAY(element) &&
6347 game.magnify_time_left > 0)
6349 Feld[x][y] = (IS_RND_GATE_GRAY(element) ?
6350 element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
6351 IS_EM_GATE_GRAY(element) ?
6352 element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
6353 IS_EMC_GATE_GRAY(element) ?
6354 element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
6355 IS_DC_GATE_GRAY(element) ?
6356 EL_DC_GATE_WHITE_GRAY_ACTIVE :
6358 TEST_DrawLevelField(x, y);
6360 else if (IS_GATE_GRAY_ACTIVE(element) &&
6361 game.magnify_time_left == 0)
6363 Feld[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
6364 element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
6365 IS_EM_GATE_GRAY_ACTIVE(element) ?
6366 element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
6367 IS_EMC_GATE_GRAY_ACTIVE(element) ?
6368 element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
6369 IS_DC_GATE_GRAY_ACTIVE(element) ?
6370 EL_DC_GATE_WHITE_GRAY :
6372 TEST_DrawLevelField(x, y);
6377 static void ToggleLightSwitch(int x, int y)
6379 int element = Feld[x][y];
6381 game.light_time_left =
6382 (element == EL_LIGHT_SWITCH ?
6383 level.time_light * FRAMES_PER_SECOND : 0);
6385 RedrawAllLightSwitchesAndInvisibleElements();
6388 static void ActivateTimegateSwitch(int x, int y)
6392 game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
6394 SCAN_PLAYFIELD(xx, yy)
6396 int element = Feld[xx][yy];
6398 if (element == EL_TIMEGATE_CLOSED ||
6399 element == EL_TIMEGATE_CLOSING)
6401 Feld[xx][yy] = EL_TIMEGATE_OPENING;
6402 PlayLevelSound(xx, yy, SND_CLASS_TIMEGATE_OPENING);
6406 else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
6408 Feld[xx][yy] = EL_TIMEGATE_SWITCH;
6409 TEST_DrawLevelField(xx, yy);
6415 Feld[x][y] = (Feld[x][y] == EL_TIMEGATE_SWITCH ? EL_TIMEGATE_SWITCH_ACTIVE :
6416 EL_DC_TIMEGATE_SWITCH_ACTIVE);
6419 static void Impact(int x, int y)
6421 boolean last_line = (y == lev_fieldy - 1);
6422 boolean object_hit = FALSE;
6423 boolean impact = (last_line || object_hit);
6424 int element = Feld[x][y];
6425 int smashed = EL_STEELWALL;
6427 if (!last_line) // check if element below was hit
6429 if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
6432 object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
6433 MovDir[x][y + 1] != MV_DOWN ||
6434 MovPos[x][y + 1] <= TILEY / 2));
6436 // do not smash moving elements that left the smashed field in time
6437 if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
6438 ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
6441 #if USE_QUICKSAND_IMPACT_BUGFIX
6442 if (Feld[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
6444 RemoveMovingField(x, y + 1);
6445 Feld[x][y + 1] = EL_QUICKSAND_EMPTY;
6446 Feld[x][y + 2] = EL_ROCK;
6447 TEST_DrawLevelField(x, y + 2);
6452 if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTYING && object_hit == FALSE)
6454 RemoveMovingField(x, y + 1);
6455 Feld[x][y + 1] = EL_QUICKSAND_FAST_EMPTY;
6456 Feld[x][y + 2] = EL_ROCK;
6457 TEST_DrawLevelField(x, y + 2);
6464 smashed = MovingOrBlocked2Element(x, y + 1);
6466 impact = (last_line || object_hit);
6469 if (!last_line && smashed == EL_ACID) // element falls into acid
6471 SplashAcid(x, y + 1);
6475 // !!! not sufficient for all cases -- see EL_PEARL below !!!
6476 // only reset graphic animation if graphic really changes after impact
6478 el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
6480 ResetGfxAnimation(x, y);
6481 TEST_DrawLevelField(x, y);
6484 if (impact && CAN_EXPLODE_IMPACT(element))
6489 else if (impact && element == EL_PEARL &&
6490 smashed != EL_DC_MAGIC_WALL && smashed != EL_DC_MAGIC_WALL_ACTIVE)
6492 ResetGfxAnimation(x, y);
6494 Feld[x][y] = EL_PEARL_BREAKING;
6495 PlayLevelSound(x, y, SND_PEARL_BREAKING);
6498 else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
6500 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6505 if (impact && element == EL_AMOEBA_DROP)
6507 if (object_hit && IS_PLAYER(x, y + 1))
6508 KillPlayerUnlessEnemyProtected(x, y + 1);
6509 else if (object_hit && smashed == EL_PENGUIN)
6513 Feld[x][y] = EL_AMOEBA_GROWING;
6514 Store[x][y] = EL_AMOEBA_WET;
6516 ResetRandomAnimationValue(x, y);
6521 if (object_hit) // check which object was hit
6523 if ((CAN_PASS_MAGIC_WALL(element) &&
6524 (smashed == EL_MAGIC_WALL ||
6525 smashed == EL_BD_MAGIC_WALL)) ||
6526 (CAN_PASS_DC_MAGIC_WALL(element) &&
6527 smashed == EL_DC_MAGIC_WALL))
6530 int activated_magic_wall =
6531 (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
6532 smashed == EL_BD_MAGIC_WALL ? EL_BD_MAGIC_WALL_ACTIVE :
6533 EL_DC_MAGIC_WALL_ACTIVE);
6535 // activate magic wall / mill
6536 SCAN_PLAYFIELD(xx, yy)
6538 if (Feld[xx][yy] == smashed)
6539 Feld[xx][yy] = activated_magic_wall;
6542 game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
6543 game.magic_wall_active = TRUE;
6545 PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
6546 SND_MAGIC_WALL_ACTIVATING :
6547 smashed == EL_BD_MAGIC_WALL ?
6548 SND_BD_MAGIC_WALL_ACTIVATING :
6549 SND_DC_MAGIC_WALL_ACTIVATING));
6552 if (IS_PLAYER(x, y + 1))
6554 if (CAN_SMASH_PLAYER(element))
6556 KillPlayerUnlessEnemyProtected(x, y + 1);
6560 else if (smashed == EL_PENGUIN)
6562 if (CAN_SMASH_PLAYER(element))
6568 else if (element == EL_BD_DIAMOND)
6570 if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
6576 else if (((element == EL_SP_INFOTRON ||
6577 element == EL_SP_ZONK) &&
6578 (smashed == EL_SP_SNIKSNAK ||
6579 smashed == EL_SP_ELECTRON ||
6580 smashed == EL_SP_DISK_ORANGE)) ||
6581 (element == EL_SP_INFOTRON &&
6582 smashed == EL_SP_DISK_YELLOW))
6587 else if (CAN_SMASH_EVERYTHING(element))
6589 if (IS_CLASSIC_ENEMY(smashed) ||
6590 CAN_EXPLODE_SMASHED(smashed))
6595 else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
6597 if (smashed == EL_LAMP ||
6598 smashed == EL_LAMP_ACTIVE)
6603 else if (smashed == EL_NUT)
6605 Feld[x][y + 1] = EL_NUT_BREAKING;
6606 PlayLevelSound(x, y, SND_NUT_BREAKING);
6607 RaiseScoreElement(EL_NUT);
6610 else if (smashed == EL_PEARL)
6612 ResetGfxAnimation(x, y);
6614 Feld[x][y + 1] = EL_PEARL_BREAKING;
6615 PlayLevelSound(x, y, SND_PEARL_BREAKING);
6618 else if (smashed == EL_DIAMOND)
6620 Feld[x][y + 1] = EL_DIAMOND_BREAKING;
6621 PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
6624 else if (IS_BELT_SWITCH(smashed))
6626 ToggleBeltSwitch(x, y + 1);
6628 else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
6629 smashed == EL_SWITCHGATE_SWITCH_DOWN ||
6630 smashed == EL_DC_SWITCHGATE_SWITCH_UP ||
6631 smashed == EL_DC_SWITCHGATE_SWITCH_DOWN)
6633 ToggleSwitchgateSwitch(x, y + 1);
6635 else if (smashed == EL_LIGHT_SWITCH ||
6636 smashed == EL_LIGHT_SWITCH_ACTIVE)
6638 ToggleLightSwitch(x, y + 1);
6642 CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6644 CheckElementChangeBySide(x, y + 1, smashed, element,
6645 CE_SWITCHED, CH_SIDE_TOP);
6646 CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
6652 CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6657 // play sound of magic wall / mill
6659 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
6660 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ||
6661 Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE))
6663 if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
6664 PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
6665 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
6666 PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
6667 else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
6668 PlayLevelSound(x, y, SND_DC_MAGIC_WALL_FILLING);
6673 // play sound of object that hits the ground
6674 if (last_line || object_hit)
6675 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6678 static void TurnRoundExt(int x, int y)
6690 { 0, 0 }, { 0, 0 }, { 0, 0 },
6695 int left, right, back;
6699 { MV_DOWN, MV_UP, MV_RIGHT },
6700 { MV_UP, MV_DOWN, MV_LEFT },
6702 { MV_LEFT, MV_RIGHT, MV_DOWN },
6706 { MV_RIGHT, MV_LEFT, MV_UP }
6709 int element = Feld[x][y];
6710 int move_pattern = element_info[element].move_pattern;
6712 int old_move_dir = MovDir[x][y];
6713 int left_dir = turn[old_move_dir].left;
6714 int right_dir = turn[old_move_dir].right;
6715 int back_dir = turn[old_move_dir].back;
6717 int left_dx = move_xy[left_dir].dx, left_dy = move_xy[left_dir].dy;
6718 int right_dx = move_xy[right_dir].dx, right_dy = move_xy[right_dir].dy;
6719 int move_dx = move_xy[old_move_dir].dx, move_dy = move_xy[old_move_dir].dy;
6720 int back_dx = move_xy[back_dir].dx, back_dy = move_xy[back_dir].dy;
6722 int left_x = x + left_dx, left_y = y + left_dy;
6723 int right_x = x + right_dx, right_y = y + right_dy;
6724 int move_x = x + move_dx, move_y = y + move_dy;
6728 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
6730 TestIfBadThingTouchesOtherBadThing(x, y);
6732 if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
6733 MovDir[x][y] = right_dir;
6734 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6735 MovDir[x][y] = left_dir;
6737 if (element == EL_BUG && MovDir[x][y] != old_move_dir)
6739 else if (element == EL_BD_BUTTERFLY) // && MovDir[x][y] == left_dir)
6742 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
6744 TestIfBadThingTouchesOtherBadThing(x, y);
6746 if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
6747 MovDir[x][y] = left_dir;
6748 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6749 MovDir[x][y] = right_dir;
6751 if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
6753 else if (element == EL_BD_FIREFLY) // && MovDir[x][y] == right_dir)
6756 else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
6758 TestIfBadThingTouchesOtherBadThing(x, y);
6760 if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
6761 MovDir[x][y] = left_dir;
6762 else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
6763 MovDir[x][y] = right_dir;
6765 if (MovDir[x][y] != old_move_dir)
6768 else if (element == EL_YAMYAM)
6770 boolean can_turn_left = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
6771 boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
6773 if (can_turn_left && can_turn_right)
6774 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6775 else if (can_turn_left)
6776 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6777 else if (can_turn_right)
6778 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6780 MovDir[x][y] = back_dir;
6782 MovDelay[x][y] = 16 + 16 * RND(3);
6784 else if (element == EL_DARK_YAMYAM)
6786 boolean can_turn_left = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6788 boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6791 if (can_turn_left && can_turn_right)
6792 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6793 else if (can_turn_left)
6794 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6795 else if (can_turn_right)
6796 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6798 MovDir[x][y] = back_dir;
6800 MovDelay[x][y] = 16 + 16 * RND(3);
6802 else if (element == EL_PACMAN)
6804 boolean can_turn_left = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
6805 boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
6807 if (can_turn_left && can_turn_right)
6808 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6809 else if (can_turn_left)
6810 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6811 else if (can_turn_right)
6812 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6814 MovDir[x][y] = back_dir;
6816 MovDelay[x][y] = 6 + RND(40);
6818 else if (element == EL_PIG)
6820 boolean can_turn_left = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
6821 boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
6822 boolean can_move_on = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
6823 boolean should_turn_left, should_turn_right, should_move_on;
6825 int rnd = RND(rnd_value);
6827 should_turn_left = (can_turn_left &&
6829 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
6830 y + back_dy + left_dy)));
6831 should_turn_right = (can_turn_right &&
6833 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
6834 y + back_dy + right_dy)));
6835 should_move_on = (can_move_on &&
6838 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
6839 y + move_dy + left_dy) ||
6840 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
6841 y + move_dy + right_dy)));
6843 if (should_turn_left || should_turn_right || should_move_on)
6845 if (should_turn_left && should_turn_right && should_move_on)
6846 MovDir[x][y] = (rnd < rnd_value / 3 ? left_dir :
6847 rnd < 2 * rnd_value / 3 ? right_dir :
6849 else if (should_turn_left && should_turn_right)
6850 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6851 else if (should_turn_left && should_move_on)
6852 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
6853 else if (should_turn_right && should_move_on)
6854 MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
6855 else if (should_turn_left)
6856 MovDir[x][y] = left_dir;
6857 else if (should_turn_right)
6858 MovDir[x][y] = right_dir;
6859 else if (should_move_on)
6860 MovDir[x][y] = old_move_dir;
6862 else if (can_move_on && rnd > rnd_value / 8)
6863 MovDir[x][y] = old_move_dir;
6864 else if (can_turn_left && can_turn_right)
6865 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6866 else if (can_turn_left && rnd > rnd_value / 8)
6867 MovDir[x][y] = left_dir;
6868 else if (can_turn_right && rnd > rnd_value/8)
6869 MovDir[x][y] = right_dir;
6871 MovDir[x][y] = back_dir;
6873 xx = x + move_xy[MovDir[x][y]].dx;
6874 yy = y + move_xy[MovDir[x][y]].dy;
6876 if (!IN_LEV_FIELD(xx, yy) ||
6877 (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy])))
6878 MovDir[x][y] = old_move_dir;
6882 else if (element == EL_DRAGON)
6884 boolean can_turn_left = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
6885 boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
6886 boolean can_move_on = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
6888 int rnd = RND(rnd_value);
6890 if (can_move_on && rnd > rnd_value / 8)
6891 MovDir[x][y] = old_move_dir;
6892 else if (can_turn_left && can_turn_right)
6893 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6894 else if (can_turn_left && rnd > rnd_value / 8)
6895 MovDir[x][y] = left_dir;
6896 else if (can_turn_right && rnd > rnd_value / 8)
6897 MovDir[x][y] = right_dir;
6899 MovDir[x][y] = back_dir;
6901 xx = x + move_xy[MovDir[x][y]].dx;
6902 yy = y + move_xy[MovDir[x][y]].dy;
6904 if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
6905 MovDir[x][y] = old_move_dir;
6909 else if (element == EL_MOLE)
6911 boolean can_move_on =
6912 (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
6913 IS_AMOEBOID(Feld[move_x][move_y]) ||
6914 Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
6917 boolean can_turn_left =
6918 (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
6919 IS_AMOEBOID(Feld[left_x][left_y])));
6921 boolean can_turn_right =
6922 (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
6923 IS_AMOEBOID(Feld[right_x][right_y])));
6925 if (can_turn_left && can_turn_right)
6926 MovDir[x][y] = (RND(2) ? left_dir : right_dir);
6927 else if (can_turn_left)
6928 MovDir[x][y] = left_dir;
6930 MovDir[x][y] = right_dir;
6933 if (MovDir[x][y] != old_move_dir)
6936 else if (element == EL_BALLOON)
6938 MovDir[x][y] = game.wind_direction;
6941 else if (element == EL_SPRING)
6943 if (MovDir[x][y] & MV_HORIZONTAL)
6945 if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
6946 !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
6948 Feld[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
6949 ResetGfxAnimation(move_x, move_y);
6950 TEST_DrawLevelField(move_x, move_y);
6952 MovDir[x][y] = back_dir;
6954 else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
6955 SPRING_CAN_ENTER_FIELD(element, x, y + 1))
6956 MovDir[x][y] = MV_NONE;
6961 else if (element == EL_ROBOT ||
6962 element == EL_SATELLITE ||
6963 element == EL_PENGUIN ||
6964 element == EL_EMC_ANDROID)
6966 int attr_x = -1, attr_y = -1;
6968 if (game.all_players_gone)
6970 attr_x = game.exit_x;
6971 attr_y = game.exit_y;
6977 for (i = 0; i < MAX_PLAYERS; i++)
6979 struct PlayerInfo *player = &stored_player[i];
6980 int jx = player->jx, jy = player->jy;
6982 if (!player->active)
6986 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
6994 if (element == EL_ROBOT &&
6995 game.robot_wheel_x >= 0 &&
6996 game.robot_wheel_y >= 0 &&
6997 (Feld[game.robot_wheel_x][game.robot_wheel_y] == EL_ROBOT_WHEEL_ACTIVE ||
6998 game.engine_version < VERSION_IDENT(3,1,0,0)))
7000 attr_x = game.robot_wheel_x;
7001 attr_y = game.robot_wheel_y;
7004 if (element == EL_PENGUIN)
7007 static int xy[4][2] =
7015 for (i = 0; i < NUM_DIRECTIONS; i++)
7017 int ex = x + xy[i][0];
7018 int ey = y + xy[i][1];
7020 if (IN_LEV_FIELD(ex, ey) && (Feld[ex][ey] == EL_EXIT_OPEN ||
7021 Feld[ex][ey] == EL_EM_EXIT_OPEN ||
7022 Feld[ex][ey] == EL_STEEL_EXIT_OPEN ||
7023 Feld[ex][ey] == EL_EM_STEEL_EXIT_OPEN))
7032 MovDir[x][y] = MV_NONE;
7034 MovDir[x][y] |= (game.all_players_gone ? MV_RIGHT : MV_LEFT);
7035 else if (attr_x > x)
7036 MovDir[x][y] |= (game.all_players_gone ? MV_LEFT : MV_RIGHT);
7038 MovDir[x][y] |= (game.all_players_gone ? MV_DOWN : MV_UP);
7039 else if (attr_y > y)
7040 MovDir[x][y] |= (game.all_players_gone ? MV_UP : MV_DOWN);
7042 if (element == EL_ROBOT)
7046 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7047 MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
7048 Moving2Blocked(x, y, &newx, &newy);
7050 if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
7051 MovDelay[x][y] = 8 + 8 * !RND(3);
7053 MovDelay[x][y] = 16;
7055 else if (element == EL_PENGUIN)
7061 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7063 boolean first_horiz = RND(2);
7064 int new_move_dir = MovDir[x][y];
7067 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7068 Moving2Blocked(x, y, &newx, &newy);
7070 if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
7074 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7075 Moving2Blocked(x, y, &newx, &newy);
7077 if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
7080 MovDir[x][y] = old_move_dir;
7084 else if (element == EL_SATELLITE)
7090 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7092 boolean first_horiz = RND(2);
7093 int new_move_dir = MovDir[x][y];
7096 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7097 Moving2Blocked(x, y, &newx, &newy);
7099 if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
7103 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7104 Moving2Blocked(x, y, &newx, &newy);
7106 if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
7109 MovDir[x][y] = old_move_dir;
7113 else if (element == EL_EMC_ANDROID)
7115 static int check_pos[16] =
7117 -1, // 0 => (invalid)
7120 -1, // 3 => (invalid)
7122 0, // 5 => MV_LEFT | MV_UP
7123 2, // 6 => MV_RIGHT | MV_UP
7124 -1, // 7 => (invalid)
7126 6, // 9 => MV_LEFT | MV_DOWN
7127 4, // 10 => MV_RIGHT | MV_DOWN
7128 -1, // 11 => (invalid)
7129 -1, // 12 => (invalid)
7130 -1, // 13 => (invalid)
7131 -1, // 14 => (invalid)
7132 -1, // 15 => (invalid)
7140 { -1, -1, MV_LEFT | MV_UP },
7142 { +1, -1, MV_RIGHT | MV_UP },
7143 { +1, 0, MV_RIGHT },
7144 { +1, +1, MV_RIGHT | MV_DOWN },
7146 { -1, +1, MV_LEFT | MV_DOWN },
7149 int start_pos, check_order;
7150 boolean can_clone = FALSE;
7153 // check if there is any free field around current position
7154 for (i = 0; i < 8; i++)
7156 int newx = x + check_xy[i].dx;
7157 int newy = y + check_xy[i].dy;
7159 if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7167 if (can_clone) // randomly find an element to clone
7171 start_pos = check_pos[RND(8)];
7172 check_order = (RND(2) ? -1 : +1);
7174 for (i = 0; i < 8; i++)
7176 int pos_raw = start_pos + i * check_order;
7177 int pos = (pos_raw + 8) % 8;
7178 int newx = x + check_xy[pos].dx;
7179 int newy = y + check_xy[pos].dy;
7181 if (ANDROID_CAN_CLONE_FIELD(newx, newy))
7183 element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
7184 element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
7186 Store[x][y] = Feld[newx][newy];
7195 if (can_clone) // randomly find a direction to move
7199 start_pos = check_pos[RND(8)];
7200 check_order = (RND(2) ? -1 : +1);
7202 for (i = 0; i < 8; i++)
7204 int pos_raw = start_pos + i * check_order;
7205 int pos = (pos_raw + 8) % 8;
7206 int newx = x + check_xy[pos].dx;
7207 int newy = y + check_xy[pos].dy;
7208 int new_move_dir = check_xy[pos].dir;
7210 if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7212 MovDir[x][y] = new_move_dir;
7213 MovDelay[x][y] = level.android_clone_time * 8 + 1;
7222 if (can_clone) // cloning and moving successful
7225 // cannot clone -- try to move towards player
7227 start_pos = check_pos[MovDir[x][y] & 0x0f];
7228 check_order = (RND(2) ? -1 : +1);
7230 for (i = 0; i < 3; i++)
7232 // first check start_pos, then previous/next or (next/previous) pos
7233 int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
7234 int pos = (pos_raw + 8) % 8;
7235 int newx = x + check_xy[pos].dx;
7236 int newy = y + check_xy[pos].dy;
7237 int new_move_dir = check_xy[pos].dir;
7239 if (IS_PLAYER(newx, newy))
7242 if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7244 MovDir[x][y] = new_move_dir;
7245 MovDelay[x][y] = level.android_move_time * 8 + 1;
7252 else if (move_pattern == MV_TURNING_LEFT ||
7253 move_pattern == MV_TURNING_RIGHT ||
7254 move_pattern == MV_TURNING_LEFT_RIGHT ||
7255 move_pattern == MV_TURNING_RIGHT_LEFT ||
7256 move_pattern == MV_TURNING_RANDOM ||
7257 move_pattern == MV_ALL_DIRECTIONS)
7259 boolean can_turn_left =
7260 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
7261 boolean can_turn_right =
7262 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
7264 if (element_info[element].move_stepsize == 0) // "not moving"
7267 if (move_pattern == MV_TURNING_LEFT)
7268 MovDir[x][y] = left_dir;
7269 else if (move_pattern == MV_TURNING_RIGHT)
7270 MovDir[x][y] = right_dir;
7271 else if (move_pattern == MV_TURNING_LEFT_RIGHT)
7272 MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
7273 else if (move_pattern == MV_TURNING_RIGHT_LEFT)
7274 MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
7275 else if (move_pattern == MV_TURNING_RANDOM)
7276 MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
7277 can_turn_right && !can_turn_left ? right_dir :
7278 RND(2) ? left_dir : right_dir);
7279 else if (can_turn_left && can_turn_right)
7280 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7281 else if (can_turn_left)
7282 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7283 else if (can_turn_right)
7284 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7286 MovDir[x][y] = back_dir;
7288 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7290 else if (move_pattern == MV_HORIZONTAL ||
7291 move_pattern == MV_VERTICAL)
7293 if (move_pattern & old_move_dir)
7294 MovDir[x][y] = back_dir;
7295 else if (move_pattern == MV_HORIZONTAL)
7296 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
7297 else if (move_pattern == MV_VERTICAL)
7298 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
7300 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7302 else if (move_pattern & MV_ANY_DIRECTION)
7304 MovDir[x][y] = move_pattern;
7305 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7307 else if (move_pattern & MV_WIND_DIRECTION)
7309 MovDir[x][y] = game.wind_direction;
7310 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7312 else if (move_pattern == MV_ALONG_LEFT_SIDE)
7314 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
7315 MovDir[x][y] = left_dir;
7316 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7317 MovDir[x][y] = right_dir;
7319 if (MovDir[x][y] != old_move_dir)
7320 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7322 else if (move_pattern == MV_ALONG_RIGHT_SIDE)
7324 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
7325 MovDir[x][y] = right_dir;
7326 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7327 MovDir[x][y] = left_dir;
7329 if (MovDir[x][y] != old_move_dir)
7330 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7332 else if (move_pattern == MV_TOWARDS_PLAYER ||
7333 move_pattern == MV_AWAY_FROM_PLAYER)
7335 int attr_x = -1, attr_y = -1;
7337 boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
7339 if (game.all_players_gone)
7341 attr_x = game.exit_x;
7342 attr_y = game.exit_y;
7348 for (i = 0; i < MAX_PLAYERS; i++)
7350 struct PlayerInfo *player = &stored_player[i];
7351 int jx = player->jx, jy = player->jy;
7353 if (!player->active)
7357 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7365 MovDir[x][y] = MV_NONE;
7367 MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
7368 else if (attr_x > x)
7369 MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
7371 MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
7372 else if (attr_y > y)
7373 MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
7375 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7377 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7379 boolean first_horiz = RND(2);
7380 int new_move_dir = MovDir[x][y];
7382 if (element_info[element].move_stepsize == 0) // "not moving"
7384 first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
7385 MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7391 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7392 Moving2Blocked(x, y, &newx, &newy);
7394 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7398 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7399 Moving2Blocked(x, y, &newx, &newy);
7401 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7404 MovDir[x][y] = old_move_dir;
7407 else if (move_pattern == MV_WHEN_PUSHED ||
7408 move_pattern == MV_WHEN_DROPPED)
7410 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7411 MovDir[x][y] = MV_NONE;
7415 else if (move_pattern & MV_MAZE_RUNNER_STYLE)
7417 static int test_xy[7][2] =
7427 static int test_dir[7] =
7437 boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
7438 int move_preference = -1000000; // start with very low preference
7439 int new_move_dir = MV_NONE;
7440 int start_test = RND(4);
7443 for (i = 0; i < NUM_DIRECTIONS; i++)
7445 int move_dir = test_dir[start_test + i];
7446 int move_dir_preference;
7448 xx = x + test_xy[start_test + i][0];
7449 yy = y + test_xy[start_test + i][1];
7451 if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
7452 (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
7454 new_move_dir = move_dir;
7459 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
7462 move_dir_preference = -1 * RunnerVisit[xx][yy];
7463 if (hunter_mode && PlayerVisit[xx][yy] > 0)
7464 move_dir_preference = PlayerVisit[xx][yy];
7466 if (move_dir_preference > move_preference)
7468 // prefer field that has not been visited for the longest time
7469 move_preference = move_dir_preference;
7470 new_move_dir = move_dir;
7472 else if (move_dir_preference == move_preference &&
7473 move_dir == old_move_dir)
7475 // prefer last direction when all directions are preferred equally
7476 move_preference = move_dir_preference;
7477 new_move_dir = move_dir;
7481 MovDir[x][y] = new_move_dir;
7482 if (old_move_dir != new_move_dir)
7483 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7487 static void TurnRound(int x, int y)
7489 int direction = MovDir[x][y];
7493 GfxDir[x][y] = MovDir[x][y];
7495 if (direction != MovDir[x][y])
7499 GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
7501 ResetGfxFrame(x, y);
7504 static boolean JustBeingPushed(int x, int y)
7508 for (i = 0; i < MAX_PLAYERS; i++)
7510 struct PlayerInfo *player = &stored_player[i];
7512 if (player->active && player->is_pushing && player->MovPos)
7514 int next_jx = player->jx + (player->jx - player->last_jx);
7515 int next_jy = player->jy + (player->jy - player->last_jy);
7517 if (x == next_jx && y == next_jy)
7525 static void StartMoving(int x, int y)
7527 boolean started_moving = FALSE; // some elements can fall _and_ move
7528 int element = Feld[x][y];
7533 if (MovDelay[x][y] == 0)
7534 GfxAction[x][y] = ACTION_DEFAULT;
7536 if (CAN_FALL(element) && y < lev_fieldy - 1)
7538 if ((x > 0 && IS_PLAYER(x - 1, y)) ||
7539 (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
7540 if (JustBeingPushed(x, y))
7543 if (element == EL_QUICKSAND_FULL)
7545 if (IS_FREE(x, y + 1))
7547 InitMovingField(x, y, MV_DOWN);
7548 started_moving = TRUE;
7550 Feld[x][y] = EL_QUICKSAND_EMPTYING;
7551 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7552 if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7553 Store[x][y] = EL_ROCK;
7555 Store[x][y] = EL_ROCK;
7558 PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7560 else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7562 if (!MovDelay[x][y])
7564 MovDelay[x][y] = TILEY + 1;
7566 ResetGfxAnimation(x, y);
7567 ResetGfxAnimation(x, y + 1);
7572 DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7573 DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7580 Feld[x][y] = EL_QUICKSAND_EMPTY;
7581 Feld[x][y + 1] = EL_QUICKSAND_FULL;
7582 Store[x][y + 1] = Store[x][y];
7585 PlayLevelSoundAction(x, y, ACTION_FILLING);
7587 else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7589 if (!MovDelay[x][y])
7591 MovDelay[x][y] = TILEY + 1;
7593 ResetGfxAnimation(x, y);
7594 ResetGfxAnimation(x, y + 1);
7599 DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7600 DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7607 Feld[x][y] = EL_QUICKSAND_EMPTY;
7608 Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7609 Store[x][y + 1] = Store[x][y];
7612 PlayLevelSoundAction(x, y, ACTION_FILLING);
7615 else if (element == EL_QUICKSAND_FAST_FULL)
7617 if (IS_FREE(x, y + 1))
7619 InitMovingField(x, y, MV_DOWN);
7620 started_moving = TRUE;
7622 Feld[x][y] = EL_QUICKSAND_FAST_EMPTYING;
7623 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7624 if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7625 Store[x][y] = EL_ROCK;
7627 Store[x][y] = EL_ROCK;
7630 PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7632 else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7634 if (!MovDelay[x][y])
7636 MovDelay[x][y] = TILEY + 1;
7638 ResetGfxAnimation(x, y);
7639 ResetGfxAnimation(x, y + 1);
7644 DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7645 DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7652 Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
7653 Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7654 Store[x][y + 1] = Store[x][y];
7657 PlayLevelSoundAction(x, y, ACTION_FILLING);
7659 else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7661 if (!MovDelay[x][y])
7663 MovDelay[x][y] = TILEY + 1;
7665 ResetGfxAnimation(x, y);
7666 ResetGfxAnimation(x, y + 1);
7671 DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7672 DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7679 Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
7680 Feld[x][y + 1] = EL_QUICKSAND_FULL;
7681 Store[x][y + 1] = Store[x][y];
7684 PlayLevelSoundAction(x, y, ACTION_FILLING);
7687 else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7688 Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7690 InitMovingField(x, y, MV_DOWN);
7691 started_moving = TRUE;
7693 Feld[x][y] = EL_QUICKSAND_FILLING;
7694 Store[x][y] = element;
7696 PlayLevelSoundAction(x, y, ACTION_FILLING);
7698 else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7699 Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7701 InitMovingField(x, y, MV_DOWN);
7702 started_moving = TRUE;
7704 Feld[x][y] = EL_QUICKSAND_FAST_FILLING;
7705 Store[x][y] = element;
7707 PlayLevelSoundAction(x, y, ACTION_FILLING);
7709 else if (element == EL_MAGIC_WALL_FULL)
7711 if (IS_FREE(x, y + 1))
7713 InitMovingField(x, y, MV_DOWN);
7714 started_moving = TRUE;
7716 Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
7717 Store[x][y] = EL_CHANGED(Store[x][y]);
7719 else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
7721 if (!MovDelay[x][y])
7722 MovDelay[x][y] = TILEY / 4 + 1;
7731 Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
7732 Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
7733 Store[x][y + 1] = EL_CHANGED(Store[x][y]);
7737 else if (element == EL_BD_MAGIC_WALL_FULL)
7739 if (IS_FREE(x, y + 1))
7741 InitMovingField(x, y, MV_DOWN);
7742 started_moving = TRUE;
7744 Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
7745 Store[x][y] = EL_CHANGED_BD(Store[x][y]);
7747 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
7749 if (!MovDelay[x][y])
7750 MovDelay[x][y] = TILEY / 4 + 1;
7759 Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
7760 Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
7761 Store[x][y + 1] = EL_CHANGED_BD(Store[x][y]);
7765 else if (element == EL_DC_MAGIC_WALL_FULL)
7767 if (IS_FREE(x, y + 1))
7769 InitMovingField(x, y, MV_DOWN);
7770 started_moving = TRUE;
7772 Feld[x][y] = EL_DC_MAGIC_WALL_EMPTYING;
7773 Store[x][y] = EL_CHANGED_DC(Store[x][y]);
7775 else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
7777 if (!MovDelay[x][y])
7778 MovDelay[x][y] = TILEY / 4 + 1;
7787 Feld[x][y] = EL_DC_MAGIC_WALL_ACTIVE;
7788 Feld[x][y + 1] = EL_DC_MAGIC_WALL_FULL;
7789 Store[x][y + 1] = EL_CHANGED_DC(Store[x][y]);
7793 else if ((CAN_PASS_MAGIC_WALL(element) &&
7794 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
7795 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)) ||
7796 (CAN_PASS_DC_MAGIC_WALL(element) &&
7797 (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)))
7800 InitMovingField(x, y, MV_DOWN);
7801 started_moving = TRUE;
7804 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
7805 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ? EL_BD_MAGIC_WALL_FILLING :
7806 EL_DC_MAGIC_WALL_FILLING);
7807 Store[x][y] = element;
7809 else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
7811 SplashAcid(x, y + 1);
7813 InitMovingField(x, y, MV_DOWN);
7814 started_moving = TRUE;
7816 Store[x][y] = EL_ACID;
7819 (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7820 CheckImpact[x][y] && !IS_FREE(x, y + 1)) ||
7821 (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
7822 CAN_FALL(element) && WasJustFalling[x][y] &&
7823 (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
7825 (game.engine_version < VERSION_IDENT(2,2,0,7) &&
7826 CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
7827 (Feld[x][y + 1] == EL_BLOCKED)))
7829 /* this is needed for a special case not covered by calling "Impact()"
7830 from "ContinueMoving()": if an element moves to a tile directly below
7831 another element which was just falling on that tile (which was empty
7832 in the previous frame), the falling element above would just stop
7833 instead of smashing the element below (in previous version, the above
7834 element was just checked for "moving" instead of "falling", resulting
7835 in incorrect smashes caused by horizontal movement of the above
7836 element; also, the case of the player being the element to smash was
7837 simply not covered here... :-/ ) */
7839 CheckCollision[x][y] = 0;
7840 CheckImpact[x][y] = 0;
7844 else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
7846 if (MovDir[x][y] == MV_NONE)
7848 InitMovingField(x, y, MV_DOWN);
7849 started_moving = TRUE;
7852 else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
7854 if (WasJustFalling[x][y]) // prevent animation from being restarted
7855 MovDir[x][y] = MV_DOWN;
7857 InitMovingField(x, y, MV_DOWN);
7858 started_moving = TRUE;
7860 else if (element == EL_AMOEBA_DROP)
7862 Feld[x][y] = EL_AMOEBA_GROWING;
7863 Store[x][y] = EL_AMOEBA_WET;
7865 else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
7866 (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
7867 !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
7868 element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
7870 boolean can_fall_left = (x > 0 && IS_FREE(x - 1, y) &&
7871 (IS_FREE(x - 1, y + 1) ||
7872 Feld[x - 1][y + 1] == EL_ACID));
7873 boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
7874 (IS_FREE(x + 1, y + 1) ||
7875 Feld[x + 1][y + 1] == EL_ACID));
7876 boolean can_fall_any = (can_fall_left || can_fall_right);
7877 boolean can_fall_both = (can_fall_left && can_fall_right);
7878 int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
7880 if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
7882 if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
7883 can_fall_right = FALSE;
7884 else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
7885 can_fall_left = FALSE;
7886 else if (slippery_type == SLIPPERY_ONLY_LEFT)
7887 can_fall_right = FALSE;
7888 else if (slippery_type == SLIPPERY_ONLY_RIGHT)
7889 can_fall_left = FALSE;
7891 can_fall_any = (can_fall_left || can_fall_right);
7892 can_fall_both = FALSE;
7897 if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
7898 can_fall_right = FALSE; // slip down on left side
7900 can_fall_left = !(can_fall_right = RND(2));
7902 can_fall_both = FALSE;
7907 // if not determined otherwise, prefer left side for slipping down
7908 InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
7909 started_moving = TRUE;
7912 else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
7914 boolean left_is_free = (x > 0 && IS_FREE(x - 1, y));
7915 boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
7916 int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
7917 int belt_dir = game.belt_dir[belt_nr];
7919 if ((belt_dir == MV_LEFT && left_is_free) ||
7920 (belt_dir == MV_RIGHT && right_is_free))
7922 int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
7924 InitMovingField(x, y, belt_dir);
7925 started_moving = TRUE;
7927 Pushed[x][y] = TRUE;
7928 Pushed[nextx][y] = TRUE;
7930 GfxAction[x][y] = ACTION_DEFAULT;
7934 MovDir[x][y] = 0; // if element was moving, stop it
7939 // not "else if" because of elements that can fall and move (EL_SPRING)
7940 if (CAN_MOVE(element) && !started_moving)
7942 int move_pattern = element_info[element].move_pattern;
7945 Moving2Blocked(x, y, &newx, &newy);
7947 if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
7950 if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7951 CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7953 WasJustMoving[x][y] = 0;
7954 CheckCollision[x][y] = 0;
7956 TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
7958 if (Feld[x][y] != element) // element has changed
7962 if (!MovDelay[x][y]) // start new movement phase
7964 // all objects that can change their move direction after each step
7965 // (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall
7967 if (element != EL_YAMYAM &&
7968 element != EL_DARK_YAMYAM &&
7969 element != EL_PACMAN &&
7970 !(move_pattern & MV_ANY_DIRECTION) &&
7971 move_pattern != MV_TURNING_LEFT &&
7972 move_pattern != MV_TURNING_RIGHT &&
7973 move_pattern != MV_TURNING_LEFT_RIGHT &&
7974 move_pattern != MV_TURNING_RIGHT_LEFT &&
7975 move_pattern != MV_TURNING_RANDOM)
7979 if (MovDelay[x][y] && (element == EL_BUG ||
7980 element == EL_SPACESHIP ||
7981 element == EL_SP_SNIKSNAK ||
7982 element == EL_SP_ELECTRON ||
7983 element == EL_MOLE))
7984 TEST_DrawLevelField(x, y);
7988 if (MovDelay[x][y]) // wait some time before next movement
7992 if (element == EL_ROBOT ||
7993 element == EL_YAMYAM ||
7994 element == EL_DARK_YAMYAM)
7996 DrawLevelElementAnimationIfNeeded(x, y, element);
7997 PlayLevelSoundAction(x, y, ACTION_WAITING);
7999 else if (element == EL_SP_ELECTRON)
8000 DrawLevelElementAnimationIfNeeded(x, y, element);
8001 else if (element == EL_DRAGON)
8004 int dir = MovDir[x][y];
8005 int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
8006 int dy = (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
8007 int graphic = (dir == MV_LEFT ? IMG_FLAMES_1_LEFT :
8008 dir == MV_RIGHT ? IMG_FLAMES_1_RIGHT :
8009 dir == MV_UP ? IMG_FLAMES_1_UP :
8010 dir == MV_DOWN ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
8011 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
8013 GfxAction[x][y] = ACTION_ATTACKING;
8015 if (IS_PLAYER(x, y))
8016 DrawPlayerField(x, y);
8018 TEST_DrawLevelField(x, y);
8020 PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
8022 for (i = 1; i <= 3; i++)
8024 int xx = x + i * dx;
8025 int yy = y + i * dy;
8026 int sx = SCREENX(xx);
8027 int sy = SCREENY(yy);
8028 int flame_graphic = graphic + (i - 1);
8030 if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
8035 int flamed = MovingOrBlocked2Element(xx, yy);
8037 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
8040 RemoveMovingField(xx, yy);
8042 ChangeDelay[xx][yy] = 0;
8044 Feld[xx][yy] = EL_FLAMES;
8046 if (IN_SCR_FIELD(sx, sy))
8048 TEST_DrawLevelFieldCrumbled(xx, yy);
8049 DrawGraphic(sx, sy, flame_graphic, frame);
8054 if (Feld[xx][yy] == EL_FLAMES)
8055 Feld[xx][yy] = EL_EMPTY;
8056 TEST_DrawLevelField(xx, yy);
8061 if (MovDelay[x][y]) // element still has to wait some time
8063 PlayLevelSoundAction(x, y, ACTION_WAITING);
8069 // now make next step
8071 Moving2Blocked(x, y, &newx, &newy); // get next screen position
8073 if (DONT_COLLIDE_WITH(element) &&
8074 IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
8075 !PLAYER_ENEMY_PROTECTED(newx, newy))
8077 TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
8082 else if (CAN_MOVE_INTO_ACID(element) &&
8083 IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID &&
8084 !IS_MV_DIAGONAL(MovDir[x][y]) &&
8085 (MovDir[x][y] == MV_DOWN ||
8086 game.engine_version >= VERSION_IDENT(3,1,0,0)))
8088 SplashAcid(newx, newy);
8089 Store[x][y] = EL_ACID;
8091 else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
8093 if (Feld[newx][newy] == EL_EXIT_OPEN ||
8094 Feld[newx][newy] == EL_EM_EXIT_OPEN ||
8095 Feld[newx][newy] == EL_STEEL_EXIT_OPEN ||
8096 Feld[newx][newy] == EL_EM_STEEL_EXIT_OPEN)
8099 TEST_DrawLevelField(x, y);
8101 PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
8102 if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
8103 DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
8105 game.friends_still_needed--;
8106 if (!game.friends_still_needed &&
8108 game.all_players_gone)
8113 else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
8115 if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MP_MOVING)
8116 TEST_DrawLevelField(newx, newy);
8118 GfxDir[x][y] = MovDir[x][y] = MV_NONE;
8120 else if (!IS_FREE(newx, newy))
8122 GfxAction[x][y] = ACTION_WAITING;
8124 if (IS_PLAYER(x, y))
8125 DrawPlayerField(x, y);
8127 TEST_DrawLevelField(x, y);
8132 else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
8134 if (IS_FOOD_PIG(Feld[newx][newy]))
8136 if (IS_MOVING(newx, newy))
8137 RemoveMovingField(newx, newy);
8140 Feld[newx][newy] = EL_EMPTY;
8141 TEST_DrawLevelField(newx, newy);
8144 PlayLevelSound(x, y, SND_PIG_DIGGING);
8146 else if (!IS_FREE(newx, newy))
8148 if (IS_PLAYER(x, y))
8149 DrawPlayerField(x, y);
8151 TEST_DrawLevelField(x, y);
8156 else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
8158 if (Store[x][y] != EL_EMPTY)
8160 boolean can_clone = FALSE;
8163 // check if element to clone is still there
8164 for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
8166 if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == Store[x][y])
8174 // cannot clone or target field not free anymore -- do not clone
8175 if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8176 Store[x][y] = EL_EMPTY;
8179 if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8181 if (IS_MV_DIAGONAL(MovDir[x][y]))
8183 int diagonal_move_dir = MovDir[x][y];
8184 int stored = Store[x][y];
8185 int change_delay = 8;
8188 // android is moving diagonally
8190 CreateField(x, y, EL_DIAGONAL_SHRINKING);
8192 Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
8193 GfxElement[x][y] = EL_EMC_ANDROID;
8194 GfxAction[x][y] = ACTION_SHRINKING;
8195 GfxDir[x][y] = diagonal_move_dir;
8196 ChangeDelay[x][y] = change_delay;
8198 graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
8201 DrawLevelGraphicAnimation(x, y, graphic);
8202 PlayLevelSoundAction(x, y, ACTION_SHRINKING);
8204 if (Feld[newx][newy] == EL_ACID)
8206 SplashAcid(newx, newy);
8211 CreateField(newx, newy, EL_DIAGONAL_GROWING);
8213 Store[newx][newy] = EL_EMC_ANDROID;
8214 GfxElement[newx][newy] = EL_EMC_ANDROID;
8215 GfxAction[newx][newy] = ACTION_GROWING;
8216 GfxDir[newx][newy] = diagonal_move_dir;
8217 ChangeDelay[newx][newy] = change_delay;
8219 graphic = el_act_dir2img(GfxElement[newx][newy],
8220 GfxAction[newx][newy], GfxDir[newx][newy]);
8222 DrawLevelGraphicAnimation(newx, newy, graphic);
8223 PlayLevelSoundAction(newx, newy, ACTION_GROWING);
8229 Feld[newx][newy] = EL_EMPTY;
8230 TEST_DrawLevelField(newx, newy);
8232 PlayLevelSoundAction(x, y, ACTION_DIGGING);
8235 else if (!IS_FREE(newx, newy))
8240 else if (IS_CUSTOM_ELEMENT(element) &&
8241 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
8243 if (!DigFieldByCE(newx, newy, element))
8246 if (move_pattern & MV_MAZE_RUNNER_STYLE)
8248 RunnerVisit[x][y] = FrameCounter;
8249 PlayerVisit[x][y] /= 8; // expire player visit path
8252 else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
8254 if (!IS_FREE(newx, newy))
8256 if (IS_PLAYER(x, y))
8257 DrawPlayerField(x, y);
8259 TEST_DrawLevelField(x, y);
8265 boolean wanna_flame = !RND(10);
8266 int dx = newx - x, dy = newy - y;
8267 int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
8268 int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
8269 int element1 = (IN_LEV_FIELD(newx1, newy1) ?
8270 MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
8271 int element2 = (IN_LEV_FIELD(newx2, newy2) ?
8272 MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
8275 IS_CLASSIC_ENEMY(element1) ||
8276 IS_CLASSIC_ENEMY(element2)) &&
8277 element1 != EL_DRAGON && element2 != EL_DRAGON &&
8278 element1 != EL_FLAMES && element2 != EL_FLAMES)
8280 ResetGfxAnimation(x, y);
8281 GfxAction[x][y] = ACTION_ATTACKING;
8283 if (IS_PLAYER(x, y))
8284 DrawPlayerField(x, y);
8286 TEST_DrawLevelField(x, y);
8288 PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
8290 MovDelay[x][y] = 50;
8292 Feld[newx][newy] = EL_FLAMES;
8293 if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
8294 Feld[newx1][newy1] = EL_FLAMES;
8295 if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
8296 Feld[newx2][newy2] = EL_FLAMES;
8302 else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8303 Feld[newx][newy] == EL_DIAMOND)
8305 if (IS_MOVING(newx, newy))
8306 RemoveMovingField(newx, newy);
8309 Feld[newx][newy] = EL_EMPTY;
8310 TEST_DrawLevelField(newx, newy);
8313 PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
8315 else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8316 IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
8318 if (AmoebaNr[newx][newy])
8320 AmoebaCnt2[AmoebaNr[newx][newy]]--;
8321 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
8322 Feld[newx][newy] == EL_BD_AMOEBA)
8323 AmoebaCnt[AmoebaNr[newx][newy]]--;
8326 if (IS_MOVING(newx, newy))
8328 RemoveMovingField(newx, newy);
8332 Feld[newx][newy] = EL_EMPTY;
8333 TEST_DrawLevelField(newx, newy);
8336 PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
8338 else if ((element == EL_PACMAN || element == EL_MOLE)
8339 && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
8341 if (AmoebaNr[newx][newy])
8343 AmoebaCnt2[AmoebaNr[newx][newy]]--;
8344 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
8345 Feld[newx][newy] == EL_BD_AMOEBA)
8346 AmoebaCnt[AmoebaNr[newx][newy]]--;
8349 if (element == EL_MOLE)
8351 Feld[newx][newy] = EL_AMOEBA_SHRINKING;
8352 PlayLevelSound(x, y, SND_MOLE_DIGGING);
8354 ResetGfxAnimation(x, y);
8355 GfxAction[x][y] = ACTION_DIGGING;
8356 TEST_DrawLevelField(x, y);
8358 MovDelay[newx][newy] = 0; // start amoeba shrinking delay
8360 return; // wait for shrinking amoeba
8362 else // element == EL_PACMAN
8364 Feld[newx][newy] = EL_EMPTY;
8365 TEST_DrawLevelField(newx, newy);
8366 PlayLevelSound(x, y, SND_PACMAN_DIGGING);
8369 else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
8370 (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
8371 (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
8373 // wait for shrinking amoeba to completely disappear
8376 else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
8378 // object was running against a wall
8382 if (GFX_ELEMENT(element) != EL_SAND) // !!! FIX THIS (crumble) !!!
8383 DrawLevelElementAnimation(x, y, element);
8385 if (DONT_TOUCH(element))
8386 TestIfBadThingTouchesPlayer(x, y);
8391 InitMovingField(x, y, MovDir[x][y]);
8393 PlayLevelSoundAction(x, y, ACTION_MOVING);
8397 ContinueMoving(x, y);
8400 void ContinueMoving(int x, int y)
8402 int element = Feld[x][y];
8403 struct ElementInfo *ei = &element_info[element];
8404 int direction = MovDir[x][y];
8405 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
8406 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
8407 int newx = x + dx, newy = y + dy;
8408 int stored = Store[x][y];
8409 int stored_new = Store[newx][newy];
8410 boolean pushed_by_player = (Pushed[x][y] && IS_PLAYER(x, y));
8411 boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
8412 boolean last_line = (newy == lev_fieldy - 1);
8414 MovPos[x][y] += getElementMoveStepsize(x, y);
8416 if (pushed_by_player) // special case: moving object pushed by player
8417 MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
8419 if (ABS(MovPos[x][y]) < TILEX)
8421 TEST_DrawLevelField(x, y);
8423 return; // element is still moving
8426 // element reached destination field
8428 Feld[x][y] = EL_EMPTY;
8429 Feld[newx][newy] = element;
8430 MovPos[x][y] = 0; // force "not moving" for "crumbled sand"
8432 if (Store[x][y] == EL_ACID) // element is moving into acid pool
8434 element = Feld[newx][newy] = EL_ACID;
8436 else if (element == EL_MOLE)
8438 Feld[x][y] = EL_SAND;
8440 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8442 else if (element == EL_QUICKSAND_FILLING)
8444 element = Feld[newx][newy] = get_next_element(element);
8445 Store[newx][newy] = Store[x][y];
8447 else if (element == EL_QUICKSAND_EMPTYING)
8449 Feld[x][y] = get_next_element(element);
8450 element = Feld[newx][newy] = Store[x][y];
8452 else if (element == EL_QUICKSAND_FAST_FILLING)
8454 element = Feld[newx][newy] = get_next_element(element);
8455 Store[newx][newy] = Store[x][y];
8457 else if (element == EL_QUICKSAND_FAST_EMPTYING)
8459 Feld[x][y] = get_next_element(element);
8460 element = Feld[newx][newy] = Store[x][y];
8462 else if (element == EL_MAGIC_WALL_FILLING)
8464 element = Feld[newx][newy] = get_next_element(element);
8465 if (!game.magic_wall_active)
8466 element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
8467 Store[newx][newy] = Store[x][y];
8469 else if (element == EL_MAGIC_WALL_EMPTYING)
8471 Feld[x][y] = get_next_element(element);
8472 if (!game.magic_wall_active)
8473 Feld[x][y] = EL_MAGIC_WALL_DEAD;
8474 element = Feld[newx][newy] = Store[x][y];
8476 InitField(newx, newy, FALSE);
8478 else if (element == EL_BD_MAGIC_WALL_FILLING)
8480 element = Feld[newx][newy] = get_next_element(element);
8481 if (!game.magic_wall_active)
8482 element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
8483 Store[newx][newy] = Store[x][y];
8485 else if (element == EL_BD_MAGIC_WALL_EMPTYING)
8487 Feld[x][y] = get_next_element(element);
8488 if (!game.magic_wall_active)
8489 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
8490 element = Feld[newx][newy] = Store[x][y];
8492 InitField(newx, newy, FALSE);
8494 else if (element == EL_DC_MAGIC_WALL_FILLING)
8496 element = Feld[newx][newy] = get_next_element(element);
8497 if (!game.magic_wall_active)
8498 element = Feld[newx][newy] = EL_DC_MAGIC_WALL_DEAD;
8499 Store[newx][newy] = Store[x][y];
8501 else if (element == EL_DC_MAGIC_WALL_EMPTYING)
8503 Feld[x][y] = get_next_element(element);
8504 if (!game.magic_wall_active)
8505 Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
8506 element = Feld[newx][newy] = Store[x][y];
8508 InitField(newx, newy, FALSE);
8510 else if (element == EL_AMOEBA_DROPPING)
8512 Feld[x][y] = get_next_element(element);
8513 element = Feld[newx][newy] = Store[x][y];
8515 else if (element == EL_SOKOBAN_OBJECT)
8518 Feld[x][y] = Back[x][y];
8520 if (Back[newx][newy])
8521 Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
8523 Back[x][y] = Back[newx][newy] = 0;
8526 Store[x][y] = EL_EMPTY;
8531 MovDelay[newx][newy] = 0;
8533 if (CAN_CHANGE_OR_HAS_ACTION(element))
8535 // copy element change control values to new field
8536 ChangeDelay[newx][newy] = ChangeDelay[x][y];
8537 ChangePage[newx][newy] = ChangePage[x][y];
8538 ChangeCount[newx][newy] = ChangeCount[x][y];
8539 ChangeEvent[newx][newy] = ChangeEvent[x][y];
8542 CustomValue[newx][newy] = CustomValue[x][y];
8544 ChangeDelay[x][y] = 0;
8545 ChangePage[x][y] = -1;
8546 ChangeCount[x][y] = 0;
8547 ChangeEvent[x][y] = -1;
8549 CustomValue[x][y] = 0;
8551 // copy animation control values to new field
8552 GfxFrame[newx][newy] = GfxFrame[x][y];
8553 GfxRandom[newx][newy] = GfxRandom[x][y]; // keep same random value
8554 GfxAction[newx][newy] = GfxAction[x][y]; // keep action one frame
8555 GfxDir[newx][newy] = GfxDir[x][y]; // keep element direction
8557 Pushed[x][y] = Pushed[newx][newy] = FALSE;
8559 // some elements can leave other elements behind after moving
8560 if (ei->move_leave_element != EL_EMPTY &&
8561 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
8562 (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
8564 int move_leave_element = ei->move_leave_element;
8566 // this makes it possible to leave the removed element again
8567 if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
8568 move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
8570 Feld[x][y] = move_leave_element;
8572 if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
8573 MovDir[x][y] = direction;
8575 InitField(x, y, FALSE);
8577 if (GFX_CRUMBLED(Feld[x][y]))
8578 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8580 if (ELEM_IS_PLAYER(move_leave_element))
8581 RelocatePlayer(x, y, move_leave_element);
8584 // do this after checking for left-behind element
8585 ResetGfxAnimation(x, y); // reset animation values for old field
8587 if (!CAN_MOVE(element) ||
8588 (CAN_FALL(element) && direction == MV_DOWN &&
8589 (element == EL_SPRING ||
8590 element_info[element].move_pattern == MV_WHEN_PUSHED ||
8591 element_info[element].move_pattern == MV_WHEN_DROPPED)))
8592 GfxDir[x][y] = MovDir[newx][newy] = 0;
8594 TEST_DrawLevelField(x, y);
8595 TEST_DrawLevelField(newx, newy);
8597 Stop[newx][newy] = TRUE; // ignore this element until the next frame
8599 // prevent pushed element from moving on in pushed direction
8600 if (pushed_by_player && CAN_MOVE(element) &&
8601 element_info[element].move_pattern & MV_ANY_DIRECTION &&
8602 !(element_info[element].move_pattern & direction))
8603 TurnRound(newx, newy);
8605 // prevent elements on conveyor belt from moving on in last direction
8606 if (pushed_by_conveyor && CAN_FALL(element) &&
8607 direction & MV_HORIZONTAL)
8608 MovDir[newx][newy] = 0;
8610 if (!pushed_by_player)
8612 int nextx = newx + dx, nexty = newy + dy;
8613 boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
8615 WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
8617 if (CAN_FALL(element) && direction == MV_DOWN)
8618 WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
8620 if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
8621 CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
8623 if (CAN_FALL(element) && direction == MV_DOWN && check_collision_again)
8624 CheckImpact[newx][newy] = CHECK_DELAY_IMPACT;
8627 if (DONT_TOUCH(element)) // object may be nasty to player or others
8629 TestIfBadThingTouchesPlayer(newx, newy);
8630 TestIfBadThingTouchesFriend(newx, newy);
8632 if (!IS_CUSTOM_ELEMENT(element))
8633 TestIfBadThingTouchesOtherBadThing(newx, newy);
8635 else if (element == EL_PENGUIN)
8636 TestIfFriendTouchesBadThing(newx, newy);
8638 if (DONT_GET_HIT_BY(element))
8640 TestIfGoodThingGetsHitByBadThing(newx, newy, direction);
8643 // give the player one last chance (one more frame) to move away
8644 if (CAN_FALL(element) && direction == MV_DOWN &&
8645 (last_line || (!IS_FREE(x, newy + 1) &&
8646 (!IS_PLAYER(x, newy + 1) ||
8647 game.engine_version < VERSION_IDENT(3,1,1,0)))))
8650 if (pushed_by_player && !game.use_change_when_pushing_bug)
8652 int push_side = MV_DIR_OPPOSITE(direction);
8653 struct PlayerInfo *player = PLAYERINFO(x, y);
8655 CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
8656 player->index_bit, push_side);
8657 CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
8658 player->index_bit, push_side);
8661 if (element == EL_EMC_ANDROID && pushed_by_player) // make another move
8662 MovDelay[newx][newy] = 1;
8664 CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
8666 TestIfElementTouchesCustomElement(x, y); // empty or new element
8667 TestIfElementHitsCustomElement(newx, newy, direction);
8668 TestIfPlayerTouchesCustomElement(newx, newy);
8669 TestIfElementTouchesCustomElement(newx, newy);
8671 if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
8672 IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
8673 CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
8674 MV_DIR_OPPOSITE(direction));
8677 int AmoebeNachbarNr(int ax, int ay)
8680 int element = Feld[ax][ay];
8682 static int xy[4][2] =
8690 for (i = 0; i < NUM_DIRECTIONS; i++)
8692 int x = ax + xy[i][0];
8693 int y = ay + xy[i][1];
8695 if (!IN_LEV_FIELD(x, y))
8698 if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
8699 group_nr = AmoebaNr[x][y];
8705 static void AmoebenVereinigen(int ax, int ay)
8707 int i, x, y, xx, yy;
8708 int new_group_nr = AmoebaNr[ax][ay];
8709 static int xy[4][2] =
8717 if (new_group_nr == 0)
8720 for (i = 0; i < NUM_DIRECTIONS; i++)
8725 if (!IN_LEV_FIELD(x, y))
8728 if ((Feld[x][y] == EL_AMOEBA_FULL ||
8729 Feld[x][y] == EL_BD_AMOEBA ||
8730 Feld[x][y] == EL_AMOEBA_DEAD) &&
8731 AmoebaNr[x][y] != new_group_nr)
8733 int old_group_nr = AmoebaNr[x][y];
8735 if (old_group_nr == 0)
8738 AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
8739 AmoebaCnt[old_group_nr] = 0;
8740 AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
8741 AmoebaCnt2[old_group_nr] = 0;
8743 SCAN_PLAYFIELD(xx, yy)
8745 if (AmoebaNr[xx][yy] == old_group_nr)
8746 AmoebaNr[xx][yy] = new_group_nr;
8752 void AmoebeUmwandeln(int ax, int ay)
8756 if (Feld[ax][ay] == EL_AMOEBA_DEAD)
8758 int group_nr = AmoebaNr[ax][ay];
8763 printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
8764 printf("AmoebeUmwandeln(): This should never happen!\n");
8769 SCAN_PLAYFIELD(x, y)
8771 if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
8774 Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
8778 PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
8779 SND_AMOEBA_TURNING_TO_GEM :
8780 SND_AMOEBA_TURNING_TO_ROCK));
8785 static int xy[4][2] =
8793 for (i = 0; i < NUM_DIRECTIONS; i++)
8798 if (!IN_LEV_FIELD(x, y))
8801 if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
8803 PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
8804 SND_AMOEBA_TURNING_TO_GEM :
8805 SND_AMOEBA_TURNING_TO_ROCK));
8812 static void AmoebeUmwandelnBD(int ax, int ay, int new_element)
8815 int group_nr = AmoebaNr[ax][ay];
8816 boolean done = FALSE;
8821 printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
8822 printf("AmoebeUmwandelnBD(): This should never happen!\n");
8827 SCAN_PLAYFIELD(x, y)
8829 if (AmoebaNr[x][y] == group_nr &&
8830 (Feld[x][y] == EL_AMOEBA_DEAD ||
8831 Feld[x][y] == EL_BD_AMOEBA ||
8832 Feld[x][y] == EL_AMOEBA_GROWING))
8835 Feld[x][y] = new_element;
8836 InitField(x, y, FALSE);
8837 TEST_DrawLevelField(x, y);
8843 PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
8844 SND_BD_AMOEBA_TURNING_TO_ROCK :
8845 SND_BD_AMOEBA_TURNING_TO_GEM));
8848 static void AmoebeWaechst(int x, int y)
8850 static unsigned int sound_delay = 0;
8851 static unsigned int sound_delay_value = 0;
8853 if (!MovDelay[x][y]) // start new growing cycle
8857 if (DelayReached(&sound_delay, sound_delay_value))
8859 PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
8860 sound_delay_value = 30;
8864 if (MovDelay[x][y]) // wait some time before growing bigger
8867 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8869 int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
8870 6 - MovDelay[x][y]);
8872 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
8875 if (!MovDelay[x][y])
8877 Feld[x][y] = Store[x][y];
8879 TEST_DrawLevelField(x, y);
8884 static void AmoebaDisappearing(int x, int y)
8886 static unsigned int sound_delay = 0;
8887 static unsigned int sound_delay_value = 0;
8889 if (!MovDelay[x][y]) // start new shrinking cycle
8893 if (DelayReached(&sound_delay, sound_delay_value))
8894 sound_delay_value = 30;
8897 if (MovDelay[x][y]) // wait some time before shrinking
8900 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8902 int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
8903 6 - MovDelay[x][y]);
8905 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
8908 if (!MovDelay[x][y])
8910 Feld[x][y] = EL_EMPTY;
8911 TEST_DrawLevelField(x, y);
8913 // don't let mole enter this field in this cycle;
8914 // (give priority to objects falling to this field from above)
8920 static void AmoebeAbleger(int ax, int ay)
8923 int element = Feld[ax][ay];
8924 int graphic = el2img(element);
8925 int newax = ax, neway = ay;
8926 boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
8927 static int xy[4][2] =
8935 if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
8937 Feld[ax][ay] = EL_AMOEBA_DEAD;
8938 TEST_DrawLevelField(ax, ay);
8942 if (IS_ANIMATED(graphic))
8943 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
8945 if (!MovDelay[ax][ay]) // start making new amoeba field
8946 MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
8948 if (MovDelay[ax][ay]) // wait some time before making new amoeba
8951 if (MovDelay[ax][ay])
8955 if (can_drop) // EL_AMOEBA_WET or EL_EMC_DRIPPER
8958 int x = ax + xy[start][0];
8959 int y = ay + xy[start][1];
8961 if (!IN_LEV_FIELD(x, y))
8964 if (IS_FREE(x, y) ||
8965 CAN_GROW_INTO(Feld[x][y]) ||
8966 Feld[x][y] == EL_QUICKSAND_EMPTY ||
8967 Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
8973 if (newax == ax && neway == ay)
8976 else // normal or "filled" (BD style) amoeba
8979 boolean waiting_for_player = FALSE;
8981 for (i = 0; i < NUM_DIRECTIONS; i++)
8983 int j = (start + i) % 4;
8984 int x = ax + xy[j][0];
8985 int y = ay + xy[j][1];
8987 if (!IN_LEV_FIELD(x, y))
8990 if (IS_FREE(x, y) ||
8991 CAN_GROW_INTO(Feld[x][y]) ||
8992 Feld[x][y] == EL_QUICKSAND_EMPTY ||
8993 Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
8999 else if (IS_PLAYER(x, y))
9000 waiting_for_player = TRUE;
9003 if (newax == ax && neway == ay) // amoeba cannot grow
9005 if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
9007 Feld[ax][ay] = EL_AMOEBA_DEAD;
9008 TEST_DrawLevelField(ax, ay);
9009 AmoebaCnt[AmoebaNr[ax][ay]]--;
9011 if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0) // amoeba is completely dead
9013 if (element == EL_AMOEBA_FULL)
9014 AmoebeUmwandeln(ax, ay);
9015 else if (element == EL_BD_AMOEBA)
9016 AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
9021 else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
9023 // amoeba gets larger by growing in some direction
9025 int new_group_nr = AmoebaNr[ax][ay];
9028 if (new_group_nr == 0)
9030 printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
9031 printf("AmoebeAbleger(): This should never happen!\n");
9036 AmoebaNr[newax][neway] = new_group_nr;
9037 AmoebaCnt[new_group_nr]++;
9038 AmoebaCnt2[new_group_nr]++;
9040 // if amoeba touches other amoeba(s) after growing, unify them
9041 AmoebenVereinigen(newax, neway);
9043 if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
9045 AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
9051 if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
9052 (neway == lev_fieldy - 1 && newax != ax))
9054 Feld[newax][neway] = EL_AMOEBA_GROWING; // creation of new amoeba
9055 Store[newax][neway] = element;
9057 else if (neway == ay || element == EL_EMC_DRIPPER)
9059 Feld[newax][neway] = EL_AMOEBA_DROP; // drop left/right of amoeba
9061 PlayLevelSoundAction(newax, neway, ACTION_GROWING);
9065 InitMovingField(ax, ay, MV_DOWN); // drop dripping from amoeba
9066 Feld[ax][ay] = EL_AMOEBA_DROPPING;
9067 Store[ax][ay] = EL_AMOEBA_DROP;
9068 ContinueMoving(ax, ay);
9072 TEST_DrawLevelField(newax, neway);
9075 static void Life(int ax, int ay)
9079 int element = Feld[ax][ay];
9080 int graphic = el2img(element);
9081 int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
9083 boolean changed = FALSE;
9085 if (IS_ANIMATED(graphic))
9086 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9091 if (!MovDelay[ax][ay]) // start new "game of life" cycle
9092 MovDelay[ax][ay] = life_time;
9094 if (MovDelay[ax][ay]) // wait some time before next cycle
9097 if (MovDelay[ax][ay])
9101 for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
9103 int xx = ax+x1, yy = ay+y1;
9104 int old_element = Feld[xx][yy];
9105 int num_neighbours = 0;
9107 if (!IN_LEV_FIELD(xx, yy))
9110 for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
9112 int x = xx+x2, y = yy+y2;
9114 if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
9117 boolean is_player_cell = (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y));
9118 boolean is_neighbour = FALSE;
9120 if (level.use_life_bugs)
9122 (((Feld[x][y] == element || is_player_cell) && !Stop[x][y]) ||
9123 (IS_FREE(x, y) && Stop[x][y]));
9126 (Last[x][y] == element || is_player_cell);
9132 boolean is_free = FALSE;
9134 if (level.use_life_bugs)
9135 is_free = (IS_FREE(xx, yy));
9137 is_free = (IS_FREE(xx, yy) && Last[xx][yy] == EL_EMPTY);
9139 if (xx == ax && yy == ay) // field in the middle
9141 if (num_neighbours < life_parameter[0] ||
9142 num_neighbours > life_parameter[1])
9144 Feld[xx][yy] = EL_EMPTY;
9145 if (Feld[xx][yy] != old_element)
9146 TEST_DrawLevelField(xx, yy);
9147 Stop[xx][yy] = TRUE;
9151 else if (is_free || CAN_GROW_INTO(Feld[xx][yy]))
9152 { // free border field
9153 if (num_neighbours >= life_parameter[2] &&
9154 num_neighbours <= life_parameter[3])
9156 Feld[xx][yy] = element;
9157 MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
9158 if (Feld[xx][yy] != old_element)
9159 TEST_DrawLevelField(xx, yy);
9160 Stop[xx][yy] = TRUE;
9167 PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
9168 SND_GAME_OF_LIFE_GROWING);
9171 static void InitRobotWheel(int x, int y)
9173 ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
9176 static void RunRobotWheel(int x, int y)
9178 PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
9181 static void StopRobotWheel(int x, int y)
9183 if (game.robot_wheel_x == x &&
9184 game.robot_wheel_y == y)
9186 game.robot_wheel_x = -1;
9187 game.robot_wheel_y = -1;
9188 game.robot_wheel_active = FALSE;
9192 static void InitTimegateWheel(int x, int y)
9194 ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
9197 static void RunTimegateWheel(int x, int y)
9199 PlayLevelSound(x, y, SND_CLASS_TIMEGATE_SWITCH_ACTIVE);
9202 static void InitMagicBallDelay(int x, int y)
9204 ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
9207 static void ActivateMagicBall(int bx, int by)
9211 if (level.ball_random)
9213 int pos_border = RND(8); // select one of the eight border elements
9214 int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
9215 int xx = pos_content % 3;
9216 int yy = pos_content / 3;
9221 if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
9222 CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9226 for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
9228 int xx = x - bx + 1;
9229 int yy = y - by + 1;
9231 if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
9232 CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9236 game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
9239 static void CheckExit(int x, int y)
9241 if (game.gems_still_needed > 0 ||
9242 game.sokoban_fields_still_needed > 0 ||
9243 game.sokoban_objects_still_needed > 0 ||
9244 game.lights_still_needed > 0)
9246 int element = Feld[x][y];
9247 int graphic = el2img(element);
9249 if (IS_ANIMATED(graphic))
9250 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9255 // do not re-open exit door closed after last player
9256 if (game.all_players_gone)
9259 Feld[x][y] = EL_EXIT_OPENING;
9261 PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
9264 static void CheckExitEM(int x, int y)
9266 if (game.gems_still_needed > 0 ||
9267 game.sokoban_fields_still_needed > 0 ||
9268 game.sokoban_objects_still_needed > 0 ||
9269 game.lights_still_needed > 0)
9271 int element = Feld[x][y];
9272 int graphic = el2img(element);
9274 if (IS_ANIMATED(graphic))
9275 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9280 // do not re-open exit door closed after last player
9281 if (game.all_players_gone)
9284 Feld[x][y] = EL_EM_EXIT_OPENING;
9286 PlayLevelSoundNearest(x, y, SND_CLASS_EM_EXIT_OPENING);
9289 static void CheckExitSteel(int x, int y)
9291 if (game.gems_still_needed > 0 ||
9292 game.sokoban_fields_still_needed > 0 ||
9293 game.sokoban_objects_still_needed > 0 ||
9294 game.lights_still_needed > 0)
9296 int element = Feld[x][y];
9297 int graphic = el2img(element);
9299 if (IS_ANIMATED(graphic))
9300 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9305 // do not re-open exit door closed after last player
9306 if (game.all_players_gone)
9309 Feld[x][y] = EL_STEEL_EXIT_OPENING;
9311 PlayLevelSoundNearest(x, y, SND_CLASS_STEEL_EXIT_OPENING);
9314 static void CheckExitSteelEM(int x, int y)
9316 if (game.gems_still_needed > 0 ||
9317 game.sokoban_fields_still_needed > 0 ||
9318 game.sokoban_objects_still_needed > 0 ||
9319 game.lights_still_needed > 0)
9321 int element = Feld[x][y];
9322 int graphic = el2img(element);
9324 if (IS_ANIMATED(graphic))
9325 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9330 // do not re-open exit door closed after last player
9331 if (game.all_players_gone)
9334 Feld[x][y] = EL_EM_STEEL_EXIT_OPENING;
9336 PlayLevelSoundNearest(x, y, SND_CLASS_EM_STEEL_EXIT_OPENING);
9339 static void CheckExitSP(int x, int y)
9341 if (game.gems_still_needed > 0)
9343 int element = Feld[x][y];
9344 int graphic = el2img(element);
9346 if (IS_ANIMATED(graphic))
9347 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9352 // do not re-open exit door closed after last player
9353 if (game.all_players_gone)
9356 Feld[x][y] = EL_SP_EXIT_OPENING;
9358 PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
9361 static void CloseAllOpenTimegates(void)
9365 SCAN_PLAYFIELD(x, y)
9367 int element = Feld[x][y];
9369 if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
9371 Feld[x][y] = EL_TIMEGATE_CLOSING;
9373 PlayLevelSoundAction(x, y, ACTION_CLOSING);
9378 static void DrawTwinkleOnField(int x, int y)
9380 if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
9383 if (Feld[x][y] == EL_BD_DIAMOND)
9386 if (MovDelay[x][y] == 0) // next animation frame
9387 MovDelay[x][y] = 11 * !GetSimpleRandom(500);
9389 if (MovDelay[x][y] != 0) // wait some time before next frame
9393 DrawLevelElementAnimation(x, y, Feld[x][y]);
9395 if (MovDelay[x][y] != 0)
9397 int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
9398 10 - MovDelay[x][y]);
9400 DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
9405 static void MauerWaechst(int x, int y)
9409 if (!MovDelay[x][y]) // next animation frame
9410 MovDelay[x][y] = 3 * delay;
9412 if (MovDelay[x][y]) // wait some time before next frame
9416 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9418 int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
9419 int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
9421 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
9424 if (!MovDelay[x][y])
9426 if (MovDir[x][y] == MV_LEFT)
9428 if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
9429 TEST_DrawLevelField(x - 1, y);
9431 else if (MovDir[x][y] == MV_RIGHT)
9433 if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
9434 TEST_DrawLevelField(x + 1, y);
9436 else if (MovDir[x][y] == MV_UP)
9438 if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
9439 TEST_DrawLevelField(x, y - 1);
9443 if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
9444 TEST_DrawLevelField(x, y + 1);
9447 Feld[x][y] = Store[x][y];
9449 GfxDir[x][y] = MovDir[x][y] = MV_NONE;
9450 TEST_DrawLevelField(x, y);
9455 static void MauerAbleger(int ax, int ay)
9457 int element = Feld[ax][ay];
9458 int graphic = el2img(element);
9459 boolean oben_frei = FALSE, unten_frei = FALSE;
9460 boolean links_frei = FALSE, rechts_frei = FALSE;
9461 boolean oben_massiv = FALSE, unten_massiv = FALSE;
9462 boolean links_massiv = FALSE, rechts_massiv = FALSE;
9463 boolean new_wall = FALSE;
9465 if (IS_ANIMATED(graphic))
9466 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9468 if (!MovDelay[ax][ay]) // start building new wall
9469 MovDelay[ax][ay] = 6;
9471 if (MovDelay[ax][ay]) // wait some time before building new wall
9474 if (MovDelay[ax][ay])
9478 if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9480 if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9482 if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9484 if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9487 if (element == EL_EXPANDABLE_WALL_VERTICAL ||
9488 element == EL_EXPANDABLE_WALL_ANY)
9492 Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
9493 Store[ax][ay-1] = element;
9494 GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9495 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9496 DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9497 IMG_EXPANDABLE_WALL_GROWING_UP, 0);
9502 Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
9503 Store[ax][ay+1] = element;
9504 GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9505 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9506 DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9507 IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
9512 if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9513 element == EL_EXPANDABLE_WALL_ANY ||
9514 element == EL_EXPANDABLE_WALL ||
9515 element == EL_BD_EXPANDABLE_WALL)
9519 Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
9520 Store[ax-1][ay] = element;
9521 GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9522 if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9523 DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9524 IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
9530 Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
9531 Store[ax+1][ay] = element;
9532 GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9533 if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9534 DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9535 IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
9540 if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
9541 TEST_DrawLevelField(ax, ay);
9543 if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
9545 if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
9546 unten_massiv = TRUE;
9547 if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
9548 links_massiv = TRUE;
9549 if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
9550 rechts_massiv = TRUE;
9552 if (((oben_massiv && unten_massiv) ||
9553 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9554 element == EL_EXPANDABLE_WALL) &&
9555 ((links_massiv && rechts_massiv) ||
9556 element == EL_EXPANDABLE_WALL_VERTICAL))
9557 Feld[ax][ay] = EL_WALL;
9560 PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9563 static void MauerAblegerStahl(int ax, int ay)
9565 int element = Feld[ax][ay];
9566 int graphic = el2img(element);
9567 boolean oben_frei = FALSE, unten_frei = FALSE;
9568 boolean links_frei = FALSE, rechts_frei = FALSE;
9569 boolean oben_massiv = FALSE, unten_massiv = FALSE;
9570 boolean links_massiv = FALSE, rechts_massiv = FALSE;
9571 boolean new_wall = FALSE;
9573 if (IS_ANIMATED(graphic))
9574 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9576 if (!MovDelay[ax][ay]) // start building new wall
9577 MovDelay[ax][ay] = 6;
9579 if (MovDelay[ax][ay]) // wait some time before building new wall
9582 if (MovDelay[ax][ay])
9586 if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9588 if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9590 if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9592 if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9595 if (element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
9596 element == EL_EXPANDABLE_STEELWALL_ANY)
9600 Feld[ax][ay-1] = EL_EXPANDABLE_STEELWALL_GROWING;
9601 Store[ax][ay-1] = element;
9602 GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9603 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9604 DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9605 IMG_EXPANDABLE_STEELWALL_GROWING_UP, 0);
9610 Feld[ax][ay+1] = EL_EXPANDABLE_STEELWALL_GROWING;
9611 Store[ax][ay+1] = element;
9612 GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9613 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9614 DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9615 IMG_EXPANDABLE_STEELWALL_GROWING_DOWN, 0);
9620 if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
9621 element == EL_EXPANDABLE_STEELWALL_ANY)
9625 Feld[ax-1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9626 Store[ax-1][ay] = element;
9627 GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9628 if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9629 DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9630 IMG_EXPANDABLE_STEELWALL_GROWING_LEFT, 0);
9636 Feld[ax+1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9637 Store[ax+1][ay] = element;
9638 GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9639 if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9640 DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9641 IMG_EXPANDABLE_STEELWALL_GROWING_RIGHT, 0);
9646 if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
9648 if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
9649 unten_massiv = TRUE;
9650 if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
9651 links_massiv = TRUE;
9652 if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
9653 rechts_massiv = TRUE;
9655 if (((oben_massiv && unten_massiv) ||
9656 element == EL_EXPANDABLE_STEELWALL_HORIZONTAL) &&
9657 ((links_massiv && rechts_massiv) ||
9658 element == EL_EXPANDABLE_STEELWALL_VERTICAL))
9659 Feld[ax][ay] = EL_STEELWALL;
9662 PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9665 static void CheckForDragon(int x, int y)
9668 boolean dragon_found = FALSE;
9669 static int xy[4][2] =
9677 for (i = 0; i < NUM_DIRECTIONS; i++)
9679 for (j = 0; j < 4; j++)
9681 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9683 if (IN_LEV_FIELD(xx, yy) &&
9684 (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
9686 if (Feld[xx][yy] == EL_DRAGON)
9687 dragon_found = TRUE;
9696 for (i = 0; i < NUM_DIRECTIONS; i++)
9698 for (j = 0; j < 3; j++)
9700 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9702 if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
9704 Feld[xx][yy] = EL_EMPTY;
9705 TEST_DrawLevelField(xx, yy);
9714 static void InitBuggyBase(int x, int y)
9716 int element = Feld[x][y];
9717 int activating_delay = FRAMES_PER_SECOND / 4;
9720 (element == EL_SP_BUGGY_BASE ?
9721 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
9722 element == EL_SP_BUGGY_BASE_ACTIVATING ?
9724 element == EL_SP_BUGGY_BASE_ACTIVE ?
9725 1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
9728 static void WarnBuggyBase(int x, int y)
9731 static int xy[4][2] =
9739 for (i = 0; i < NUM_DIRECTIONS; i++)
9741 int xx = x + xy[i][0];
9742 int yy = y + xy[i][1];
9744 if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
9746 PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
9753 static void InitTrap(int x, int y)
9755 ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
9758 static void ActivateTrap(int x, int y)
9760 PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
9763 static void ChangeActiveTrap(int x, int y)
9765 int graphic = IMG_TRAP_ACTIVE;
9767 // if new animation frame was drawn, correct crumbled sand border
9768 if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
9769 TEST_DrawLevelFieldCrumbled(x, y);
9772 static int getSpecialActionElement(int element, int number, int base_element)
9774 return (element != EL_EMPTY ? element :
9775 number != -1 ? base_element + number - 1 :
9779 static int getModifiedActionNumber(int value_old, int operator, int operand,
9780 int value_min, int value_max)
9782 int value_new = (operator == CA_MODE_SET ? operand :
9783 operator == CA_MODE_ADD ? value_old + operand :
9784 operator == CA_MODE_SUBTRACT ? value_old - operand :
9785 operator == CA_MODE_MULTIPLY ? value_old * operand :
9786 operator == CA_MODE_DIVIDE ? value_old / MAX(1, operand) :
9787 operator == CA_MODE_MODULO ? value_old % MAX(1, operand) :
9790 return (value_new < value_min ? value_min :
9791 value_new > value_max ? value_max :
9795 static void ExecuteCustomElementAction(int x, int y, int element, int page)
9797 struct ElementInfo *ei = &element_info[element];
9798 struct ElementChangeInfo *change = &ei->change_page[page];
9799 int target_element = change->target_element;
9800 int action_type = change->action_type;
9801 int action_mode = change->action_mode;
9802 int action_arg = change->action_arg;
9803 int action_element = change->action_element;
9806 if (!change->has_action)
9809 // ---------- determine action paramater values -----------------------------
9811 int level_time_value =
9812 (level.time > 0 ? TimeLeft :
9815 int action_arg_element_raw =
9816 (action_arg == CA_ARG_PLAYER_TRIGGER ? change->actual_trigger_player :
9817 action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
9818 action_arg == CA_ARG_ELEMENT_TARGET ? change->target_element :
9819 action_arg == CA_ARG_ELEMENT_ACTION ? change->action_element :
9820 action_arg == CA_ARG_INVENTORY_RM_TRIGGER ? change->actual_trigger_element:
9821 action_arg == CA_ARG_INVENTORY_RM_TARGET ? change->target_element :
9822 action_arg == CA_ARG_INVENTORY_RM_ACTION ? change->action_element :
9824 int action_arg_element = GetElementFromGroupElement(action_arg_element_raw);
9826 int action_arg_direction =
9827 (action_arg >= CA_ARG_DIRECTION_LEFT &&
9828 action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
9829 action_arg == CA_ARG_DIRECTION_TRIGGER ?
9830 change->actual_trigger_side :
9831 action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
9832 MV_DIR_OPPOSITE(change->actual_trigger_side) :
9835 int action_arg_number_min =
9836 (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
9839 int action_arg_number_max =
9840 (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
9841 action_type == CA_SET_LEVEL_GEMS ? 999 :
9842 action_type == CA_SET_LEVEL_TIME ? 9999 :
9843 action_type == CA_SET_LEVEL_SCORE ? 99999 :
9844 action_type == CA_SET_CE_VALUE ? 9999 :
9845 action_type == CA_SET_CE_SCORE ? 9999 :
9848 int action_arg_number_reset =
9849 (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize[0] :
9850 action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
9851 action_type == CA_SET_LEVEL_TIME ? level.time :
9852 action_type == CA_SET_LEVEL_SCORE ? 0 :
9853 action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
9854 action_type == CA_SET_CE_SCORE ? 0 :
9857 int action_arg_number =
9858 (action_arg <= CA_ARG_MAX ? action_arg :
9859 action_arg >= CA_ARG_SPEED_NOT_MOVING &&
9860 action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
9861 action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
9862 action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
9863 action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
9864 action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
9865 action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
9866 action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
9867 action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
9868 action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
9869 action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? game.gems_still_needed :
9870 action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? game.score :
9871 action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
9872 action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
9873 action_arg == CA_ARG_ELEMENT_CV_ACTION ? GET_NEW_CE_VALUE(action_element):
9874 action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
9875 action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
9876 action_arg == CA_ARG_ELEMENT_CS_ACTION ? GET_CE_SCORE(action_element) :
9877 action_arg == CA_ARG_ELEMENT_NR_TARGET ? change->target_element :
9878 action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
9879 action_arg == CA_ARG_ELEMENT_NR_ACTION ? change->action_element :
9882 int action_arg_number_old =
9883 (action_type == CA_SET_LEVEL_GEMS ? game.gems_still_needed :
9884 action_type == CA_SET_LEVEL_TIME ? TimeLeft :
9885 action_type == CA_SET_LEVEL_SCORE ? game.score :
9886 action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
9887 action_type == CA_SET_CE_SCORE ? ei->collect_score :
9890 int action_arg_number_new =
9891 getModifiedActionNumber(action_arg_number_old,
9892 action_mode, action_arg_number,
9893 action_arg_number_min, action_arg_number_max);
9895 int trigger_player_bits =
9896 (change->actual_trigger_player_bits != CH_PLAYER_NONE ?
9897 change->actual_trigger_player_bits : change->trigger_player);
9899 int action_arg_player_bits =
9900 (action_arg >= CA_ARG_PLAYER_1 &&
9901 action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
9902 action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
9903 action_arg == CA_ARG_PLAYER_ACTION ? 1 << GET_PLAYER_NR(action_element) :
9906 // ---------- execute action -----------------------------------------------
9908 switch (action_type)
9915 // ---------- level actions ----------------------------------------------
9917 case CA_RESTART_LEVEL:
9919 game.restart_level = TRUE;
9924 case CA_SHOW_ENVELOPE:
9926 int element = getSpecialActionElement(action_arg_element,
9927 action_arg_number, EL_ENVELOPE_1);
9929 if (IS_ENVELOPE(element))
9930 local_player->show_envelope = element;
9935 case CA_SET_LEVEL_TIME:
9937 if (level.time > 0) // only modify limited time value
9939 TimeLeft = action_arg_number_new;
9941 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
9943 DisplayGameControlValues();
9945 if (!TimeLeft && setup.time_limit)
9946 for (i = 0; i < MAX_PLAYERS; i++)
9947 KillPlayer(&stored_player[i]);
9953 case CA_SET_LEVEL_SCORE:
9955 game.score = action_arg_number_new;
9957 game_panel_controls[GAME_PANEL_SCORE].value = game.score;
9959 DisplayGameControlValues();
9964 case CA_SET_LEVEL_GEMS:
9966 game.gems_still_needed = action_arg_number_new;
9968 game.snapshot.collected_item = TRUE;
9970 game_panel_controls[GAME_PANEL_GEMS].value = game.gems_still_needed;
9972 DisplayGameControlValues();
9977 case CA_SET_LEVEL_WIND:
9979 game.wind_direction = action_arg_direction;
9984 case CA_SET_LEVEL_RANDOM_SEED:
9986 // ensure that setting a new random seed while playing is predictable
9987 InitRND(action_arg_number_new ? action_arg_number_new : RND(1000000) + 1);
9992 // ---------- player actions ---------------------------------------------
9994 case CA_MOVE_PLAYER:
9995 case CA_MOVE_PLAYER_NEW:
9997 // automatically move to the next field in specified direction
9998 for (i = 0; i < MAX_PLAYERS; i++)
9999 if (trigger_player_bits & (1 << i))
10000 if (action_type == CA_MOVE_PLAYER ||
10001 stored_player[i].MovPos == 0)
10002 stored_player[i].programmed_action = action_arg_direction;
10007 case CA_EXIT_PLAYER:
10009 for (i = 0; i < MAX_PLAYERS; i++)
10010 if (action_arg_player_bits & (1 << i))
10011 ExitPlayer(&stored_player[i]);
10013 if (game.players_still_needed == 0)
10019 case CA_KILL_PLAYER:
10021 for (i = 0; i < MAX_PLAYERS; i++)
10022 if (action_arg_player_bits & (1 << i))
10023 KillPlayer(&stored_player[i]);
10028 case CA_SET_PLAYER_KEYS:
10030 int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
10031 int element = getSpecialActionElement(action_arg_element,
10032 action_arg_number, EL_KEY_1);
10034 if (IS_KEY(element))
10036 for (i = 0; i < MAX_PLAYERS; i++)
10038 if (trigger_player_bits & (1 << i))
10040 stored_player[i].key[KEY_NR(element)] = key_state;
10042 DrawGameDoorValues();
10050 case CA_SET_PLAYER_SPEED:
10052 for (i = 0; i < MAX_PLAYERS; i++)
10054 if (trigger_player_bits & (1 << i))
10056 int move_stepsize = TILEX / stored_player[i].move_delay_value;
10058 if (action_arg == CA_ARG_SPEED_FASTER &&
10059 stored_player[i].cannot_move)
10061 action_arg_number = STEPSIZE_VERY_SLOW;
10063 else if (action_arg == CA_ARG_SPEED_SLOWER ||
10064 action_arg == CA_ARG_SPEED_FASTER)
10066 action_arg_number = 2;
10067 action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
10070 else if (action_arg == CA_ARG_NUMBER_RESET)
10072 action_arg_number = level.initial_player_stepsize[i];
10076 getModifiedActionNumber(move_stepsize,
10079 action_arg_number_min,
10080 action_arg_number_max);
10082 SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
10089 case CA_SET_PLAYER_SHIELD:
10091 for (i = 0; i < MAX_PLAYERS; i++)
10093 if (trigger_player_bits & (1 << i))
10095 if (action_arg == CA_ARG_SHIELD_OFF)
10097 stored_player[i].shield_normal_time_left = 0;
10098 stored_player[i].shield_deadly_time_left = 0;
10100 else if (action_arg == CA_ARG_SHIELD_NORMAL)
10102 stored_player[i].shield_normal_time_left = 999999;
10104 else if (action_arg == CA_ARG_SHIELD_DEADLY)
10106 stored_player[i].shield_normal_time_left = 999999;
10107 stored_player[i].shield_deadly_time_left = 999999;
10115 case CA_SET_PLAYER_GRAVITY:
10117 for (i = 0; i < MAX_PLAYERS; i++)
10119 if (trigger_player_bits & (1 << i))
10121 stored_player[i].gravity =
10122 (action_arg == CA_ARG_GRAVITY_OFF ? FALSE :
10123 action_arg == CA_ARG_GRAVITY_ON ? TRUE :
10124 action_arg == CA_ARG_GRAVITY_TOGGLE ? !stored_player[i].gravity :
10125 stored_player[i].gravity);
10132 case CA_SET_PLAYER_ARTWORK:
10134 for (i = 0; i < MAX_PLAYERS; i++)
10136 if (trigger_player_bits & (1 << i))
10138 int artwork_element = action_arg_element;
10140 if (action_arg == CA_ARG_ELEMENT_RESET)
10142 (level.use_artwork_element[i] ? level.artwork_element[i] :
10143 stored_player[i].element_nr);
10145 if (stored_player[i].artwork_element != artwork_element)
10146 stored_player[i].Frame = 0;
10148 stored_player[i].artwork_element = artwork_element;
10150 SetPlayerWaiting(&stored_player[i], FALSE);
10152 // set number of special actions for bored and sleeping animation
10153 stored_player[i].num_special_action_bored =
10154 get_num_special_action(artwork_element,
10155 ACTION_BORING_1, ACTION_BORING_LAST);
10156 stored_player[i].num_special_action_sleeping =
10157 get_num_special_action(artwork_element,
10158 ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
10165 case CA_SET_PLAYER_INVENTORY:
10167 for (i = 0; i < MAX_PLAYERS; i++)
10169 struct PlayerInfo *player = &stored_player[i];
10172 if (trigger_player_bits & (1 << i))
10174 int inventory_element = action_arg_element;
10176 if (action_arg == CA_ARG_ELEMENT_TARGET ||
10177 action_arg == CA_ARG_ELEMENT_TRIGGER ||
10178 action_arg == CA_ARG_ELEMENT_ACTION)
10180 int element = inventory_element;
10181 int collect_count = element_info[element].collect_count_initial;
10183 if (!IS_CUSTOM_ELEMENT(element))
10186 if (collect_count == 0)
10187 player->inventory_infinite_element = element;
10189 for (k = 0; k < collect_count; k++)
10190 if (player->inventory_size < MAX_INVENTORY_SIZE)
10191 player->inventory_element[player->inventory_size++] =
10194 else if (action_arg == CA_ARG_INVENTORY_RM_TARGET ||
10195 action_arg == CA_ARG_INVENTORY_RM_TRIGGER ||
10196 action_arg == CA_ARG_INVENTORY_RM_ACTION)
10198 if (player->inventory_infinite_element != EL_UNDEFINED &&
10199 IS_EQUAL_OR_IN_GROUP(player->inventory_infinite_element,
10200 action_arg_element_raw))
10201 player->inventory_infinite_element = EL_UNDEFINED;
10203 for (k = 0, j = 0; j < player->inventory_size; j++)
10205 if (!IS_EQUAL_OR_IN_GROUP(player->inventory_element[j],
10206 action_arg_element_raw))
10207 player->inventory_element[k++] = player->inventory_element[j];
10210 player->inventory_size = k;
10212 else if (action_arg == CA_ARG_INVENTORY_RM_FIRST)
10214 if (player->inventory_size > 0)
10216 for (j = 0; j < player->inventory_size - 1; j++)
10217 player->inventory_element[j] = player->inventory_element[j + 1];
10219 player->inventory_size--;
10222 else if (action_arg == CA_ARG_INVENTORY_RM_LAST)
10224 if (player->inventory_size > 0)
10225 player->inventory_size--;
10227 else if (action_arg == CA_ARG_INVENTORY_RM_ALL)
10229 player->inventory_infinite_element = EL_UNDEFINED;
10230 player->inventory_size = 0;
10232 else if (action_arg == CA_ARG_INVENTORY_RESET)
10234 player->inventory_infinite_element = EL_UNDEFINED;
10235 player->inventory_size = 0;
10237 if (level.use_initial_inventory[i])
10239 for (j = 0; j < level.initial_inventory_size[i]; j++)
10241 int element = level.initial_inventory_content[i][j];
10242 int collect_count = element_info[element].collect_count_initial;
10244 if (!IS_CUSTOM_ELEMENT(element))
10247 if (collect_count == 0)
10248 player->inventory_infinite_element = element;
10250 for (k = 0; k < collect_count; k++)
10251 if (player->inventory_size < MAX_INVENTORY_SIZE)
10252 player->inventory_element[player->inventory_size++] =
10263 // ---------- CE actions -------------------------------------------------
10265 case CA_SET_CE_VALUE:
10267 int last_ce_value = CustomValue[x][y];
10269 CustomValue[x][y] = action_arg_number_new;
10271 if (CustomValue[x][y] != last_ce_value)
10273 CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_CHANGES);
10274 CheckTriggeredElementChange(x, y, element, CE_VALUE_CHANGES_OF_X);
10276 if (CustomValue[x][y] == 0)
10278 // reset change counter (else CE_VALUE_GETS_ZERO would not work)
10279 ChangeCount[x][y] = 0; // allow at least one more change
10281 CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
10282 CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
10289 case CA_SET_CE_SCORE:
10291 int last_ce_score = ei->collect_score;
10293 ei->collect_score = action_arg_number_new;
10295 if (ei->collect_score != last_ce_score)
10297 CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
10298 CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
10300 if (ei->collect_score == 0)
10304 // reset change counter (else CE_SCORE_GETS_ZERO would not work)
10305 ChangeCount[x][y] = 0; // allow at least one more change
10307 CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
10308 CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
10311 This is a very special case that seems to be a mixture between
10312 CheckElementChange() and CheckTriggeredElementChange(): while
10313 the first one only affects single elements that are triggered
10314 directly, the second one affects multiple elements in the playfield
10315 that are triggered indirectly by another element. This is a third
10316 case: Changing the CE score always affects multiple identical CEs,
10317 so every affected CE must be checked, not only the single CE for
10318 which the CE score was changed in the first place (as every instance
10319 of that CE shares the same CE score, and therefore also can change)!
10321 SCAN_PLAYFIELD(xx, yy)
10323 if (Feld[xx][yy] == element)
10324 CheckElementChange(xx, yy, element, EL_UNDEFINED,
10325 CE_SCORE_GETS_ZERO);
10333 case CA_SET_CE_ARTWORK:
10335 int artwork_element = action_arg_element;
10336 boolean reset_frame = FALSE;
10339 if (action_arg == CA_ARG_ELEMENT_RESET)
10340 artwork_element = (ei->use_gfx_element ? ei->gfx_element_initial :
10343 if (ei->gfx_element != artwork_element)
10344 reset_frame = TRUE;
10346 ei->gfx_element = artwork_element;
10348 SCAN_PLAYFIELD(xx, yy)
10350 if (Feld[xx][yy] == element)
10354 ResetGfxAnimation(xx, yy);
10355 ResetRandomAnimationValue(xx, yy);
10358 TEST_DrawLevelField(xx, yy);
10365 // ---------- engine actions ---------------------------------------------
10367 case CA_SET_ENGINE_SCAN_MODE:
10369 InitPlayfieldScanMode(action_arg);
10379 static void CreateFieldExt(int x, int y, int element, boolean is_change)
10381 int old_element = Feld[x][y];
10382 int new_element = GetElementFromGroupElement(element);
10383 int previous_move_direction = MovDir[x][y];
10384 int last_ce_value = CustomValue[x][y];
10385 boolean player_explosion_protected = PLAYER_EXPLOSION_PROTECTED(x, y);
10386 boolean new_element_is_player = ELEM_IS_PLAYER(new_element);
10387 boolean add_player_onto_element = (new_element_is_player &&
10388 new_element != EL_SOKOBAN_FIELD_PLAYER &&
10389 IS_WALKABLE(old_element));
10391 if (!add_player_onto_element)
10393 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
10394 RemoveMovingField(x, y);
10398 Feld[x][y] = new_element;
10400 if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
10401 MovDir[x][y] = previous_move_direction;
10403 if (element_info[new_element].use_last_ce_value)
10404 CustomValue[x][y] = last_ce_value;
10406 InitField_WithBug1(x, y, FALSE);
10408 new_element = Feld[x][y]; // element may have changed
10410 ResetGfxAnimation(x, y);
10411 ResetRandomAnimationValue(x, y);
10413 TEST_DrawLevelField(x, y);
10415 if (GFX_CRUMBLED(new_element))
10416 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
10419 // check if element under the player changes from accessible to unaccessible
10420 // (needed for special case of dropping element which then changes)
10421 // (must be checked after creating new element for walkable group elements)
10422 if (IS_PLAYER(x, y) && !player_explosion_protected &&
10423 IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
10430 // "ChangeCount" not set yet to allow "entered by player" change one time
10431 if (new_element_is_player)
10432 RelocatePlayer(x, y, new_element);
10435 ChangeCount[x][y]++; // count number of changes in the same frame
10437 TestIfBadThingTouchesPlayer(x, y);
10438 TestIfPlayerTouchesCustomElement(x, y);
10439 TestIfElementTouchesCustomElement(x, y);
10442 static void CreateField(int x, int y, int element)
10444 CreateFieldExt(x, y, element, FALSE);
10447 static void CreateElementFromChange(int x, int y, int element)
10449 element = GET_VALID_RUNTIME_ELEMENT(element);
10451 if (game.engine_version >= VERSION_IDENT(3,2,0,7))
10453 int old_element = Feld[x][y];
10455 // prevent changed element from moving in same engine frame
10456 // unless both old and new element can either fall or move
10457 if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
10458 (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
10462 CreateFieldExt(x, y, element, TRUE);
10465 static boolean ChangeElement(int x, int y, int element, int page)
10467 struct ElementInfo *ei = &element_info[element];
10468 struct ElementChangeInfo *change = &ei->change_page[page];
10469 int ce_value = CustomValue[x][y];
10470 int ce_score = ei->collect_score;
10471 int target_element;
10472 int old_element = Feld[x][y];
10474 // always use default change event to prevent running into a loop
10475 if (ChangeEvent[x][y] == -1)
10476 ChangeEvent[x][y] = CE_DELAY;
10478 if (ChangeEvent[x][y] == CE_DELAY)
10480 // reset actual trigger element, trigger player and action element
10481 change->actual_trigger_element = EL_EMPTY;
10482 change->actual_trigger_player = EL_EMPTY;
10483 change->actual_trigger_player_bits = CH_PLAYER_NONE;
10484 change->actual_trigger_side = CH_SIDE_NONE;
10485 change->actual_trigger_ce_value = 0;
10486 change->actual_trigger_ce_score = 0;
10489 // do not change elements more than a specified maximum number of changes
10490 if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
10493 ChangeCount[x][y]++; // count number of changes in the same frame
10495 if (change->explode)
10502 if (change->use_target_content)
10504 boolean complete_replace = TRUE;
10505 boolean can_replace[3][3];
10508 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10511 boolean is_walkable;
10512 boolean is_diggable;
10513 boolean is_collectible;
10514 boolean is_removable;
10515 boolean is_destructible;
10516 int ex = x + xx - 1;
10517 int ey = y + yy - 1;
10518 int content_element = change->target_content.e[xx][yy];
10521 can_replace[xx][yy] = TRUE;
10523 if (ex == x && ey == y) // do not check changing element itself
10526 if (content_element == EL_EMPTY_SPACE)
10528 can_replace[xx][yy] = FALSE; // do not replace border with space
10533 if (!IN_LEV_FIELD(ex, ey))
10535 can_replace[xx][yy] = FALSE;
10536 complete_replace = FALSE;
10543 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10544 e = MovingOrBlocked2Element(ex, ey);
10546 is_empty = (IS_FREE(ex, ey) ||
10547 (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
10549 is_walkable = (is_empty || IS_WALKABLE(e));
10550 is_diggable = (is_empty || IS_DIGGABLE(e));
10551 is_collectible = (is_empty || IS_COLLECTIBLE(e));
10552 is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
10553 is_removable = (is_diggable || is_collectible);
10555 can_replace[xx][yy] =
10556 (((change->replace_when == CP_WHEN_EMPTY && is_empty) ||
10557 (change->replace_when == CP_WHEN_WALKABLE && is_walkable) ||
10558 (change->replace_when == CP_WHEN_DIGGABLE && is_diggable) ||
10559 (change->replace_when == CP_WHEN_COLLECTIBLE && is_collectible) ||
10560 (change->replace_when == CP_WHEN_REMOVABLE && is_removable) ||
10561 (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
10562 !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
10564 if (!can_replace[xx][yy])
10565 complete_replace = FALSE;
10568 if (!change->only_if_complete || complete_replace)
10570 boolean something_has_changed = FALSE;
10572 if (change->only_if_complete && change->use_random_replace &&
10573 RND(100) < change->random_percentage)
10576 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10578 int ex = x + xx - 1;
10579 int ey = y + yy - 1;
10580 int content_element;
10582 if (can_replace[xx][yy] && (!change->use_random_replace ||
10583 RND(100) < change->random_percentage))
10585 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10586 RemoveMovingField(ex, ey);
10588 ChangeEvent[ex][ey] = ChangeEvent[x][y];
10590 content_element = change->target_content.e[xx][yy];
10591 target_element = GET_TARGET_ELEMENT(element, content_element, change,
10592 ce_value, ce_score);
10594 CreateElementFromChange(ex, ey, target_element);
10596 something_has_changed = TRUE;
10598 // for symmetry reasons, freeze newly created border elements
10599 if (ex != x || ey != y)
10600 Stop[ex][ey] = TRUE; // no more moving in this frame
10604 if (something_has_changed)
10606 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10607 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10613 target_element = GET_TARGET_ELEMENT(element, change->target_element, change,
10614 ce_value, ce_score);
10616 if (element == EL_DIAGONAL_GROWING ||
10617 element == EL_DIAGONAL_SHRINKING)
10619 target_element = Store[x][y];
10621 Store[x][y] = EL_EMPTY;
10624 CreateElementFromChange(x, y, target_element);
10626 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10627 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10630 // this uses direct change before indirect change
10631 CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
10636 static void HandleElementChange(int x, int y, int page)
10638 int element = MovingOrBlocked2Element(x, y);
10639 struct ElementInfo *ei = &element_info[element];
10640 struct ElementChangeInfo *change = &ei->change_page[page];
10641 boolean handle_action_before_change = FALSE;
10644 if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
10645 !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
10648 printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
10649 x, y, element, element_info[element].token_name);
10650 printf("HandleElementChange(): This should never happen!\n");
10655 // this can happen with classic bombs on walkable, changing elements
10656 if (!CAN_CHANGE_OR_HAS_ACTION(element))
10661 if (ChangeDelay[x][y] == 0) // initialize element change
10663 ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
10665 if (change->can_change)
10667 // !!! not clear why graphic animation should be reset at all here !!!
10668 // !!! UPDATE: but is needed for correct Snake Bite tail animation !!!
10669 // !!! SOLUTION: do not reset if graphics engine set to 4 or above !!!
10672 GRAPHICAL BUG ADDRESSED BY CHECKING GRAPHICS ENGINE VERSION:
10674 When using an animation frame delay of 1 (this only happens with
10675 "sp_zonk.moving.left/right" in the classic graphics), the default
10676 (non-moving) animation shows wrong animation frames (while the
10677 moving animation, like "sp_zonk.moving.left/right", is correct,
10678 so this graphical bug never shows up with the classic graphics).
10679 For an animation with 4 frames, this causes wrong frames 0,0,1,2
10680 be drawn instead of the correct frames 0,1,2,3. This is caused by
10681 "GfxFrame[][]" being reset *twice* (in two successive frames) after
10682 an element change: First when the change delay ("ChangeDelay[][]")
10683 counter has reached zero after decrementing, then a second time in
10684 the next frame (after "GfxFrame[][]" was already incremented) when
10685 "ChangeDelay[][]" is reset to the initial delay value again.
10687 This causes frame 0 to be drawn twice, while the last frame won't
10688 be drawn anymore, resulting in the wrong frame sequence 0,0,1,2.
10690 As some animations may already be cleverly designed around this bug
10691 (at least the "Snake Bite" snake tail animation does this), it cannot
10692 simply be fixed here without breaking such existing animations.
10693 Unfortunately, it cannot easily be detected if a graphics set was
10694 designed "before" or "after" the bug was fixed. As a workaround,
10695 a new graphics set option "game.graphics_engine_version" was added
10696 to be able to specify the game's major release version for which the
10697 graphics set was designed, which can then be used to decide if the
10698 bugfix should be used (version 4 and above) or not (version 3 or
10699 below, or if no version was specified at all, as with old sets).
10701 (The wrong/fixed animation frames can be tested with the test level set
10702 "test_gfxframe" and level "000", which contains a specially prepared
10703 custom element at level position (x/y) == (11/9) which uses the zonk
10704 animation mentioned above. Using "game.graphics_engine_version: 4"
10705 fixes the wrong animation frames, showing the correct frames 0,1,2,3.
10706 This can also be seen from the debug output for this test element.)
10709 // when a custom element is about to change (for example by change delay),
10710 // do not reset graphic animation when the custom element is moving
10711 if (game.graphics_engine_version < 4 &&
10714 ResetGfxAnimation(x, y);
10715 ResetRandomAnimationValue(x, y);
10718 if (change->pre_change_function)
10719 change->pre_change_function(x, y);
10723 ChangeDelay[x][y]--;
10725 if (ChangeDelay[x][y] != 0) // continue element change
10727 if (change->can_change)
10729 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10731 if (IS_ANIMATED(graphic))
10732 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10734 if (change->change_function)
10735 change->change_function(x, y);
10738 else // finish element change
10740 if (ChangePage[x][y] != -1) // remember page from delayed change
10742 page = ChangePage[x][y];
10743 ChangePage[x][y] = -1;
10745 change = &ei->change_page[page];
10748 if (IS_MOVING(x, y)) // never change a running system ;-)
10750 ChangeDelay[x][y] = 1; // try change after next move step
10751 ChangePage[x][y] = page; // remember page to use for change
10756 // special case: set new level random seed before changing element
10757 if (change->has_action && change->action_type == CA_SET_LEVEL_RANDOM_SEED)
10758 handle_action_before_change = TRUE;
10760 if (change->has_action && handle_action_before_change)
10761 ExecuteCustomElementAction(x, y, element, page);
10763 if (change->can_change)
10765 if (ChangeElement(x, y, element, page))
10767 if (change->post_change_function)
10768 change->post_change_function(x, y);
10772 if (change->has_action && !handle_action_before_change)
10773 ExecuteCustomElementAction(x, y, element, page);
10777 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
10778 int trigger_element,
10780 int trigger_player,
10784 boolean change_done_any = FALSE;
10785 int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
10788 if (!(trigger_events[trigger_element][trigger_event]))
10791 RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
10793 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
10795 int element = EL_CUSTOM_START + i;
10796 boolean change_done = FALSE;
10799 if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
10800 !HAS_ANY_CHANGE_EVENT(element, trigger_event))
10803 for (p = 0; p < element_info[element].num_change_pages; p++)
10805 struct ElementChangeInfo *change = &element_info[element].change_page[p];
10807 if (change->can_change_or_has_action &&
10808 change->has_event[trigger_event] &&
10809 change->trigger_side & trigger_side &&
10810 change->trigger_player & trigger_player &&
10811 change->trigger_page & trigger_page_bits &&
10812 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
10814 change->actual_trigger_element = trigger_element;
10815 change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
10816 change->actual_trigger_player_bits = trigger_player;
10817 change->actual_trigger_side = trigger_side;
10818 change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
10819 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10821 if ((change->can_change && !change_done) || change->has_action)
10825 SCAN_PLAYFIELD(x, y)
10827 if (Feld[x][y] == element)
10829 if (change->can_change && !change_done)
10831 // if element already changed in this frame, not only prevent
10832 // another element change (checked in ChangeElement()), but
10833 // also prevent additional element actions for this element
10835 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
10836 !level.use_action_after_change_bug)
10839 ChangeDelay[x][y] = 1;
10840 ChangeEvent[x][y] = trigger_event;
10842 HandleElementChange(x, y, p);
10844 else if (change->has_action)
10846 // if element already changed in this frame, not only prevent
10847 // another element change (checked in ChangeElement()), but
10848 // also prevent additional element actions for this element
10850 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
10851 !level.use_action_after_change_bug)
10854 ExecuteCustomElementAction(x, y, element, p);
10855 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10860 if (change->can_change)
10862 change_done = TRUE;
10863 change_done_any = TRUE;
10870 RECURSION_LOOP_DETECTION_END();
10872 return change_done_any;
10875 static boolean CheckElementChangeExt(int x, int y,
10877 int trigger_element,
10879 int trigger_player,
10882 boolean change_done = FALSE;
10885 if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
10886 !HAS_ANY_CHANGE_EVENT(element, trigger_event))
10889 if (Feld[x][y] == EL_BLOCKED)
10891 Blocked2Moving(x, y, &x, &y);
10892 element = Feld[x][y];
10895 // check if element has already changed or is about to change after moving
10896 if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
10897 Feld[x][y] != element) ||
10899 (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
10900 (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
10901 ChangePage[x][y] != -1)))
10904 RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
10906 for (p = 0; p < element_info[element].num_change_pages; p++)
10908 struct ElementChangeInfo *change = &element_info[element].change_page[p];
10910 /* check trigger element for all events where the element that is checked
10911 for changing interacts with a directly adjacent element -- this is
10912 different to element changes that affect other elements to change on the
10913 whole playfield (which is handeld by CheckTriggeredElementChangeExt()) */
10914 boolean check_trigger_element =
10915 (trigger_event == CE_TOUCHING_X ||
10916 trigger_event == CE_HITTING_X ||
10917 trigger_event == CE_HIT_BY_X ||
10918 trigger_event == CE_DIGGING_X); // this one was forgotten until 3.2.3
10920 if (change->can_change_or_has_action &&
10921 change->has_event[trigger_event] &&
10922 change->trigger_side & trigger_side &&
10923 change->trigger_player & trigger_player &&
10924 (!check_trigger_element ||
10925 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
10927 change->actual_trigger_element = trigger_element;
10928 change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
10929 change->actual_trigger_player_bits = trigger_player;
10930 change->actual_trigger_side = trigger_side;
10931 change->actual_trigger_ce_value = CustomValue[x][y];
10932 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10934 // special case: trigger element not at (x,y) position for some events
10935 if (check_trigger_element)
10947 { 0, 0 }, { 0, 0 }, { 0, 0 },
10951 int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
10952 int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
10954 change->actual_trigger_ce_value = CustomValue[xx][yy];
10955 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10958 if (change->can_change && !change_done)
10960 ChangeDelay[x][y] = 1;
10961 ChangeEvent[x][y] = trigger_event;
10963 HandleElementChange(x, y, p);
10965 change_done = TRUE;
10967 else if (change->has_action)
10969 ExecuteCustomElementAction(x, y, element, p);
10970 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10975 RECURSION_LOOP_DETECTION_END();
10977 return change_done;
10980 static void PlayPlayerSound(struct PlayerInfo *player)
10982 int jx = player->jx, jy = player->jy;
10983 int sound_element = player->artwork_element;
10984 int last_action = player->last_action_waiting;
10985 int action = player->action_waiting;
10987 if (player->is_waiting)
10989 if (action != last_action)
10990 PlayLevelSoundElementAction(jx, jy, sound_element, action);
10992 PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
10996 if (action != last_action)
10997 StopSound(element_info[sound_element].sound[last_action]);
10999 if (last_action == ACTION_SLEEPING)
11000 PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
11004 static void PlayAllPlayersSound(void)
11008 for (i = 0; i < MAX_PLAYERS; i++)
11009 if (stored_player[i].active)
11010 PlayPlayerSound(&stored_player[i]);
11013 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
11015 boolean last_waiting = player->is_waiting;
11016 int move_dir = player->MovDir;
11018 player->dir_waiting = move_dir;
11019 player->last_action_waiting = player->action_waiting;
11023 if (!last_waiting) // not waiting -> waiting
11025 player->is_waiting = TRUE;
11027 player->frame_counter_bored =
11029 game.player_boring_delay_fixed +
11030 GetSimpleRandom(game.player_boring_delay_random);
11031 player->frame_counter_sleeping =
11033 game.player_sleeping_delay_fixed +
11034 GetSimpleRandom(game.player_sleeping_delay_random);
11036 InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
11039 if (game.player_sleeping_delay_fixed +
11040 game.player_sleeping_delay_random > 0 &&
11041 player->anim_delay_counter == 0 &&
11042 player->post_delay_counter == 0 &&
11043 FrameCounter >= player->frame_counter_sleeping)
11044 player->is_sleeping = TRUE;
11045 else if (game.player_boring_delay_fixed +
11046 game.player_boring_delay_random > 0 &&
11047 FrameCounter >= player->frame_counter_bored)
11048 player->is_bored = TRUE;
11050 player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
11051 player->is_bored ? ACTION_BORING :
11054 if (player->is_sleeping && player->use_murphy)
11056 // special case for sleeping Murphy when leaning against non-free tile
11058 if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
11059 (Feld[player->jx - 1][player->jy] != EL_EMPTY &&
11060 !IS_MOVING(player->jx - 1, player->jy)))
11061 move_dir = MV_LEFT;
11062 else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
11063 (Feld[player->jx + 1][player->jy] != EL_EMPTY &&
11064 !IS_MOVING(player->jx + 1, player->jy)))
11065 move_dir = MV_RIGHT;
11067 player->is_sleeping = FALSE;
11069 player->dir_waiting = move_dir;
11072 if (player->is_sleeping)
11074 if (player->num_special_action_sleeping > 0)
11076 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11078 int last_special_action = player->special_action_sleeping;
11079 int num_special_action = player->num_special_action_sleeping;
11080 int special_action =
11081 (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
11082 last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
11083 last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
11084 last_special_action + 1 : ACTION_SLEEPING);
11085 int special_graphic =
11086 el_act_dir2img(player->artwork_element, special_action, move_dir);
11088 player->anim_delay_counter =
11089 graphic_info[special_graphic].anim_delay_fixed +
11090 GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
11091 player->post_delay_counter =
11092 graphic_info[special_graphic].post_delay_fixed +
11093 GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
11095 player->special_action_sleeping = special_action;
11098 if (player->anim_delay_counter > 0)
11100 player->action_waiting = player->special_action_sleeping;
11101 player->anim_delay_counter--;
11103 else if (player->post_delay_counter > 0)
11105 player->post_delay_counter--;
11109 else if (player->is_bored)
11111 if (player->num_special_action_bored > 0)
11113 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11115 int special_action =
11116 ACTION_BORING_1 + GetSimpleRandom(player->num_special_action_bored);
11117 int special_graphic =
11118 el_act_dir2img(player->artwork_element, special_action, move_dir);
11120 player->anim_delay_counter =
11121 graphic_info[special_graphic].anim_delay_fixed +
11122 GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
11123 player->post_delay_counter =
11124 graphic_info[special_graphic].post_delay_fixed +
11125 GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
11127 player->special_action_bored = special_action;
11130 if (player->anim_delay_counter > 0)
11132 player->action_waiting = player->special_action_bored;
11133 player->anim_delay_counter--;
11135 else if (player->post_delay_counter > 0)
11137 player->post_delay_counter--;
11142 else if (last_waiting) // waiting -> not waiting
11144 player->is_waiting = FALSE;
11145 player->is_bored = FALSE;
11146 player->is_sleeping = FALSE;
11148 player->frame_counter_bored = -1;
11149 player->frame_counter_sleeping = -1;
11151 player->anim_delay_counter = 0;
11152 player->post_delay_counter = 0;
11154 player->dir_waiting = player->MovDir;
11155 player->action_waiting = ACTION_DEFAULT;
11157 player->special_action_bored = ACTION_DEFAULT;
11158 player->special_action_sleeping = ACTION_DEFAULT;
11162 static void CheckSaveEngineSnapshot(struct PlayerInfo *player)
11164 if ((!player->is_moving && player->was_moving) ||
11165 (player->MovPos == 0 && player->was_moving) ||
11166 (player->is_snapping && !player->was_snapping) ||
11167 (player->is_dropping && !player->was_dropping))
11169 if (!CheckSaveEngineSnapshotToList())
11172 player->was_moving = FALSE;
11173 player->was_snapping = TRUE;
11174 player->was_dropping = TRUE;
11178 if (player->is_moving)
11179 player->was_moving = TRUE;
11181 if (!player->is_snapping)
11182 player->was_snapping = FALSE;
11184 if (!player->is_dropping)
11185 player->was_dropping = FALSE;
11189 static void CheckSingleStepMode(struct PlayerInfo *player)
11191 if (tape.single_step && tape.recording && !tape.pausing)
11193 /* as it is called "single step mode", just return to pause mode when the
11194 player stopped moving after one tile (or never starts moving at all) */
11195 if (!player->is_moving &&
11196 !player->is_pushing &&
11197 !player->is_dropping_pressed)
11198 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
11201 CheckSaveEngineSnapshot(player);
11204 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
11206 int left = player_action & JOY_LEFT;
11207 int right = player_action & JOY_RIGHT;
11208 int up = player_action & JOY_UP;
11209 int down = player_action & JOY_DOWN;
11210 int button1 = player_action & JOY_BUTTON_1;
11211 int button2 = player_action & JOY_BUTTON_2;
11212 int dx = (left ? -1 : right ? 1 : 0);
11213 int dy = (up ? -1 : down ? 1 : 0);
11215 if (!player->active || tape.pausing)
11221 SnapField(player, dx, dy);
11225 DropElement(player);
11227 MovePlayer(player, dx, dy);
11230 CheckSingleStepMode(player);
11232 SetPlayerWaiting(player, FALSE);
11234 return player_action;
11238 // no actions for this player (no input at player's configured device)
11240 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
11241 SnapField(player, 0, 0);
11242 CheckGravityMovementWhenNotMoving(player);
11244 if (player->MovPos == 0)
11245 SetPlayerWaiting(player, TRUE);
11247 if (player->MovPos == 0) // needed for tape.playing
11248 player->is_moving = FALSE;
11250 player->is_dropping = FALSE;
11251 player->is_dropping_pressed = FALSE;
11252 player->drop_pressed_delay = 0;
11254 CheckSingleStepMode(player);
11260 static void SetMouseActionFromTapeAction(struct MouseActionInfo *mouse_action,
11263 if (!tape.use_mouse_actions)
11266 mouse_action->lx = tape_action[TAPE_ACTION_LX];
11267 mouse_action->ly = tape_action[TAPE_ACTION_LY];
11268 mouse_action->button = tape_action[TAPE_ACTION_BUTTON];
11271 static void SetTapeActionFromMouseAction(byte *tape_action,
11272 struct MouseActionInfo *mouse_action)
11274 if (!tape.use_mouse_actions)
11277 tape_action[TAPE_ACTION_LX] = mouse_action->lx;
11278 tape_action[TAPE_ACTION_LY] = mouse_action->ly;
11279 tape_action[TAPE_ACTION_BUTTON] = mouse_action->button;
11282 static void CheckLevelSolved(void)
11284 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11286 if (game_em.level_solved &&
11287 !game_em.game_over) // game won
11291 game_em.game_over = TRUE;
11293 game.all_players_gone = TRUE;
11296 if (game_em.game_over) // game lost
11297 game.all_players_gone = TRUE;
11299 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11301 if (game_sp.level_solved &&
11302 !game_sp.game_over) // game won
11306 game_sp.game_over = TRUE;
11308 game.all_players_gone = TRUE;
11311 if (game_sp.game_over) // game lost
11312 game.all_players_gone = TRUE;
11314 else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
11316 if (game_mm.level_solved &&
11317 !game_mm.game_over) // game won
11321 game_mm.game_over = TRUE;
11323 game.all_players_gone = TRUE;
11326 if (game_mm.game_over) // game lost
11327 game.all_players_gone = TRUE;
11331 static void CheckLevelTime(void)
11335 if (TimeFrames >= FRAMES_PER_SECOND)
11340 for (i = 0; i < MAX_PLAYERS; i++)
11342 struct PlayerInfo *player = &stored_player[i];
11344 if (SHIELD_ON(player))
11346 player->shield_normal_time_left--;
11348 if (player->shield_deadly_time_left > 0)
11349 player->shield_deadly_time_left--;
11353 if (!game.LevelSolved && !level.use_step_counter)
11361 if (TimeLeft <= 10 && setup.time_limit)
11362 PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
11364 /* this does not make sense: game_panel_controls[GAME_PANEL_TIME].value
11365 is reset from other values in UpdateGameDoorValues() -- FIX THIS */
11367 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
11369 if (!TimeLeft && setup.time_limit)
11371 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11372 game_em.lev->killed_out_of_time = TRUE;
11374 for (i = 0; i < MAX_PLAYERS; i++)
11375 KillPlayer(&stored_player[i]);
11378 else if (game.no_time_limit && !game.all_players_gone)
11380 game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
11383 game_em.lev->time = (game.no_time_limit ? TimePlayed : TimeLeft);
11386 if (tape.recording || tape.playing)
11387 DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
11390 if (tape.recording || tape.playing)
11391 DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
11393 UpdateAndDisplayGameControlValues();
11396 void AdvanceFrameAndPlayerCounters(int player_nr)
11400 // advance frame counters (global frame counter and time frame counter)
11404 // advance player counters (counters for move delay, move animation etc.)
11405 for (i = 0; i < MAX_PLAYERS; i++)
11407 boolean advance_player_counters = (player_nr == -1 || player_nr == i);
11408 int move_delay_value = stored_player[i].move_delay_value;
11409 int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
11411 if (!advance_player_counters) // not all players may be affected
11414 if (move_frames == 0) // less than one move per game frame
11416 int stepsize = TILEX / move_delay_value;
11417 int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
11418 int count = (stored_player[i].is_moving ?
11419 ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
11421 if (count % delay == 0)
11425 stored_player[i].Frame += move_frames;
11427 if (stored_player[i].MovPos != 0)
11428 stored_player[i].StepFrame += move_frames;
11430 if (stored_player[i].move_delay > 0)
11431 stored_player[i].move_delay--;
11433 // due to bugs in previous versions, counter must count up, not down
11434 if (stored_player[i].push_delay != -1)
11435 stored_player[i].push_delay++;
11437 if (stored_player[i].drop_delay > 0)
11438 stored_player[i].drop_delay--;
11440 if (stored_player[i].is_dropping_pressed)
11441 stored_player[i].drop_pressed_delay++;
11445 void StartGameActions(boolean init_network_game, boolean record_tape,
11448 unsigned int new_random_seed = InitRND(random_seed);
11451 TapeStartRecording(new_random_seed);
11453 if (init_network_game)
11455 SendToServer_LevelFile();
11456 SendToServer_StartPlaying();
11464 static void GameActionsExt(void)
11467 static unsigned int game_frame_delay = 0;
11469 unsigned int game_frame_delay_value;
11470 byte *recorded_player_action;
11471 byte summarized_player_action = 0;
11472 byte tape_action[MAX_TAPE_ACTIONS] = { 0 };
11475 // detect endless loops, caused by custom element programming
11476 if (recursion_loop_detected && recursion_loop_depth == 0)
11478 char *message = getStringCat3("Internal Error! Element ",
11479 EL_NAME(recursion_loop_element),
11480 " caused endless loop! Quit the game?");
11482 Error(ERR_WARN, "element '%s' caused endless loop in game engine",
11483 EL_NAME(recursion_loop_element));
11485 RequestQuitGameExt(FALSE, level_editor_test_game, message);
11487 recursion_loop_detected = FALSE; // if game should be continued
11494 if (game.restart_level)
11495 StartGameActions(network.enabled, setup.autorecord, level.random_seed);
11497 CheckLevelSolved();
11499 if (game.LevelSolved && !game.LevelSolved_GameEnd)
11502 if (game.all_players_gone && !TAPE_IS_STOPPED(tape))
11505 if (game_status != GAME_MODE_PLAYING) // status might have changed
11508 game_frame_delay_value =
11509 (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
11511 if (tape.playing && tape.warp_forward && !tape.pausing)
11512 game_frame_delay_value = 0;
11514 SetVideoFrameDelay(game_frame_delay_value);
11516 // (de)activate virtual buttons depending on current game status
11517 if (strEqual(setup.touch.control_type, TOUCH_CONTROL_VIRTUAL_BUTTONS))
11519 if (game.all_players_gone) // if no players there to be controlled anymore
11520 SetOverlayActive(FALSE);
11521 else if (!tape.playing) // if game continues after tape stopped playing
11522 SetOverlayActive(TRUE);
11527 // ---------- main game synchronization point ----------
11529 int skip = WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11531 printf("::: skip == %d\n", skip);
11534 // ---------- main game synchronization point ----------
11536 WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11540 if (network_playing && !network_player_action_received)
11542 // try to get network player actions in time
11544 // last chance to get network player actions without main loop delay
11545 HandleNetworking();
11547 // game was quit by network peer
11548 if (game_status != GAME_MODE_PLAYING)
11551 // check if network player actions still missing and game still running
11552 if (!network_player_action_received && !checkGameEnded())
11553 return; // failed to get network player actions in time
11555 // do not yet reset "network_player_action_received" (for tape.pausing)
11561 // at this point we know that we really continue executing the game
11563 network_player_action_received = FALSE;
11565 // when playing tape, read previously recorded player input from tape data
11566 recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
11568 local_player->effective_mouse_action = local_player->mouse_action;
11570 if (recorded_player_action != NULL)
11571 SetMouseActionFromTapeAction(&local_player->effective_mouse_action,
11572 recorded_player_action);
11574 // TapePlayAction() may return NULL when toggling to "pause before death"
11578 if (tape.set_centered_player)
11580 game.centered_player_nr_next = tape.centered_player_nr_next;
11581 game.set_centered_player = TRUE;
11584 for (i = 0; i < MAX_PLAYERS; i++)
11586 summarized_player_action |= stored_player[i].action;
11588 if (!network_playing && (game.team_mode || tape.playing))
11589 stored_player[i].effective_action = stored_player[i].action;
11592 if (network_playing && !checkGameEnded())
11593 SendToServer_MovePlayer(summarized_player_action);
11595 // summarize all actions at local players mapped input device position
11596 // (this allows using different input devices in single player mode)
11597 if (!network.enabled && !game.team_mode)
11598 stored_player[map_player_action[local_player->index_nr]].effective_action =
11599 summarized_player_action;
11601 // summarize all actions at centered player in local team mode
11602 if (tape.recording &&
11603 setup.team_mode && !network.enabled &&
11604 setup.input_on_focus &&
11605 game.centered_player_nr != -1)
11607 for (i = 0; i < MAX_PLAYERS; i++)
11608 stored_player[map_player_action[i]].effective_action =
11609 (i == game.centered_player_nr ? summarized_player_action : 0);
11612 if (recorded_player_action != NULL)
11613 for (i = 0; i < MAX_PLAYERS; i++)
11614 stored_player[i].effective_action = recorded_player_action[i];
11616 for (i = 0; i < MAX_PLAYERS; i++)
11618 tape_action[i] = stored_player[i].effective_action;
11620 /* (this may happen in the RND game engine if a player was not present on
11621 the playfield on level start, but appeared later from a custom element */
11622 if (setup.team_mode &&
11625 !tape.player_participates[i])
11626 tape.player_participates[i] = TRUE;
11629 SetTapeActionFromMouseAction(tape_action,
11630 &local_player->effective_mouse_action);
11632 // only record actions from input devices, but not programmed actions
11633 if (tape.recording)
11634 TapeRecordAction(tape_action);
11636 // remember if game was played (especially after tape stopped playing)
11637 if (!tape.playing && summarized_player_action)
11638 game.GamePlayed = TRUE;
11640 #if USE_NEW_PLAYER_ASSIGNMENTS
11641 // !!! also map player actions in single player mode !!!
11642 // if (game.team_mode)
11645 byte mapped_action[MAX_PLAYERS];
11647 #if DEBUG_PLAYER_ACTIONS
11649 for (i = 0; i < MAX_PLAYERS; i++)
11650 printf(" %d, ", stored_player[i].effective_action);
11653 for (i = 0; i < MAX_PLAYERS; i++)
11654 mapped_action[i] = stored_player[map_player_action[i]].effective_action;
11656 for (i = 0; i < MAX_PLAYERS; i++)
11657 stored_player[i].effective_action = mapped_action[i];
11659 #if DEBUG_PLAYER_ACTIONS
11661 for (i = 0; i < MAX_PLAYERS; i++)
11662 printf(" %d, ", stored_player[i].effective_action);
11666 #if DEBUG_PLAYER_ACTIONS
11670 for (i = 0; i < MAX_PLAYERS; i++)
11671 printf(" %d, ", stored_player[i].effective_action);
11677 for (i = 0; i < MAX_PLAYERS; i++)
11679 // allow engine snapshot in case of changed movement attempt
11680 if ((game.snapshot.last_action[i] & KEY_MOTION) !=
11681 (stored_player[i].effective_action & KEY_MOTION))
11682 game.snapshot.changed_action = TRUE;
11684 // allow engine snapshot in case of snapping/dropping attempt
11685 if ((game.snapshot.last_action[i] & KEY_BUTTON) == 0 &&
11686 (stored_player[i].effective_action & KEY_BUTTON) != 0)
11687 game.snapshot.changed_action = TRUE;
11689 game.snapshot.last_action[i] = stored_player[i].effective_action;
11692 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11694 GameActions_EM_Main();
11696 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11698 GameActions_SP_Main();
11700 else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
11702 GameActions_MM_Main();
11706 GameActions_RND_Main();
11709 BlitScreenToBitmap(backbuffer);
11711 CheckLevelSolved();
11714 AdvanceFrameAndPlayerCounters(-1); // advance counters for all players
11716 if (global.show_frames_per_second)
11718 static unsigned int fps_counter = 0;
11719 static int fps_frames = 0;
11720 unsigned int fps_delay_ms = Counter() - fps_counter;
11724 if (fps_delay_ms >= 500) // calculate FPS every 0.5 seconds
11726 global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
11729 fps_counter = Counter();
11731 // always draw FPS to screen after FPS value was updated
11732 redraw_mask |= REDRAW_FPS;
11735 // only draw FPS if no screen areas are deactivated (invisible warp mode)
11736 if (GetDrawDeactivationMask() == REDRAW_NONE)
11737 redraw_mask |= REDRAW_FPS;
11741 static void GameActions_CheckSaveEngineSnapshot(void)
11743 if (!game.snapshot.save_snapshot)
11746 // clear flag for saving snapshot _before_ saving snapshot
11747 game.snapshot.save_snapshot = FALSE;
11749 SaveEngineSnapshotToList();
11752 void GameActions(void)
11756 GameActions_CheckSaveEngineSnapshot();
11759 void GameActions_EM_Main(void)
11761 byte effective_action[MAX_PLAYERS];
11762 boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11765 for (i = 0; i < MAX_PLAYERS; i++)
11766 effective_action[i] = stored_player[i].effective_action;
11768 GameActions_EM(effective_action, warp_mode);
11771 void GameActions_SP_Main(void)
11773 byte effective_action[MAX_PLAYERS];
11774 boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11777 for (i = 0; i < MAX_PLAYERS; i++)
11778 effective_action[i] = stored_player[i].effective_action;
11780 GameActions_SP(effective_action, warp_mode);
11782 for (i = 0; i < MAX_PLAYERS; i++)
11784 if (stored_player[i].force_dropping)
11785 stored_player[i].action |= KEY_BUTTON_DROP;
11787 stored_player[i].force_dropping = FALSE;
11791 void GameActions_MM_Main(void)
11793 boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11795 GameActions_MM(local_player->effective_mouse_action, warp_mode);
11798 void GameActions_RND_Main(void)
11803 void GameActions_RND(void)
11805 static struct MouseActionInfo mouse_action_last = { 0 };
11806 struct MouseActionInfo mouse_action = local_player->effective_mouse_action;
11807 int magic_wall_x = 0, magic_wall_y = 0;
11808 int i, x, y, element, graphic, last_gfx_frame;
11810 InitPlayfieldScanModeVars();
11812 if (game.engine_version >= VERSION_IDENT(3,2,0,7))
11814 SCAN_PLAYFIELD(x, y)
11816 ChangeCount[x][y] = 0;
11817 ChangeEvent[x][y] = -1;
11821 if (game.set_centered_player)
11823 boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
11825 // switching to "all players" only possible if all players fit to screen
11826 if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
11828 game.centered_player_nr_next = game.centered_player_nr;
11829 game.set_centered_player = FALSE;
11832 // do not switch focus to non-existing (or non-active) player
11833 if (game.centered_player_nr_next >= 0 &&
11834 !stored_player[game.centered_player_nr_next].active)
11836 game.centered_player_nr_next = game.centered_player_nr;
11837 game.set_centered_player = FALSE;
11841 if (game.set_centered_player &&
11842 ScreenMovPos == 0) // screen currently aligned at tile position
11846 if (game.centered_player_nr_next == -1)
11848 setScreenCenteredToAllPlayers(&sx, &sy);
11852 sx = stored_player[game.centered_player_nr_next].jx;
11853 sy = stored_player[game.centered_player_nr_next].jy;
11856 game.centered_player_nr = game.centered_player_nr_next;
11857 game.set_centered_player = FALSE;
11859 DrawRelocateScreen(0, 0, sx, sy, MV_NONE, TRUE, setup.quick_switch);
11860 DrawGameDoorValues();
11863 for (i = 0; i < MAX_PLAYERS; i++)
11865 int actual_player_action = stored_player[i].effective_action;
11868 /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
11869 - rnd_equinox_tetrachloride 048
11870 - rnd_equinox_tetrachloride_ii 096
11871 - rnd_emanuel_schmieg 002
11872 - doctor_sloan_ww 001, 020
11874 if (stored_player[i].MovPos == 0)
11875 CheckGravityMovement(&stored_player[i]);
11878 // overwrite programmed action with tape action
11879 if (stored_player[i].programmed_action)
11880 actual_player_action = stored_player[i].programmed_action;
11882 PlayerActions(&stored_player[i], actual_player_action);
11884 ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
11887 ScrollScreen(NULL, SCROLL_GO_ON);
11889 /* for backwards compatibility, the following code emulates a fixed bug that
11890 occured when pushing elements (causing elements that just made their last
11891 pushing step to already (if possible) make their first falling step in the
11892 same game frame, which is bad); this code is also needed to use the famous
11893 "spring push bug" which is used in older levels and might be wanted to be
11894 used also in newer levels, but in this case the buggy pushing code is only
11895 affecting the "spring" element and no other elements */
11897 if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
11899 for (i = 0; i < MAX_PLAYERS; i++)
11901 struct PlayerInfo *player = &stored_player[i];
11902 int x = player->jx;
11903 int y = player->jy;
11905 if (player->active && player->is_pushing && player->is_moving &&
11907 (game.engine_version < VERSION_IDENT(2,2,0,7) ||
11908 Feld[x][y] == EL_SPRING))
11910 ContinueMoving(x, y);
11912 // continue moving after pushing (this is actually a bug)
11913 if (!IS_MOVING(x, y))
11914 Stop[x][y] = FALSE;
11919 SCAN_PLAYFIELD(x, y)
11921 Last[x][y] = Feld[x][y];
11923 ChangeCount[x][y] = 0;
11924 ChangeEvent[x][y] = -1;
11926 // this must be handled before main playfield loop
11927 if (Feld[x][y] == EL_PLAYER_IS_LEAVING)
11930 if (MovDelay[x][y] <= 0)
11934 if (Feld[x][y] == EL_ELEMENT_SNAPPING)
11937 if (MovDelay[x][y] <= 0)
11940 TEST_DrawLevelField(x, y);
11942 TestIfElementTouchesCustomElement(x, y); // for empty space
11947 if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
11949 printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
11950 printf("GameActions(): This should never happen!\n");
11952 ChangePage[x][y] = -1;
11956 Stop[x][y] = FALSE;
11957 if (WasJustMoving[x][y] > 0)
11958 WasJustMoving[x][y]--;
11959 if (WasJustFalling[x][y] > 0)
11960 WasJustFalling[x][y]--;
11961 if (CheckCollision[x][y] > 0)
11962 CheckCollision[x][y]--;
11963 if (CheckImpact[x][y] > 0)
11964 CheckImpact[x][y]--;
11968 /* reset finished pushing action (not done in ContinueMoving() to allow
11969 continuous pushing animation for elements with zero push delay) */
11970 if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
11972 ResetGfxAnimation(x, y);
11973 TEST_DrawLevelField(x, y);
11977 if (IS_BLOCKED(x, y))
11981 Blocked2Moving(x, y, &oldx, &oldy);
11982 if (!IS_MOVING(oldx, oldy))
11984 printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
11985 printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
11986 printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
11987 printf("GameActions(): This should never happen!\n");
11993 if (mouse_action.button)
11995 int new_button = (mouse_action.button && mouse_action_last.button == 0);
11997 x = mouse_action.lx;
11998 y = mouse_action.ly;
11999 element = Feld[x][y];
12003 CheckElementChange(x, y, element, EL_UNDEFINED, CE_CLICKED_BY_MOUSE);
12004 CheckTriggeredElementChange(x, y, element, CE_MOUSE_CLICKED_ON_X);
12007 CheckElementChange(x, y, element, EL_UNDEFINED, CE_PRESSED_BY_MOUSE);
12008 CheckTriggeredElementChange(x, y, element, CE_MOUSE_PRESSED_ON_X);
12011 SCAN_PLAYFIELD(x, y)
12013 element = Feld[x][y];
12014 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12015 last_gfx_frame = GfxFrame[x][y];
12017 ResetGfxFrame(x, y);
12019 if (GfxFrame[x][y] != last_gfx_frame && !Stop[x][y])
12020 DrawLevelGraphicAnimation(x, y, graphic);
12022 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
12023 IS_NEXT_FRAME(GfxFrame[x][y], graphic))
12024 ResetRandomAnimationValue(x, y);
12026 SetRandomAnimationValue(x, y);
12028 PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
12030 if (IS_INACTIVE(element))
12032 if (IS_ANIMATED(graphic))
12033 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12038 // this may take place after moving, so 'element' may have changed
12039 if (IS_CHANGING(x, y) &&
12040 (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
12042 int page = element_info[element].event_page_nr[CE_DELAY];
12044 HandleElementChange(x, y, page);
12046 element = Feld[x][y];
12047 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12050 if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
12054 element = Feld[x][y];
12055 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12057 if (IS_ANIMATED(graphic) &&
12058 !IS_MOVING(x, y) &&
12060 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12062 if (IS_GEM(element) || element == EL_SP_INFOTRON)
12063 TEST_DrawTwinkleOnField(x, y);
12065 else if (element == EL_ACID)
12068 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12070 else if ((element == EL_EXIT_OPEN ||
12071 element == EL_EM_EXIT_OPEN ||
12072 element == EL_SP_EXIT_OPEN ||
12073 element == EL_STEEL_EXIT_OPEN ||
12074 element == EL_EM_STEEL_EXIT_OPEN ||
12075 element == EL_SP_TERMINAL ||
12076 element == EL_SP_TERMINAL_ACTIVE ||
12077 element == EL_EXTRA_TIME ||
12078 element == EL_SHIELD_NORMAL ||
12079 element == EL_SHIELD_DEADLY) &&
12080 IS_ANIMATED(graphic))
12081 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12082 else if (IS_MOVING(x, y))
12083 ContinueMoving(x, y);
12084 else if (IS_ACTIVE_BOMB(element))
12085 CheckDynamite(x, y);
12086 else if (element == EL_AMOEBA_GROWING)
12087 AmoebeWaechst(x, y);
12088 else if (element == EL_AMOEBA_SHRINKING)
12089 AmoebaDisappearing(x, y);
12091 #if !USE_NEW_AMOEBA_CODE
12092 else if (IS_AMOEBALIVE(element))
12093 AmoebeAbleger(x, y);
12096 else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
12098 else if (element == EL_EXIT_CLOSED)
12100 else if (element == EL_EM_EXIT_CLOSED)
12102 else if (element == EL_STEEL_EXIT_CLOSED)
12103 CheckExitSteel(x, y);
12104 else if (element == EL_EM_STEEL_EXIT_CLOSED)
12105 CheckExitSteelEM(x, y);
12106 else if (element == EL_SP_EXIT_CLOSED)
12108 else if (element == EL_EXPANDABLE_WALL_GROWING ||
12109 element == EL_EXPANDABLE_STEELWALL_GROWING)
12110 MauerWaechst(x, y);
12111 else if (element == EL_EXPANDABLE_WALL ||
12112 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
12113 element == EL_EXPANDABLE_WALL_VERTICAL ||
12114 element == EL_EXPANDABLE_WALL_ANY ||
12115 element == EL_BD_EXPANDABLE_WALL)
12116 MauerAbleger(x, y);
12117 else if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
12118 element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
12119 element == EL_EXPANDABLE_STEELWALL_ANY)
12120 MauerAblegerStahl(x, y);
12121 else if (element == EL_FLAMES)
12122 CheckForDragon(x, y);
12123 else if (element == EL_EXPLOSION)
12124 ; // drawing of correct explosion animation is handled separately
12125 else if (element == EL_ELEMENT_SNAPPING ||
12126 element == EL_DIAGONAL_SHRINKING ||
12127 element == EL_DIAGONAL_GROWING)
12129 graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
12131 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12133 else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
12134 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12136 if (IS_BELT_ACTIVE(element))
12137 PlayLevelSoundAction(x, y, ACTION_ACTIVE);
12139 if (game.magic_wall_active)
12141 int jx = local_player->jx, jy = local_player->jy;
12143 // play the element sound at the position nearest to the player
12144 if ((element == EL_MAGIC_WALL_FULL ||
12145 element == EL_MAGIC_WALL_ACTIVE ||
12146 element == EL_MAGIC_WALL_EMPTYING ||
12147 element == EL_BD_MAGIC_WALL_FULL ||
12148 element == EL_BD_MAGIC_WALL_ACTIVE ||
12149 element == EL_BD_MAGIC_WALL_EMPTYING ||
12150 element == EL_DC_MAGIC_WALL_FULL ||
12151 element == EL_DC_MAGIC_WALL_ACTIVE ||
12152 element == EL_DC_MAGIC_WALL_EMPTYING) &&
12153 ABS(x - jx) + ABS(y - jy) <
12154 ABS(magic_wall_x - jx) + ABS(magic_wall_y - jy))
12162 #if USE_NEW_AMOEBA_CODE
12163 // new experimental amoeba growth stuff
12164 if (!(FrameCounter % 8))
12166 static unsigned int random = 1684108901;
12168 for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
12170 x = RND(lev_fieldx);
12171 y = RND(lev_fieldy);
12172 element = Feld[x][y];
12174 if (!IS_PLAYER(x,y) &&
12175 (element == EL_EMPTY ||
12176 CAN_GROW_INTO(element) ||
12177 element == EL_QUICKSAND_EMPTY ||
12178 element == EL_QUICKSAND_FAST_EMPTY ||
12179 element == EL_ACID_SPLASH_LEFT ||
12180 element == EL_ACID_SPLASH_RIGHT))
12182 if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
12183 (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
12184 (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
12185 (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
12186 Feld[x][y] = EL_AMOEBA_DROP;
12189 random = random * 129 + 1;
12194 game.explosions_delayed = FALSE;
12196 SCAN_PLAYFIELD(x, y)
12198 element = Feld[x][y];
12200 if (ExplodeField[x][y])
12201 Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
12202 else if (element == EL_EXPLOSION)
12203 Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
12205 ExplodeField[x][y] = EX_TYPE_NONE;
12208 game.explosions_delayed = TRUE;
12210 if (game.magic_wall_active)
12212 if (!(game.magic_wall_time_left % 4))
12214 int element = Feld[magic_wall_x][magic_wall_y];
12216 if (element == EL_BD_MAGIC_WALL_FULL ||
12217 element == EL_BD_MAGIC_WALL_ACTIVE ||
12218 element == EL_BD_MAGIC_WALL_EMPTYING)
12219 PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
12220 else if (element == EL_DC_MAGIC_WALL_FULL ||
12221 element == EL_DC_MAGIC_WALL_ACTIVE ||
12222 element == EL_DC_MAGIC_WALL_EMPTYING)
12223 PlayLevelSound(magic_wall_x, magic_wall_y, SND_DC_MAGIC_WALL_ACTIVE);
12225 PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
12228 if (game.magic_wall_time_left > 0)
12230 game.magic_wall_time_left--;
12232 if (!game.magic_wall_time_left)
12234 SCAN_PLAYFIELD(x, y)
12236 element = Feld[x][y];
12238 if (element == EL_MAGIC_WALL_ACTIVE ||
12239 element == EL_MAGIC_WALL_FULL)
12241 Feld[x][y] = EL_MAGIC_WALL_DEAD;
12242 TEST_DrawLevelField(x, y);
12244 else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
12245 element == EL_BD_MAGIC_WALL_FULL)
12247 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
12248 TEST_DrawLevelField(x, y);
12250 else if (element == EL_DC_MAGIC_WALL_ACTIVE ||
12251 element == EL_DC_MAGIC_WALL_FULL)
12253 Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
12254 TEST_DrawLevelField(x, y);
12258 game.magic_wall_active = FALSE;
12263 if (game.light_time_left > 0)
12265 game.light_time_left--;
12267 if (game.light_time_left == 0)
12268 RedrawAllLightSwitchesAndInvisibleElements();
12271 if (game.timegate_time_left > 0)
12273 game.timegate_time_left--;
12275 if (game.timegate_time_left == 0)
12276 CloseAllOpenTimegates();
12279 if (game.lenses_time_left > 0)
12281 game.lenses_time_left--;
12283 if (game.lenses_time_left == 0)
12284 RedrawAllInvisibleElementsForLenses();
12287 if (game.magnify_time_left > 0)
12289 game.magnify_time_left--;
12291 if (game.magnify_time_left == 0)
12292 RedrawAllInvisibleElementsForMagnifier();
12295 for (i = 0; i < MAX_PLAYERS; i++)
12297 struct PlayerInfo *player = &stored_player[i];
12299 if (SHIELD_ON(player))
12301 if (player->shield_deadly_time_left)
12302 PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
12303 else if (player->shield_normal_time_left)
12304 PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
12308 #if USE_DELAYED_GFX_REDRAW
12309 SCAN_PLAYFIELD(x, y)
12311 if (GfxRedraw[x][y] != GFX_REDRAW_NONE)
12313 /* !!! PROBLEM: THIS REDRAWS THE PLAYFIELD _AFTER_ THE SCAN, BUT TILES
12314 !!! MAY HAVE CHANGED AFTER BEING DRAWN DURING PLAYFIELD SCAN !!! */
12316 if (GfxRedraw[x][y] & GFX_REDRAW_TILE)
12317 DrawLevelField(x, y);
12319 if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED)
12320 DrawLevelFieldCrumbled(x, y);
12322 if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS)
12323 DrawLevelFieldCrumbledNeighbours(x, y);
12325 if (GfxRedraw[x][y] & GFX_REDRAW_TILE_TWINKLED)
12326 DrawTwinkleOnField(x, y);
12329 GfxRedraw[x][y] = GFX_REDRAW_NONE;
12334 PlayAllPlayersSound();
12336 for (i = 0; i < MAX_PLAYERS; i++)
12338 struct PlayerInfo *player = &stored_player[i];
12340 if (player->show_envelope != 0 && (!player->active ||
12341 player->MovPos == 0))
12343 ShowEnvelope(player->show_envelope - EL_ENVELOPE_1);
12345 player->show_envelope = 0;
12349 // use random number generator in every frame to make it less predictable
12350 if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12353 mouse_action_last = mouse_action;
12356 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
12358 int min_x = x, min_y = y, max_x = x, max_y = y;
12361 for (i = 0; i < MAX_PLAYERS; i++)
12363 int jx = stored_player[i].jx, jy = stored_player[i].jy;
12365 if (!stored_player[i].active || &stored_player[i] == player)
12368 min_x = MIN(min_x, jx);
12369 min_y = MIN(min_y, jy);
12370 max_x = MAX(max_x, jx);
12371 max_y = MAX(max_y, jy);
12374 return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
12377 static boolean AllPlayersInVisibleScreen(void)
12381 for (i = 0; i < MAX_PLAYERS; i++)
12383 int jx = stored_player[i].jx, jy = stored_player[i].jy;
12385 if (!stored_player[i].active)
12388 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12395 void ScrollLevel(int dx, int dy)
12397 int scroll_offset = 2 * TILEX_VAR;
12400 BlitBitmap(drawto_field, drawto_field,
12401 FX + TILEX_VAR * (dx == -1) - scroll_offset,
12402 FY + TILEY_VAR * (dy == -1) - scroll_offset,
12403 SXSIZE - TILEX_VAR * (dx != 0) + 2 * scroll_offset,
12404 SYSIZE - TILEY_VAR * (dy != 0) + 2 * scroll_offset,
12405 FX + TILEX_VAR * (dx == 1) - scroll_offset,
12406 FY + TILEY_VAR * (dy == 1) - scroll_offset);
12410 x = (dx == 1 ? BX1 : BX2);
12411 for (y = BY1; y <= BY2; y++)
12412 DrawScreenField(x, y);
12417 y = (dy == 1 ? BY1 : BY2);
12418 for (x = BX1; x <= BX2; x++)
12419 DrawScreenField(x, y);
12422 redraw_mask |= REDRAW_FIELD;
12425 static boolean canFallDown(struct PlayerInfo *player)
12427 int jx = player->jx, jy = player->jy;
12429 return (IN_LEV_FIELD(jx, jy + 1) &&
12430 (IS_FREE(jx, jy + 1) ||
12431 (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
12432 IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
12433 !IS_WALKABLE_INSIDE(Feld[jx][jy]));
12436 static boolean canPassField(int x, int y, int move_dir)
12438 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12439 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12440 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
12441 int nextx = x + dx;
12442 int nexty = y + dy;
12443 int element = Feld[x][y];
12445 return (IS_PASSABLE_FROM(element, opposite_dir) &&
12446 !CAN_MOVE(element) &&
12447 IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
12448 IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
12449 (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
12452 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
12454 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12455 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12456 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
12460 return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
12461 IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
12462 (IS_DIGGABLE(Feld[newx][newy]) ||
12463 IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
12464 canPassField(newx, newy, move_dir)));
12467 static void CheckGravityMovement(struct PlayerInfo *player)
12469 if (player->gravity && !player->programmed_action)
12471 int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
12472 int move_dir_vertical = player->effective_action & MV_VERTICAL;
12473 boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
12474 int jx = player->jx, jy = player->jy;
12475 boolean player_is_moving_to_valid_field =
12476 (!player_is_snapping &&
12477 (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
12478 canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
12479 boolean player_can_fall_down = canFallDown(player);
12481 if (player_can_fall_down &&
12482 !player_is_moving_to_valid_field)
12483 player->programmed_action = MV_DOWN;
12487 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
12489 return CheckGravityMovement(player);
12491 if (player->gravity && !player->programmed_action)
12493 int jx = player->jx, jy = player->jy;
12494 boolean field_under_player_is_free =
12495 (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
12496 boolean player_is_standing_on_valid_field =
12497 (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
12498 (IS_WALKABLE(Feld[jx][jy]) &&
12499 !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
12501 if (field_under_player_is_free && !player_is_standing_on_valid_field)
12502 player->programmed_action = MV_DOWN;
12507 MovePlayerOneStep()
12508 -----------------------------------------------------------------------------
12509 dx, dy: direction (non-diagonal) to try to move the player to
12510 real_dx, real_dy: direction as read from input device (can be diagonal)
12513 boolean MovePlayerOneStep(struct PlayerInfo *player,
12514 int dx, int dy, int real_dx, int real_dy)
12516 int jx = player->jx, jy = player->jy;
12517 int new_jx = jx + dx, new_jy = jy + dy;
12519 boolean player_can_move = !player->cannot_move;
12521 if (!player->active || (!dx && !dy))
12522 return MP_NO_ACTION;
12524 player->MovDir = (dx < 0 ? MV_LEFT :
12525 dx > 0 ? MV_RIGHT :
12527 dy > 0 ? MV_DOWN : MV_NONE);
12529 if (!IN_LEV_FIELD(new_jx, new_jy))
12530 return MP_NO_ACTION;
12532 if (!player_can_move)
12534 if (player->MovPos == 0)
12536 player->is_moving = FALSE;
12537 player->is_digging = FALSE;
12538 player->is_collecting = FALSE;
12539 player->is_snapping = FALSE;
12540 player->is_pushing = FALSE;
12544 if (!network.enabled && game.centered_player_nr == -1 &&
12545 !AllPlayersInSight(player, new_jx, new_jy))
12546 return MP_NO_ACTION;
12548 can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
12549 if (can_move != MP_MOVING)
12552 // check if DigField() has caused relocation of the player
12553 if (player->jx != jx || player->jy != jy)
12554 return MP_NO_ACTION; // <-- !!! CHECK THIS [-> MP_ACTION ?] !!!
12556 StorePlayer[jx][jy] = 0;
12557 player->last_jx = jx;
12558 player->last_jy = jy;
12559 player->jx = new_jx;
12560 player->jy = new_jy;
12561 StorePlayer[new_jx][new_jy] = player->element_nr;
12563 if (player->move_delay_value_next != -1)
12565 player->move_delay_value = player->move_delay_value_next;
12566 player->move_delay_value_next = -1;
12570 (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
12572 player->step_counter++;
12574 PlayerVisit[jx][jy] = FrameCounter;
12576 player->is_moving = TRUE;
12579 // should better be called in MovePlayer(), but this breaks some tapes
12580 ScrollPlayer(player, SCROLL_INIT);
12586 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
12588 int jx = player->jx, jy = player->jy;
12589 int old_jx = jx, old_jy = jy;
12590 int moved = MP_NO_ACTION;
12592 if (!player->active)
12597 if (player->MovPos == 0)
12599 player->is_moving = FALSE;
12600 player->is_digging = FALSE;
12601 player->is_collecting = FALSE;
12602 player->is_snapping = FALSE;
12603 player->is_pushing = FALSE;
12609 if (player->move_delay > 0)
12612 player->move_delay = -1; // set to "uninitialized" value
12614 // store if player is automatically moved to next field
12615 player->is_auto_moving = (player->programmed_action != MV_NONE);
12617 // remove the last programmed player action
12618 player->programmed_action = 0;
12620 if (player->MovPos)
12622 // should only happen if pre-1.2 tape recordings are played
12623 // this is only for backward compatibility
12625 int original_move_delay_value = player->move_delay_value;
12628 printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%d]\n",
12632 // scroll remaining steps with finest movement resolution
12633 player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
12635 while (player->MovPos)
12637 ScrollPlayer(player, SCROLL_GO_ON);
12638 ScrollScreen(NULL, SCROLL_GO_ON);
12640 AdvanceFrameAndPlayerCounters(player->index_nr);
12643 BackToFront_WithFrameDelay(0);
12646 player->move_delay_value = original_move_delay_value;
12649 player->is_active = FALSE;
12651 if (player->last_move_dir & MV_HORIZONTAL)
12653 if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
12654 moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
12658 if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
12659 moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
12662 if (!moved && !player->is_active)
12664 player->is_moving = FALSE;
12665 player->is_digging = FALSE;
12666 player->is_collecting = FALSE;
12667 player->is_snapping = FALSE;
12668 player->is_pushing = FALSE;
12674 if (moved & MP_MOVING && !ScreenMovPos &&
12675 (player->index_nr == game.centered_player_nr ||
12676 game.centered_player_nr == -1))
12678 int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
12680 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12682 // actual player has left the screen -- scroll in that direction
12683 if (jx != old_jx) // player has moved horizontally
12684 scroll_x += (jx - old_jx);
12685 else // player has moved vertically
12686 scroll_y += (jy - old_jy);
12690 int offset_raw = game.scroll_delay_value;
12692 if (jx != old_jx) // player has moved horizontally
12694 int offset = MIN(offset_raw, (SCR_FIELDX - 2) / 2);
12695 int offset_x = offset * (player->MovDir == MV_LEFT ? +1 : -1);
12696 int new_scroll_x = jx - MIDPOSX + offset_x;
12698 if ((player->MovDir == MV_LEFT && scroll_x > new_scroll_x) ||
12699 (player->MovDir == MV_RIGHT && scroll_x < new_scroll_x))
12700 scroll_x = new_scroll_x;
12702 // don't scroll over playfield boundaries
12703 scroll_x = MIN(MAX(SBX_Left, scroll_x), SBX_Right);
12705 // don't scroll more than one field at a time
12706 scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
12708 // don't scroll against the player's moving direction
12709 if ((player->MovDir == MV_LEFT && scroll_x > old_scroll_x) ||
12710 (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
12711 scroll_x = old_scroll_x;
12713 else // player has moved vertically
12715 int offset = MIN(offset_raw, (SCR_FIELDY - 2) / 2);
12716 int offset_y = offset * (player->MovDir == MV_UP ? +1 : -1);
12717 int new_scroll_y = jy - MIDPOSY + offset_y;
12719 if ((player->MovDir == MV_UP && scroll_y > new_scroll_y) ||
12720 (player->MovDir == MV_DOWN && scroll_y < new_scroll_y))
12721 scroll_y = new_scroll_y;
12723 // don't scroll over playfield boundaries
12724 scroll_y = MIN(MAX(SBY_Upper, scroll_y), SBY_Lower);
12726 // don't scroll more than one field at a time
12727 scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
12729 // don't scroll against the player's moving direction
12730 if ((player->MovDir == MV_UP && scroll_y > old_scroll_y) ||
12731 (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
12732 scroll_y = old_scroll_y;
12736 if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
12738 if (!network.enabled && game.centered_player_nr == -1 &&
12739 !AllPlayersInVisibleScreen())
12741 scroll_x = old_scroll_x;
12742 scroll_y = old_scroll_y;
12746 ScrollScreen(player, SCROLL_INIT);
12747 ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
12752 player->StepFrame = 0;
12754 if (moved & MP_MOVING)
12756 if (old_jx != jx && old_jy == jy)
12757 player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
12758 else if (old_jx == jx && old_jy != jy)
12759 player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
12761 TEST_DrawLevelField(jx, jy); // for "crumbled sand"
12763 player->last_move_dir = player->MovDir;
12764 player->is_moving = TRUE;
12765 player->is_snapping = FALSE;
12766 player->is_switching = FALSE;
12767 player->is_dropping = FALSE;
12768 player->is_dropping_pressed = FALSE;
12769 player->drop_pressed_delay = 0;
12772 // should better be called here than above, but this breaks some tapes
12773 ScrollPlayer(player, SCROLL_INIT);
12778 CheckGravityMovementWhenNotMoving(player);
12780 player->is_moving = FALSE;
12782 /* at this point, the player is allowed to move, but cannot move right now
12783 (e.g. because of something blocking the way) -- ensure that the player
12784 is also allowed to move in the next frame (in old versions before 3.1.1,
12785 the player was forced to wait again for eight frames before next try) */
12787 if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12788 player->move_delay = 0; // allow direct movement in the next frame
12791 if (player->move_delay == -1) // not yet initialized by DigField()
12792 player->move_delay = player->move_delay_value;
12794 if (game.engine_version < VERSION_IDENT(3,0,7,0))
12796 TestIfPlayerTouchesBadThing(jx, jy);
12797 TestIfPlayerTouchesCustomElement(jx, jy);
12800 if (!player->active)
12801 RemovePlayer(player);
12806 void ScrollPlayer(struct PlayerInfo *player, int mode)
12808 int jx = player->jx, jy = player->jy;
12809 int last_jx = player->last_jx, last_jy = player->last_jy;
12810 int move_stepsize = TILEX / player->move_delay_value;
12812 if (!player->active)
12815 if (player->MovPos == 0 && mode == SCROLL_GO_ON) // player not moving
12818 if (mode == SCROLL_INIT)
12820 player->actual_frame_counter = FrameCounter;
12821 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
12823 if ((player->block_last_field || player->block_delay_adjustment > 0) &&
12824 Feld[last_jx][last_jy] == EL_EMPTY)
12826 int last_field_block_delay = 0; // start with no blocking at all
12827 int block_delay_adjustment = player->block_delay_adjustment;
12829 // if player blocks last field, add delay for exactly one move
12830 if (player->block_last_field)
12832 last_field_block_delay += player->move_delay_value;
12834 // when blocking enabled, prevent moving up despite gravity
12835 if (player->gravity && player->MovDir == MV_UP)
12836 block_delay_adjustment = -1;
12839 // add block delay adjustment (also possible when not blocking)
12840 last_field_block_delay += block_delay_adjustment;
12842 Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
12843 MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
12846 if (player->MovPos != 0) // player has not yet reached destination
12849 else if (!FrameReached(&player->actual_frame_counter, 1))
12852 if (player->MovPos != 0)
12854 player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
12855 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
12857 // before DrawPlayer() to draw correct player graphic for this case
12858 if (player->MovPos == 0)
12859 CheckGravityMovement(player);
12862 if (player->MovPos == 0) // player reached destination field
12864 if (player->move_delay_reset_counter > 0)
12866 player->move_delay_reset_counter--;
12868 if (player->move_delay_reset_counter == 0)
12870 // continue with normal speed after quickly moving through gate
12871 HALVE_PLAYER_SPEED(player);
12873 // be able to make the next move without delay
12874 player->move_delay = 0;
12878 player->last_jx = jx;
12879 player->last_jy = jy;
12881 if (Feld[jx][jy] == EL_EXIT_OPEN ||
12882 Feld[jx][jy] == EL_EM_EXIT_OPEN ||
12883 Feld[jx][jy] == EL_EM_EXIT_OPENING ||
12884 Feld[jx][jy] == EL_STEEL_EXIT_OPEN ||
12885 Feld[jx][jy] == EL_EM_STEEL_EXIT_OPEN ||
12886 Feld[jx][jy] == EL_EM_STEEL_EXIT_OPENING ||
12887 Feld[jx][jy] == EL_SP_EXIT_OPEN ||
12888 Feld[jx][jy] == EL_SP_EXIT_OPENING) // <-- special case
12890 ExitPlayer(player);
12892 if (game.players_still_needed == 0 &&
12893 (game.friends_still_needed == 0 ||
12894 IS_SP_ELEMENT(Feld[jx][jy])))
12898 // this breaks one level: "machine", level 000
12900 int move_direction = player->MovDir;
12901 int enter_side = MV_DIR_OPPOSITE(move_direction);
12902 int leave_side = move_direction;
12903 int old_jx = last_jx;
12904 int old_jy = last_jy;
12905 int old_element = Feld[old_jx][old_jy];
12906 int new_element = Feld[jx][jy];
12908 if (IS_CUSTOM_ELEMENT(old_element))
12909 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
12911 player->index_bit, leave_side);
12913 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
12914 CE_PLAYER_LEAVES_X,
12915 player->index_bit, leave_side);
12917 if (IS_CUSTOM_ELEMENT(new_element))
12918 CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
12919 player->index_bit, enter_side);
12921 CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
12922 CE_PLAYER_ENTERS_X,
12923 player->index_bit, enter_side);
12925 CheckTriggeredElementChangeBySide(jx, jy, player->initial_element,
12926 CE_MOVE_OF_X, move_direction);
12929 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
12931 TestIfPlayerTouchesBadThing(jx, jy);
12932 TestIfPlayerTouchesCustomElement(jx, jy);
12934 /* needed because pushed element has not yet reached its destination,
12935 so it would trigger a change event at its previous field location */
12936 if (!player->is_pushing)
12937 TestIfElementTouchesCustomElement(jx, jy); // for empty space
12939 if (!player->active)
12940 RemovePlayer(player);
12943 if (!game.LevelSolved && level.use_step_counter)
12953 if (TimeLeft <= 10 && setup.time_limit)
12954 PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
12956 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
12958 DisplayGameControlValues();
12960 if (!TimeLeft && setup.time_limit)
12961 for (i = 0; i < MAX_PLAYERS; i++)
12962 KillPlayer(&stored_player[i]);
12964 else if (game.no_time_limit && !game.all_players_gone)
12966 game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
12968 DisplayGameControlValues();
12972 if (tape.single_step && tape.recording && !tape.pausing &&
12973 !player->programmed_action)
12974 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
12976 if (!player->programmed_action)
12977 CheckSaveEngineSnapshot(player);
12981 void ScrollScreen(struct PlayerInfo *player, int mode)
12983 static unsigned int screen_frame_counter = 0;
12985 if (mode == SCROLL_INIT)
12987 // set scrolling step size according to actual player's moving speed
12988 ScrollStepSize = TILEX / player->move_delay_value;
12990 screen_frame_counter = FrameCounter;
12991 ScreenMovDir = player->MovDir;
12992 ScreenMovPos = player->MovPos;
12993 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
12996 else if (!FrameReached(&screen_frame_counter, 1))
13001 ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
13002 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
13003 redraw_mask |= REDRAW_FIELD;
13006 ScreenMovDir = MV_NONE;
13009 void TestIfPlayerTouchesCustomElement(int x, int y)
13011 static int xy[4][2] =
13018 static int trigger_sides[4][2] =
13020 // center side border side
13021 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, // check top
13022 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, // check left
13023 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, // check right
13024 { CH_SIDE_BOTTOM, CH_SIDE_TOP } // check bottom
13026 static int touch_dir[4] =
13028 MV_LEFT | MV_RIGHT,
13033 int center_element = Feld[x][y]; // should always be non-moving!
13036 for (i = 0; i < NUM_DIRECTIONS; i++)
13038 int xx = x + xy[i][0];
13039 int yy = y + xy[i][1];
13040 int center_side = trigger_sides[i][0];
13041 int border_side = trigger_sides[i][1];
13042 int border_element;
13044 if (!IN_LEV_FIELD(xx, yy))
13047 if (IS_PLAYER(x, y)) // player found at center element
13049 struct PlayerInfo *player = PLAYERINFO(x, y);
13051 if (game.engine_version < VERSION_IDENT(3,0,7,0))
13052 border_element = Feld[xx][yy]; // may be moving!
13053 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
13054 border_element = Feld[xx][yy];
13055 else if (MovDir[xx][yy] & touch_dir[i]) // elements are touching
13056 border_element = MovingOrBlocked2Element(xx, yy);
13058 continue; // center and border element do not touch
13060 CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
13061 player->index_bit, border_side);
13062 CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
13063 CE_PLAYER_TOUCHES_X,
13064 player->index_bit, border_side);
13067 /* use player element that is initially defined in the level playfield,
13068 not the player element that corresponds to the runtime player number
13069 (example: a level that contains EL_PLAYER_3 as the only player would
13070 incorrectly give EL_PLAYER_1 for "player->element_nr") */
13071 int player_element = PLAYERINFO(x, y)->initial_element;
13073 CheckElementChangeBySide(xx, yy, border_element, player_element,
13074 CE_TOUCHING_X, border_side);
13077 else if (IS_PLAYER(xx, yy)) // player found at border element
13079 struct PlayerInfo *player = PLAYERINFO(xx, yy);
13081 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13083 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
13084 continue; // center and border element do not touch
13087 CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
13088 player->index_bit, center_side);
13089 CheckTriggeredElementChangeByPlayer(x, y, center_element,
13090 CE_PLAYER_TOUCHES_X,
13091 player->index_bit, center_side);
13094 /* use player element that is initially defined in the level playfield,
13095 not the player element that corresponds to the runtime player number
13096 (example: a level that contains EL_PLAYER_3 as the only player would
13097 incorrectly give EL_PLAYER_1 for "player->element_nr") */
13098 int player_element = PLAYERINFO(xx, yy)->initial_element;
13100 CheckElementChangeBySide(x, y, center_element, player_element,
13101 CE_TOUCHING_X, center_side);
13109 void TestIfElementTouchesCustomElement(int x, int y)
13111 static int xy[4][2] =
13118 static int trigger_sides[4][2] =
13120 // center side border side
13121 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, // check top
13122 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, // check left
13123 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, // check right
13124 { CH_SIDE_BOTTOM, CH_SIDE_TOP } // check bottom
13126 static int touch_dir[4] =
13128 MV_LEFT | MV_RIGHT,
13133 boolean change_center_element = FALSE;
13134 int center_element = Feld[x][y]; // should always be non-moving!
13135 int border_element_old[NUM_DIRECTIONS];
13138 for (i = 0; i < NUM_DIRECTIONS; i++)
13140 int xx = x + xy[i][0];
13141 int yy = y + xy[i][1];
13142 int border_element;
13144 border_element_old[i] = -1;
13146 if (!IN_LEV_FIELD(xx, yy))
13149 if (game.engine_version < VERSION_IDENT(3,0,7,0))
13150 border_element = Feld[xx][yy]; // may be moving!
13151 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
13152 border_element = Feld[xx][yy];
13153 else if (MovDir[xx][yy] & touch_dir[i]) // elements are touching
13154 border_element = MovingOrBlocked2Element(xx, yy);
13156 continue; // center and border element do not touch
13158 border_element_old[i] = border_element;
13161 for (i = 0; i < NUM_DIRECTIONS; i++)
13163 int xx = x + xy[i][0];
13164 int yy = y + xy[i][1];
13165 int center_side = trigger_sides[i][0];
13166 int border_element = border_element_old[i];
13168 if (border_element == -1)
13171 // check for change of border element
13172 CheckElementChangeBySide(xx, yy, border_element, center_element,
13173 CE_TOUCHING_X, center_side);
13175 // (center element cannot be player, so we dont have to check this here)
13178 for (i = 0; i < NUM_DIRECTIONS; i++)
13180 int xx = x + xy[i][0];
13181 int yy = y + xy[i][1];
13182 int border_side = trigger_sides[i][1];
13183 int border_element = border_element_old[i];
13185 if (border_element == -1)
13188 // check for change of center element (but change it only once)
13189 if (!change_center_element)
13190 change_center_element =
13191 CheckElementChangeBySide(x, y, center_element, border_element,
13192 CE_TOUCHING_X, border_side);
13194 if (IS_PLAYER(xx, yy))
13196 /* use player element that is initially defined in the level playfield,
13197 not the player element that corresponds to the runtime player number
13198 (example: a level that contains EL_PLAYER_3 as the only player would
13199 incorrectly give EL_PLAYER_1 for "player->element_nr") */
13200 int player_element = PLAYERINFO(xx, yy)->initial_element;
13202 CheckElementChangeBySide(x, y, center_element, player_element,
13203 CE_TOUCHING_X, border_side);
13208 void TestIfElementHitsCustomElement(int x, int y, int direction)
13210 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
13211 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
13212 int hitx = x + dx, hity = y + dy;
13213 int hitting_element = Feld[x][y];
13214 int touched_element;
13216 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
13219 touched_element = (IN_LEV_FIELD(hitx, hity) ?
13220 MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
13222 if (IN_LEV_FIELD(hitx, hity))
13224 int opposite_direction = MV_DIR_OPPOSITE(direction);
13225 int hitting_side = direction;
13226 int touched_side = opposite_direction;
13227 boolean object_hit = (!IS_MOVING(hitx, hity) ||
13228 MovDir[hitx][hity] != direction ||
13229 ABS(MovPos[hitx][hity]) <= TILEY / 2);
13235 CheckElementChangeBySide(x, y, hitting_element, touched_element,
13236 CE_HITTING_X, touched_side);
13238 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13239 CE_HIT_BY_X, hitting_side);
13241 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13242 CE_HIT_BY_SOMETHING, opposite_direction);
13244 if (IS_PLAYER(hitx, hity))
13246 /* use player element that is initially defined in the level playfield,
13247 not the player element that corresponds to the runtime player number
13248 (example: a level that contains EL_PLAYER_3 as the only player would
13249 incorrectly give EL_PLAYER_1 for "player->element_nr") */
13250 int player_element = PLAYERINFO(hitx, hity)->initial_element;
13252 CheckElementChangeBySide(x, y, hitting_element, player_element,
13253 CE_HITTING_X, touched_side);
13258 // "hitting something" is also true when hitting the playfield border
13259 CheckElementChangeBySide(x, y, hitting_element, touched_element,
13260 CE_HITTING_SOMETHING, direction);
13263 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
13265 int i, kill_x = -1, kill_y = -1;
13267 int bad_element = -1;
13268 static int test_xy[4][2] =
13275 static int test_dir[4] =
13283 for (i = 0; i < NUM_DIRECTIONS; i++)
13285 int test_x, test_y, test_move_dir, test_element;
13287 test_x = good_x + test_xy[i][0];
13288 test_y = good_y + test_xy[i][1];
13290 if (!IN_LEV_FIELD(test_x, test_y))
13294 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13296 test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
13298 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13299 2nd case: DONT_TOUCH style bad thing does not move away from good thing
13301 if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
13302 (DONT_TOUCH(test_element) && test_move_dir != test_dir[i]))
13306 bad_element = test_element;
13312 if (kill_x != -1 || kill_y != -1)
13314 if (IS_PLAYER(good_x, good_y))
13316 struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
13318 if (player->shield_deadly_time_left > 0 &&
13319 !IS_INDESTRUCTIBLE(bad_element))
13320 Bang(kill_x, kill_y);
13321 else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
13322 KillPlayer(player);
13325 Bang(good_x, good_y);
13329 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
13331 int i, kill_x = -1, kill_y = -1;
13332 int bad_element = Feld[bad_x][bad_y];
13333 static int test_xy[4][2] =
13340 static int touch_dir[4] =
13342 MV_LEFT | MV_RIGHT,
13347 static int test_dir[4] =
13355 if (bad_element == EL_EXPLOSION) // skip just exploding bad things
13358 for (i = 0; i < NUM_DIRECTIONS; i++)
13360 int test_x, test_y, test_move_dir, test_element;
13362 test_x = bad_x + test_xy[i][0];
13363 test_y = bad_y + test_xy[i][1];
13365 if (!IN_LEV_FIELD(test_x, test_y))
13369 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13371 test_element = Feld[test_x][test_y];
13373 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13374 2nd case: DONT_TOUCH style bad thing does not move away from good thing
13376 if ((DONT_RUN_INTO(bad_element) && bad_move_dir == test_dir[i]) ||
13377 (DONT_TOUCH(bad_element) && test_move_dir != test_dir[i]))
13379 // good thing is player or penguin that does not move away
13380 if (IS_PLAYER(test_x, test_y))
13382 struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
13384 if (bad_element == EL_ROBOT && player->is_moving)
13385 continue; // robot does not kill player if he is moving
13387 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13389 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
13390 continue; // center and border element do not touch
13398 else if (test_element == EL_PENGUIN)
13408 if (kill_x != -1 || kill_y != -1)
13410 if (IS_PLAYER(kill_x, kill_y))
13412 struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
13414 if (player->shield_deadly_time_left > 0 &&
13415 !IS_INDESTRUCTIBLE(bad_element))
13416 Bang(bad_x, bad_y);
13417 else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
13418 KillPlayer(player);
13421 Bang(kill_x, kill_y);
13425 void TestIfGoodThingGetsHitByBadThing(int bad_x, int bad_y, int bad_move_dir)
13427 int bad_element = Feld[bad_x][bad_y];
13428 int dx = (bad_move_dir == MV_LEFT ? -1 : bad_move_dir == MV_RIGHT ? +1 : 0);
13429 int dy = (bad_move_dir == MV_UP ? -1 : bad_move_dir == MV_DOWN ? +1 : 0);
13430 int test_x = bad_x + dx, test_y = bad_y + dy;
13431 int test_move_dir, test_element;
13432 int kill_x = -1, kill_y = -1;
13434 if (!IN_LEV_FIELD(test_x, test_y))
13438 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13440 test_element = Feld[test_x][test_y];
13442 if (test_move_dir != bad_move_dir)
13444 // good thing can be player or penguin that does not move away
13445 if (IS_PLAYER(test_x, test_y))
13447 struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
13449 /* (note: in comparison to DONT_RUN_TO and DONT_TOUCH, also handle the
13450 player as being hit when he is moving towards the bad thing, because
13451 the "get hit by" condition would be lost after the player stops) */
13452 if (player->MovPos != 0 && player->MovDir == bad_move_dir)
13453 return; // player moves away from bad thing
13458 else if (test_element == EL_PENGUIN)
13465 if (kill_x != -1 || kill_y != -1)
13467 if (IS_PLAYER(kill_x, kill_y))
13469 struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
13471 if (player->shield_deadly_time_left > 0 &&
13472 !IS_INDESTRUCTIBLE(bad_element))
13473 Bang(bad_x, bad_y);
13474 else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
13475 KillPlayer(player);
13478 Bang(kill_x, kill_y);
13482 void TestIfPlayerTouchesBadThing(int x, int y)
13484 TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13487 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
13489 TestIfGoodThingHitsBadThing(x, y, move_dir);
13492 void TestIfBadThingTouchesPlayer(int x, int y)
13494 TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13497 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
13499 TestIfBadThingHitsGoodThing(x, y, move_dir);
13502 void TestIfFriendTouchesBadThing(int x, int y)
13504 TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13507 void TestIfBadThingTouchesFriend(int x, int y)
13509 TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13512 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
13514 int i, kill_x = bad_x, kill_y = bad_y;
13515 static int xy[4][2] =
13523 for (i = 0; i < NUM_DIRECTIONS; i++)
13527 x = bad_x + xy[i][0];
13528 y = bad_y + xy[i][1];
13529 if (!IN_LEV_FIELD(x, y))
13532 element = Feld[x][y];
13533 if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
13534 element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
13542 if (kill_x != bad_x || kill_y != bad_y)
13543 Bang(bad_x, bad_y);
13546 void KillPlayer(struct PlayerInfo *player)
13548 int jx = player->jx, jy = player->jy;
13550 if (!player->active)
13554 printf("::: 0: killed == %d, active == %d, reanimated == %d\n",
13555 player->killed, player->active, player->reanimated);
13558 /* the following code was introduced to prevent an infinite loop when calling
13560 -> CheckTriggeredElementChangeExt()
13561 -> ExecuteCustomElementAction()
13563 -> (infinitely repeating the above sequence of function calls)
13564 which occurs when killing the player while having a CE with the setting
13565 "kill player X when explosion of <player X>"; the solution using a new
13566 field "player->killed" was chosen for backwards compatibility, although
13567 clever use of the fields "player->active" etc. would probably also work */
13569 if (player->killed)
13573 player->killed = TRUE;
13575 // remove accessible field at the player's position
13576 Feld[jx][jy] = EL_EMPTY;
13578 // deactivate shield (else Bang()/Explode() would not work right)
13579 player->shield_normal_time_left = 0;
13580 player->shield_deadly_time_left = 0;
13583 printf("::: 1: killed == %d, active == %d, reanimated == %d\n",
13584 player->killed, player->active, player->reanimated);
13590 printf("::: 2: killed == %d, active == %d, reanimated == %d\n",
13591 player->killed, player->active, player->reanimated);
13594 if (player->reanimated) // killed player may have been reanimated
13595 player->killed = player->reanimated = FALSE;
13597 BuryPlayer(player);
13600 static void KillPlayerUnlessEnemyProtected(int x, int y)
13602 if (!PLAYER_ENEMY_PROTECTED(x, y))
13603 KillPlayer(PLAYERINFO(x, y));
13606 static void KillPlayerUnlessExplosionProtected(int x, int y)
13608 if (!PLAYER_EXPLOSION_PROTECTED(x, y))
13609 KillPlayer(PLAYERINFO(x, y));
13612 void BuryPlayer(struct PlayerInfo *player)
13614 int jx = player->jx, jy = player->jy;
13616 if (!player->active)
13619 PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
13620 PlayLevelSound(jx, jy, SND_GAME_LOSING);
13622 RemovePlayer(player);
13624 player->buried = TRUE;
13626 if (game.all_players_gone)
13627 game.GameOver = TRUE;
13630 void RemovePlayer(struct PlayerInfo *player)
13632 int jx = player->jx, jy = player->jy;
13633 int i, found = FALSE;
13635 player->present = FALSE;
13636 player->active = FALSE;
13638 // required for some CE actions (even if the player is not active anymore)
13639 player->MovPos = 0;
13641 if (!ExplodeField[jx][jy])
13642 StorePlayer[jx][jy] = 0;
13644 if (player->is_moving)
13645 TEST_DrawLevelField(player->last_jx, player->last_jy);
13647 for (i = 0; i < MAX_PLAYERS; i++)
13648 if (stored_player[i].active)
13653 game.all_players_gone = TRUE;
13654 game.GameOver = TRUE;
13657 game.exit_x = game.robot_wheel_x = jx;
13658 game.exit_y = game.robot_wheel_y = jy;
13661 void ExitPlayer(struct PlayerInfo *player)
13663 DrawPlayer(player); // needed here only to cleanup last field
13664 RemovePlayer(player);
13666 if (game.players_still_needed > 0)
13667 game.players_still_needed--;
13670 static void setFieldForSnapping(int x, int y, int element, int direction)
13672 struct ElementInfo *ei = &element_info[element];
13673 int direction_bit = MV_DIR_TO_BIT(direction);
13674 int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
13675 int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
13676 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
13678 Feld[x][y] = EL_ELEMENT_SNAPPING;
13679 MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
13681 ResetGfxAnimation(x, y);
13683 GfxElement[x][y] = element;
13684 GfxAction[x][y] = action;
13685 GfxDir[x][y] = direction;
13686 GfxFrame[x][y] = -1;
13690 =============================================================================
13691 checkDiagonalPushing()
13692 -----------------------------------------------------------------------------
13693 check if diagonal input device direction results in pushing of object
13694 (by checking if the alternative direction is walkable, diggable, ...)
13695 =============================================================================
13698 static boolean checkDiagonalPushing(struct PlayerInfo *player,
13699 int x, int y, int real_dx, int real_dy)
13701 int jx, jy, dx, dy, xx, yy;
13703 if (real_dx == 0 || real_dy == 0) // no diagonal direction => push
13706 // diagonal direction: check alternative direction
13711 xx = jx + (dx == 0 ? real_dx : 0);
13712 yy = jy + (dy == 0 ? real_dy : 0);
13714 return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
13718 =============================================================================
13720 -----------------------------------------------------------------------------
13721 x, y: field next to player (non-diagonal) to try to dig to
13722 real_dx, real_dy: direction as read from input device (can be diagonal)
13723 =============================================================================
13726 static int DigField(struct PlayerInfo *player,
13727 int oldx, int oldy, int x, int y,
13728 int real_dx, int real_dy, int mode)
13730 boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
13731 boolean player_was_pushing = player->is_pushing;
13732 boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
13733 boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
13734 int jx = oldx, jy = oldy;
13735 int dx = x - jx, dy = y - jy;
13736 int nextx = x + dx, nexty = y + dy;
13737 int move_direction = (dx == -1 ? MV_LEFT :
13738 dx == +1 ? MV_RIGHT :
13740 dy == +1 ? MV_DOWN : MV_NONE);
13741 int opposite_direction = MV_DIR_OPPOSITE(move_direction);
13742 int dig_side = MV_DIR_OPPOSITE(move_direction);
13743 int old_element = Feld[jx][jy];
13744 int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
13747 if (is_player) // function can also be called by EL_PENGUIN
13749 if (player->MovPos == 0)
13751 player->is_digging = FALSE;
13752 player->is_collecting = FALSE;
13755 if (player->MovPos == 0) // last pushing move finished
13756 player->is_pushing = FALSE;
13758 if (mode == DF_NO_PUSH) // player just stopped pushing
13760 player->is_switching = FALSE;
13761 player->push_delay = -1;
13763 return MP_NO_ACTION;
13767 if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
13768 old_element = Back[jx][jy];
13770 // in case of element dropped at player position, check background
13771 else if (Back[jx][jy] != EL_EMPTY &&
13772 game.engine_version >= VERSION_IDENT(2,2,0,0))
13773 old_element = Back[jx][jy];
13775 if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
13776 return MP_NO_ACTION; // field has no opening in this direction
13778 if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
13779 return MP_NO_ACTION; // field has no opening in this direction
13781 if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
13785 Feld[jx][jy] = player->artwork_element;
13786 InitMovingField(jx, jy, MV_DOWN);
13787 Store[jx][jy] = EL_ACID;
13788 ContinueMoving(jx, jy);
13789 BuryPlayer(player);
13791 return MP_DONT_RUN_INTO;
13794 if (player_can_move && DONT_RUN_INTO(element))
13796 TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
13798 return MP_DONT_RUN_INTO;
13801 if (IS_MOVING(x, y) || IS_PLAYER(x, y))
13802 return MP_NO_ACTION;
13804 collect_count = element_info[element].collect_count_initial;
13806 if (!is_player && !IS_COLLECTIBLE(element)) // penguin cannot collect it
13807 return MP_NO_ACTION;
13809 if (game.engine_version < VERSION_IDENT(2,2,0,0))
13810 player_can_move = player_can_move_or_snap;
13812 if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
13813 game.engine_version >= VERSION_IDENT(2,2,0,0))
13815 CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
13816 player->index_bit, dig_side);
13817 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13818 player->index_bit, dig_side);
13820 if (element == EL_DC_LANDMINE)
13823 if (Feld[x][y] != element) // field changed by snapping
13826 return MP_NO_ACTION;
13829 if (player->gravity && is_player && !player->is_auto_moving &&
13830 canFallDown(player) && move_direction != MV_DOWN &&
13831 !canMoveToValidFieldWithGravity(jx, jy, move_direction))
13832 return MP_NO_ACTION; // player cannot walk here due to gravity
13834 if (player_can_move &&
13835 IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
13837 int sound_element = SND_ELEMENT(element);
13838 int sound_action = ACTION_WALKING;
13840 if (IS_RND_GATE(element))
13842 if (!player->key[RND_GATE_NR(element)])
13843 return MP_NO_ACTION;
13845 else if (IS_RND_GATE_GRAY(element))
13847 if (!player->key[RND_GATE_GRAY_NR(element)])
13848 return MP_NO_ACTION;
13850 else if (IS_RND_GATE_GRAY_ACTIVE(element))
13852 if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
13853 return MP_NO_ACTION;
13855 else if (element == EL_EXIT_OPEN ||
13856 element == EL_EM_EXIT_OPEN ||
13857 element == EL_EM_EXIT_OPENING ||
13858 element == EL_STEEL_EXIT_OPEN ||
13859 element == EL_EM_STEEL_EXIT_OPEN ||
13860 element == EL_EM_STEEL_EXIT_OPENING ||
13861 element == EL_SP_EXIT_OPEN ||
13862 element == EL_SP_EXIT_OPENING)
13864 sound_action = ACTION_PASSING; // player is passing exit
13866 else if (element == EL_EMPTY)
13868 sound_action = ACTION_MOVING; // nothing to walk on
13871 // play sound from background or player, whatever is available
13872 if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
13873 PlayLevelSoundElementAction(x, y, sound_element, sound_action);
13875 PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
13877 else if (player_can_move &&
13878 IS_PASSABLE(element) && canPassField(x, y, move_direction))
13880 if (!ACCESS_FROM(element, opposite_direction))
13881 return MP_NO_ACTION; // field not accessible from this direction
13883 if (CAN_MOVE(element)) // only fixed elements can be passed!
13884 return MP_NO_ACTION;
13886 if (IS_EM_GATE(element))
13888 if (!player->key[EM_GATE_NR(element)])
13889 return MP_NO_ACTION;
13891 else if (IS_EM_GATE_GRAY(element))
13893 if (!player->key[EM_GATE_GRAY_NR(element)])
13894 return MP_NO_ACTION;
13896 else if (IS_EM_GATE_GRAY_ACTIVE(element))
13898 if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
13899 return MP_NO_ACTION;
13901 else if (IS_EMC_GATE(element))
13903 if (!player->key[EMC_GATE_NR(element)])
13904 return MP_NO_ACTION;
13906 else if (IS_EMC_GATE_GRAY(element))
13908 if (!player->key[EMC_GATE_GRAY_NR(element)])
13909 return MP_NO_ACTION;
13911 else if (IS_EMC_GATE_GRAY_ACTIVE(element))
13913 if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
13914 return MP_NO_ACTION;
13916 else if (element == EL_DC_GATE_WHITE ||
13917 element == EL_DC_GATE_WHITE_GRAY ||
13918 element == EL_DC_GATE_WHITE_GRAY_ACTIVE)
13920 if (player->num_white_keys == 0)
13921 return MP_NO_ACTION;
13923 player->num_white_keys--;
13925 else if (IS_SP_PORT(element))
13927 if (element == EL_SP_GRAVITY_PORT_LEFT ||
13928 element == EL_SP_GRAVITY_PORT_RIGHT ||
13929 element == EL_SP_GRAVITY_PORT_UP ||
13930 element == EL_SP_GRAVITY_PORT_DOWN)
13931 player->gravity = !player->gravity;
13932 else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
13933 element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
13934 element == EL_SP_GRAVITY_ON_PORT_UP ||
13935 element == EL_SP_GRAVITY_ON_PORT_DOWN)
13936 player->gravity = TRUE;
13937 else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
13938 element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
13939 element == EL_SP_GRAVITY_OFF_PORT_UP ||
13940 element == EL_SP_GRAVITY_OFF_PORT_DOWN)
13941 player->gravity = FALSE;
13944 // automatically move to the next field with double speed
13945 player->programmed_action = move_direction;
13947 if (player->move_delay_reset_counter == 0)
13949 player->move_delay_reset_counter = 2; // two double speed steps
13951 DOUBLE_PLAYER_SPEED(player);
13954 PlayLevelSoundAction(x, y, ACTION_PASSING);
13956 else if (player_can_move_or_snap && IS_DIGGABLE(element))
13960 if (mode != DF_SNAP)
13962 GfxElement[x][y] = GFX_ELEMENT(element);
13963 player->is_digging = TRUE;
13966 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
13968 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
13969 player->index_bit, dig_side);
13971 if (mode == DF_SNAP)
13973 if (level.block_snap_field)
13974 setFieldForSnapping(x, y, element, move_direction);
13976 TestIfElementTouchesCustomElement(x, y); // for empty space
13978 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13979 player->index_bit, dig_side);
13982 else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
13986 if (is_player && mode != DF_SNAP)
13988 GfxElement[x][y] = element;
13989 player->is_collecting = TRUE;
13992 if (element == EL_SPEED_PILL)
13994 player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
13996 else if (element == EL_EXTRA_TIME && level.time > 0)
13998 TimeLeft += level.extra_time;
14000 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
14002 DisplayGameControlValues();
14004 else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
14006 player->shield_normal_time_left += level.shield_normal_time;
14007 if (element == EL_SHIELD_DEADLY)
14008 player->shield_deadly_time_left += level.shield_deadly_time;
14010 else if (element == EL_DYNAMITE ||
14011 element == EL_EM_DYNAMITE ||
14012 element == EL_SP_DISK_RED)
14014 if (player->inventory_size < MAX_INVENTORY_SIZE)
14015 player->inventory_element[player->inventory_size++] = element;
14017 DrawGameDoorValues();
14019 else if (element == EL_DYNABOMB_INCREASE_NUMBER)
14021 player->dynabomb_count++;
14022 player->dynabombs_left++;
14024 else if (element == EL_DYNABOMB_INCREASE_SIZE)
14026 player->dynabomb_size++;
14028 else if (element == EL_DYNABOMB_INCREASE_POWER)
14030 player->dynabomb_xl = TRUE;
14032 else if (IS_KEY(element))
14034 player->key[KEY_NR(element)] = TRUE;
14036 DrawGameDoorValues();
14038 else if (element == EL_DC_KEY_WHITE)
14040 player->num_white_keys++;
14042 // display white keys?
14043 // DrawGameDoorValues();
14045 else if (IS_ENVELOPE(element))
14047 player->show_envelope = element;
14049 else if (element == EL_EMC_LENSES)
14051 game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
14053 RedrawAllInvisibleElementsForLenses();
14055 else if (element == EL_EMC_MAGNIFIER)
14057 game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
14059 RedrawAllInvisibleElementsForMagnifier();
14061 else if (IS_DROPPABLE(element) ||
14062 IS_THROWABLE(element)) // can be collected and dropped
14066 if (collect_count == 0)
14067 player->inventory_infinite_element = element;
14069 for (i = 0; i < collect_count; i++)
14070 if (player->inventory_size < MAX_INVENTORY_SIZE)
14071 player->inventory_element[player->inventory_size++] = element;
14073 DrawGameDoorValues();
14075 else if (collect_count > 0)
14077 game.gems_still_needed -= collect_count;
14078 if (game.gems_still_needed < 0)
14079 game.gems_still_needed = 0;
14081 game.snapshot.collected_item = TRUE;
14083 game_panel_controls[GAME_PANEL_GEMS].value = game.gems_still_needed;
14085 DisplayGameControlValues();
14088 RaiseScoreElement(element);
14089 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
14092 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
14093 player->index_bit, dig_side);
14095 if (mode == DF_SNAP)
14097 if (level.block_snap_field)
14098 setFieldForSnapping(x, y, element, move_direction);
14100 TestIfElementTouchesCustomElement(x, y); // for empty space
14102 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14103 player->index_bit, dig_side);
14106 else if (player_can_move_or_snap && IS_PUSHABLE(element))
14108 if (mode == DF_SNAP && element != EL_BD_ROCK)
14109 return MP_NO_ACTION;
14111 if (CAN_FALL(element) && dy)
14112 return MP_NO_ACTION;
14114 if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
14115 !(element == EL_SPRING && level.use_spring_bug))
14116 return MP_NO_ACTION;
14118 if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
14119 ((move_direction & MV_VERTICAL &&
14120 ((element_info[element].move_pattern & MV_LEFT &&
14121 IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
14122 (element_info[element].move_pattern & MV_RIGHT &&
14123 IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
14124 (move_direction & MV_HORIZONTAL &&
14125 ((element_info[element].move_pattern & MV_UP &&
14126 IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
14127 (element_info[element].move_pattern & MV_DOWN &&
14128 IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
14129 return MP_NO_ACTION;
14131 // do not push elements already moving away faster than player
14132 if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
14133 ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
14134 return MP_NO_ACTION;
14136 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
14138 if (player->push_delay_value == -1 || !player_was_pushing)
14139 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14141 else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
14143 if (player->push_delay_value == -1)
14144 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14146 else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
14148 if (!player->is_pushing)
14149 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14152 player->is_pushing = TRUE;
14153 player->is_active = TRUE;
14155 if (!(IN_LEV_FIELD(nextx, nexty) &&
14156 (IS_FREE(nextx, nexty) ||
14157 (IS_SB_ELEMENT(element) &&
14158 Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY) ||
14159 (IS_CUSTOM_ELEMENT(element) &&
14160 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty)))))
14161 return MP_NO_ACTION;
14163 if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
14164 return MP_NO_ACTION;
14166 if (player->push_delay == -1) // new pushing; restart delay
14167 player->push_delay = 0;
14169 if (player->push_delay < player->push_delay_value &&
14170 !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
14171 element != EL_SPRING && element != EL_BALLOON)
14173 // make sure that there is no move delay before next try to push
14174 if (game.engine_version >= VERSION_IDENT(3,0,7,1))
14175 player->move_delay = 0;
14177 return MP_NO_ACTION;
14180 if (IS_CUSTOM_ELEMENT(element) &&
14181 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty))
14183 if (!DigFieldByCE(nextx, nexty, element))
14184 return MP_NO_ACTION;
14187 if (IS_SB_ELEMENT(element))
14189 boolean sokoban_task_solved = FALSE;
14191 if (element == EL_SOKOBAN_FIELD_FULL)
14193 Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
14195 IncrementSokobanFieldsNeeded();
14196 IncrementSokobanObjectsNeeded();
14199 if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
14201 Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
14203 DecrementSokobanFieldsNeeded();
14204 DecrementSokobanObjectsNeeded();
14206 // sokoban object was pushed from empty field to sokoban field
14207 if (Back[x][y] == EL_EMPTY)
14208 sokoban_task_solved = TRUE;
14211 Feld[x][y] = EL_SOKOBAN_OBJECT;
14213 if (Back[x][y] == Back[nextx][nexty])
14214 PlayLevelSoundAction(x, y, ACTION_PUSHING);
14215 else if (Back[x][y] != 0)
14216 PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
14219 PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
14222 if (sokoban_task_solved &&
14223 game.sokoban_fields_still_needed == 0 &&
14224 game.sokoban_objects_still_needed == 0 &&
14225 (game.emulation == EMU_SOKOBAN || level.auto_exit_sokoban))
14227 game.players_still_needed = 0;
14231 PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
14235 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14237 InitMovingField(x, y, move_direction);
14238 GfxAction[x][y] = ACTION_PUSHING;
14240 if (mode == DF_SNAP)
14241 ContinueMoving(x, y);
14243 MovPos[x][y] = (dx != 0 ? dx : dy);
14245 Pushed[x][y] = TRUE;
14246 Pushed[nextx][nexty] = TRUE;
14248 if (game.engine_version < VERSION_IDENT(2,2,0,7))
14249 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14251 player->push_delay_value = -1; // get new value later
14253 // check for element change _after_ element has been pushed
14254 if (game.use_change_when_pushing_bug)
14256 CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
14257 player->index_bit, dig_side);
14258 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
14259 player->index_bit, dig_side);
14262 else if (IS_SWITCHABLE(element))
14264 if (PLAYER_SWITCHING(player, x, y))
14266 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14267 player->index_bit, dig_side);
14272 player->is_switching = TRUE;
14273 player->switch_x = x;
14274 player->switch_y = y;
14276 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
14278 if (element == EL_ROBOT_WHEEL)
14280 Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
14282 game.robot_wheel_x = x;
14283 game.robot_wheel_y = y;
14284 game.robot_wheel_active = TRUE;
14286 TEST_DrawLevelField(x, y);
14288 else if (element == EL_SP_TERMINAL)
14292 SCAN_PLAYFIELD(xx, yy)
14294 if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
14298 else if (Feld[xx][yy] == EL_SP_TERMINAL)
14300 Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
14302 ResetGfxAnimation(xx, yy);
14303 TEST_DrawLevelField(xx, yy);
14307 else if (IS_BELT_SWITCH(element))
14309 ToggleBeltSwitch(x, y);
14311 else if (element == EL_SWITCHGATE_SWITCH_UP ||
14312 element == EL_SWITCHGATE_SWITCH_DOWN ||
14313 element == EL_DC_SWITCHGATE_SWITCH_UP ||
14314 element == EL_DC_SWITCHGATE_SWITCH_DOWN)
14316 ToggleSwitchgateSwitch(x, y);
14318 else if (element == EL_LIGHT_SWITCH ||
14319 element == EL_LIGHT_SWITCH_ACTIVE)
14321 ToggleLightSwitch(x, y);
14323 else if (element == EL_TIMEGATE_SWITCH ||
14324 element == EL_DC_TIMEGATE_SWITCH)
14326 ActivateTimegateSwitch(x, y);
14328 else if (element == EL_BALLOON_SWITCH_LEFT ||
14329 element == EL_BALLOON_SWITCH_RIGHT ||
14330 element == EL_BALLOON_SWITCH_UP ||
14331 element == EL_BALLOON_SWITCH_DOWN ||
14332 element == EL_BALLOON_SWITCH_NONE ||
14333 element == EL_BALLOON_SWITCH_ANY)
14335 game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT ? MV_LEFT :
14336 element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
14337 element == EL_BALLOON_SWITCH_UP ? MV_UP :
14338 element == EL_BALLOON_SWITCH_DOWN ? MV_DOWN :
14339 element == EL_BALLOON_SWITCH_NONE ? MV_NONE :
14342 else if (element == EL_LAMP)
14344 Feld[x][y] = EL_LAMP_ACTIVE;
14345 game.lights_still_needed--;
14347 ResetGfxAnimation(x, y);
14348 TEST_DrawLevelField(x, y);
14350 else if (element == EL_TIME_ORB_FULL)
14352 Feld[x][y] = EL_TIME_ORB_EMPTY;
14354 if (level.time > 0 || level.use_time_orb_bug)
14356 TimeLeft += level.time_orb_time;
14357 game.no_time_limit = FALSE;
14359 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
14361 DisplayGameControlValues();
14364 ResetGfxAnimation(x, y);
14365 TEST_DrawLevelField(x, y);
14367 else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
14368 element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14372 game.ball_active = !game.ball_active;
14374 SCAN_PLAYFIELD(xx, yy)
14376 int e = Feld[xx][yy];
14378 if (game.ball_active)
14380 if (e == EL_EMC_MAGIC_BALL)
14381 CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
14382 else if (e == EL_EMC_MAGIC_BALL_SWITCH)
14383 CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
14387 if (e == EL_EMC_MAGIC_BALL_ACTIVE)
14388 CreateField(xx, yy, EL_EMC_MAGIC_BALL);
14389 else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14390 CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
14395 CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14396 player->index_bit, dig_side);
14398 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14399 player->index_bit, dig_side);
14401 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14402 player->index_bit, dig_side);
14408 if (!PLAYER_SWITCHING(player, x, y))
14410 player->is_switching = TRUE;
14411 player->switch_x = x;
14412 player->switch_y = y;
14414 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
14415 player->index_bit, dig_side);
14416 CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14417 player->index_bit, dig_side);
14419 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
14420 player->index_bit, dig_side);
14421 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14422 player->index_bit, dig_side);
14425 CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
14426 player->index_bit, dig_side);
14427 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14428 player->index_bit, dig_side);
14430 return MP_NO_ACTION;
14433 player->push_delay = -1;
14435 if (is_player) // function can also be called by EL_PENGUIN
14437 if (Feld[x][y] != element) // really digged/collected something
14439 player->is_collecting = !player->is_digging;
14440 player->is_active = TRUE;
14447 static boolean DigFieldByCE(int x, int y, int digging_element)
14449 int element = Feld[x][y];
14451 if (!IS_FREE(x, y))
14453 int action = (IS_DIGGABLE(element) ? ACTION_DIGGING :
14454 IS_COLLECTIBLE(element) ? ACTION_COLLECTING :
14457 // no element can dig solid indestructible elements
14458 if (IS_INDESTRUCTIBLE(element) &&
14459 !IS_DIGGABLE(element) &&
14460 !IS_COLLECTIBLE(element))
14463 if (AmoebaNr[x][y] &&
14464 (element == EL_AMOEBA_FULL ||
14465 element == EL_BD_AMOEBA ||
14466 element == EL_AMOEBA_GROWING))
14468 AmoebaCnt[AmoebaNr[x][y]]--;
14469 AmoebaCnt2[AmoebaNr[x][y]]--;
14472 if (IS_MOVING(x, y))
14473 RemoveMovingField(x, y);
14477 TEST_DrawLevelField(x, y);
14480 // if digged element was about to explode, prevent the explosion
14481 ExplodeField[x][y] = EX_TYPE_NONE;
14483 PlayLevelSoundAction(x, y, action);
14486 Store[x][y] = EL_EMPTY;
14488 // this makes it possible to leave the removed element again
14489 if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element)))
14490 Store[x][y] = element;
14495 static boolean SnapField(struct PlayerInfo *player, int dx, int dy)
14497 int jx = player->jx, jy = player->jy;
14498 int x = jx + dx, y = jy + dy;
14499 int snap_direction = (dx == -1 ? MV_LEFT :
14500 dx == +1 ? MV_RIGHT :
14502 dy == +1 ? MV_DOWN : MV_NONE);
14503 boolean can_continue_snapping = (level.continuous_snapping &&
14504 WasJustFalling[x][y] < CHECK_DELAY_FALLING);
14506 if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
14509 if (!player->active || !IN_LEV_FIELD(x, y))
14517 if (player->MovPos == 0)
14518 player->is_pushing = FALSE;
14520 player->is_snapping = FALSE;
14522 if (player->MovPos == 0)
14524 player->is_moving = FALSE;
14525 player->is_digging = FALSE;
14526 player->is_collecting = FALSE;
14532 // prevent snapping with already pressed snap key when not allowed
14533 if (player->is_snapping && !can_continue_snapping)
14536 player->MovDir = snap_direction;
14538 if (player->MovPos == 0)
14540 player->is_moving = FALSE;
14541 player->is_digging = FALSE;
14542 player->is_collecting = FALSE;
14545 player->is_dropping = FALSE;
14546 player->is_dropping_pressed = FALSE;
14547 player->drop_pressed_delay = 0;
14549 if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
14552 player->is_snapping = TRUE;
14553 player->is_active = TRUE;
14555 if (player->MovPos == 0)
14557 player->is_moving = FALSE;
14558 player->is_digging = FALSE;
14559 player->is_collecting = FALSE;
14562 if (player->MovPos != 0) // prevent graphic bugs in versions < 2.2.0
14563 TEST_DrawLevelField(player->last_jx, player->last_jy);
14565 TEST_DrawLevelField(x, y);
14570 static boolean DropElement(struct PlayerInfo *player)
14572 int old_element, new_element;
14573 int dropx = player->jx, dropy = player->jy;
14574 int drop_direction = player->MovDir;
14575 int drop_side = drop_direction;
14576 int drop_element = get_next_dropped_element(player);
14578 /* do not drop an element on top of another element; when holding drop key
14579 pressed without moving, dropped element must move away before the next
14580 element can be dropped (this is especially important if the next element
14581 is dynamite, which can be placed on background for historical reasons) */
14582 if (PLAYER_DROPPING(player, dropx, dropy) && Feld[dropx][dropy] != EL_EMPTY)
14585 if (IS_THROWABLE(drop_element))
14587 dropx += GET_DX_FROM_DIR(drop_direction);
14588 dropy += GET_DY_FROM_DIR(drop_direction);
14590 if (!IN_LEV_FIELD(dropx, dropy))
14594 old_element = Feld[dropx][dropy]; // old element at dropping position
14595 new_element = drop_element; // default: no change when dropping
14597 // check if player is active, not moving and ready to drop
14598 if (!player->active || player->MovPos || player->drop_delay > 0)
14601 // check if player has anything that can be dropped
14602 if (new_element == EL_UNDEFINED)
14605 // only set if player has anything that can be dropped
14606 player->is_dropping_pressed = TRUE;
14608 // check if drop key was pressed long enough for EM style dynamite
14609 if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
14612 // check if anything can be dropped at the current position
14613 if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
14616 // collected custom elements can only be dropped on empty fields
14617 if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
14620 if (old_element != EL_EMPTY)
14621 Back[dropx][dropy] = old_element; // store old element on this field
14623 ResetGfxAnimation(dropx, dropy);
14624 ResetRandomAnimationValue(dropx, dropy);
14626 if (player->inventory_size > 0 ||
14627 player->inventory_infinite_element != EL_UNDEFINED)
14629 if (player->inventory_size > 0)
14631 player->inventory_size--;
14633 DrawGameDoorValues();
14635 if (new_element == EL_DYNAMITE)
14636 new_element = EL_DYNAMITE_ACTIVE;
14637 else if (new_element == EL_EM_DYNAMITE)
14638 new_element = EL_EM_DYNAMITE_ACTIVE;
14639 else if (new_element == EL_SP_DISK_RED)
14640 new_element = EL_SP_DISK_RED_ACTIVE;
14643 Feld[dropx][dropy] = new_element;
14645 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
14646 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
14647 el2img(Feld[dropx][dropy]), 0);
14649 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
14651 // needed if previous element just changed to "empty" in the last frame
14652 ChangeCount[dropx][dropy] = 0; // allow at least one more change
14654 CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
14655 player->index_bit, drop_side);
14656 CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
14658 player->index_bit, drop_side);
14660 TestIfElementTouchesCustomElement(dropx, dropy);
14662 else // player is dropping a dyna bomb
14664 player->dynabombs_left--;
14666 Feld[dropx][dropy] = new_element;
14668 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
14669 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
14670 el2img(Feld[dropx][dropy]), 0);
14672 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
14675 if (Feld[dropx][dropy] == new_element) // uninitialized unless CE change
14676 InitField_WithBug1(dropx, dropy, FALSE);
14678 new_element = Feld[dropx][dropy]; // element might have changed
14680 if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
14681 element_info[new_element].move_pattern == MV_WHEN_DROPPED)
14683 if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
14684 MovDir[dropx][dropy] = drop_direction;
14686 ChangeCount[dropx][dropy] = 0; // allow at least one more change
14688 // do not cause impact style collision by dropping elements that can fall
14689 CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
14692 player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
14693 player->is_dropping = TRUE;
14695 player->drop_pressed_delay = 0;
14696 player->is_dropping_pressed = FALSE;
14698 player->drop_x = dropx;
14699 player->drop_y = dropy;
14704 // ----------------------------------------------------------------------------
14705 // game sound playing functions
14706 // ----------------------------------------------------------------------------
14708 static int *loop_sound_frame = NULL;
14709 static int *loop_sound_volume = NULL;
14711 void InitPlayLevelSound(void)
14713 int num_sounds = getSoundListSize();
14715 checked_free(loop_sound_frame);
14716 checked_free(loop_sound_volume);
14718 loop_sound_frame = checked_calloc(num_sounds * sizeof(int));
14719 loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
14722 static void PlayLevelSound(int x, int y, int nr)
14724 int sx = SCREENX(x), sy = SCREENY(y);
14725 int volume, stereo_position;
14726 int max_distance = 8;
14727 int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
14729 if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
14730 (!setup.sound_loops && IS_LOOP_SOUND(nr)))
14733 if (!IN_LEV_FIELD(x, y) ||
14734 sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
14735 sy < -max_distance || sy >= SCR_FIELDY + max_distance)
14738 volume = SOUND_MAX_VOLUME;
14740 if (!IN_SCR_FIELD(sx, sy))
14742 int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
14743 int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
14745 volume -= volume * (dx > dy ? dx : dy) / max_distance;
14748 stereo_position = (SOUND_MAX_LEFT +
14749 (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
14750 (SCR_FIELDX + 2 * max_distance));
14752 if (IS_LOOP_SOUND(nr))
14754 /* This assures that quieter loop sounds do not overwrite louder ones,
14755 while restarting sound volume comparison with each new game frame. */
14757 if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
14760 loop_sound_volume[nr] = volume;
14761 loop_sound_frame[nr] = FrameCounter;
14764 PlaySoundExt(nr, volume, stereo_position, type);
14767 static void PlayLevelSoundNearest(int x, int y, int sound_action)
14769 PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
14770 x > LEVELX(BX2) ? LEVELX(BX2) : x,
14771 y < LEVELY(BY1) ? LEVELY(BY1) :
14772 y > LEVELY(BY2) ? LEVELY(BY2) : y,
14776 static void PlayLevelSoundAction(int x, int y, int action)
14778 PlayLevelSoundElementAction(x, y, Feld[x][y], action);
14781 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
14783 int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
14785 if (sound_effect != SND_UNDEFINED)
14786 PlayLevelSound(x, y, sound_effect);
14789 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
14792 int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
14794 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14795 PlayLevelSound(x, y, sound_effect);
14798 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
14800 int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
14802 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14803 PlayLevelSound(x, y, sound_effect);
14806 static void StopLevelSoundActionIfLoop(int x, int y, int action)
14808 int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
14810 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14811 StopSound(sound_effect);
14814 static int getLevelMusicNr(void)
14816 if (levelset.music[level_nr] != MUS_UNDEFINED)
14817 return levelset.music[level_nr]; // from config file
14819 return MAP_NOCONF_MUSIC(level_nr); // from music dir
14822 static void FadeLevelSounds(void)
14827 static void FadeLevelMusic(void)
14829 int music_nr = getLevelMusicNr();
14830 char *curr_music = getCurrentlyPlayingMusicFilename();
14831 char *next_music = getMusicInfoEntryFilename(music_nr);
14833 if (!strEqual(curr_music, next_music))
14837 void FadeLevelSoundsAndMusic(void)
14843 static void PlayLevelMusic(void)
14845 int music_nr = getLevelMusicNr();
14846 char *curr_music = getCurrentlyPlayingMusicFilename();
14847 char *next_music = getMusicInfoEntryFilename(music_nr);
14849 if (!strEqual(curr_music, next_music))
14850 PlayMusicLoop(music_nr);
14853 void PlayLevelSound_EM(int xx, int yy, int element_em, int sample)
14855 int element = (element_em > -1 ? map_element_EM_to_RND_game(element_em) : 0);
14856 int offset = (BorderElement == EL_STEELWALL ? 1 : 0);
14857 int x = xx - 1 - offset;
14858 int y = yy - 1 - offset;
14863 PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
14867 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14871 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14875 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14879 PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
14883 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14887 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14890 case SOUND_android_clone:
14891 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14894 case SOUND_android_move:
14895 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14899 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14903 PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
14907 PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
14910 case SOUND_eater_eat:
14911 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14915 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14918 case SOUND_collect:
14919 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
14922 case SOUND_diamond:
14923 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14927 // !!! CHECK THIS !!!
14929 PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
14931 PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
14935 case SOUND_wonderfall:
14936 PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
14940 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14944 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14948 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14952 PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
14956 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14960 PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
14964 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14968 PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
14971 case SOUND_exit_open:
14972 PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
14975 case SOUND_exit_leave:
14976 PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
14979 case SOUND_dynamite:
14980 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14984 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14988 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
14992 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14996 PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
15000 PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
15004 PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
15008 PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
15013 void PlayLevelSound_SP(int xx, int yy, int element_sp, int action_sp)
15015 int element = map_element_SP_to_RND(element_sp);
15016 int action = map_action_SP_to_RND(action_sp);
15017 int offset = (setup.sp_show_border_elements ? 0 : 1);
15018 int x = xx - offset;
15019 int y = yy - offset;
15021 PlayLevelSoundElementAction(x, y, element, action);
15024 void PlayLevelSound_MM(int xx, int yy, int element_mm, int action_mm)
15026 int element = map_element_MM_to_RND(element_mm);
15027 int action = map_action_MM_to_RND(action_mm);
15029 int x = xx - offset;
15030 int y = yy - offset;
15032 if (!IS_MM_ELEMENT(element))
15033 element = EL_MM_DEFAULT;
15035 PlayLevelSoundElementAction(x, y, element, action);
15038 void PlaySound_MM(int sound_mm)
15040 int sound = map_sound_MM_to_RND(sound_mm);
15042 if (sound == SND_UNDEFINED)
15048 void PlaySoundLoop_MM(int sound_mm)
15050 int sound = map_sound_MM_to_RND(sound_mm);
15052 if (sound == SND_UNDEFINED)
15055 PlaySoundLoop(sound);
15058 void StopSound_MM(int sound_mm)
15060 int sound = map_sound_MM_to_RND(sound_mm);
15062 if (sound == SND_UNDEFINED)
15068 void RaiseScore(int value)
15070 game.score += value;
15072 game_panel_controls[GAME_PANEL_SCORE].value = game.score;
15074 DisplayGameControlValues();
15077 void RaiseScoreElement(int element)
15082 case EL_BD_DIAMOND:
15083 case EL_EMERALD_YELLOW:
15084 case EL_EMERALD_RED:
15085 case EL_EMERALD_PURPLE:
15086 case EL_SP_INFOTRON:
15087 RaiseScore(level.score[SC_EMERALD]);
15090 RaiseScore(level.score[SC_DIAMOND]);
15093 RaiseScore(level.score[SC_CRYSTAL]);
15096 RaiseScore(level.score[SC_PEARL]);
15099 case EL_BD_BUTTERFLY:
15100 case EL_SP_ELECTRON:
15101 RaiseScore(level.score[SC_BUG]);
15104 case EL_BD_FIREFLY:
15105 case EL_SP_SNIKSNAK:
15106 RaiseScore(level.score[SC_SPACESHIP]);
15109 case EL_DARK_YAMYAM:
15110 RaiseScore(level.score[SC_YAMYAM]);
15113 RaiseScore(level.score[SC_ROBOT]);
15116 RaiseScore(level.score[SC_PACMAN]);
15119 RaiseScore(level.score[SC_NUT]);
15122 case EL_EM_DYNAMITE:
15123 case EL_SP_DISK_RED:
15124 case EL_DYNABOMB_INCREASE_NUMBER:
15125 case EL_DYNABOMB_INCREASE_SIZE:
15126 case EL_DYNABOMB_INCREASE_POWER:
15127 RaiseScore(level.score[SC_DYNAMITE]);
15129 case EL_SHIELD_NORMAL:
15130 case EL_SHIELD_DEADLY:
15131 RaiseScore(level.score[SC_SHIELD]);
15133 case EL_EXTRA_TIME:
15134 RaiseScore(level.extra_time_score);
15148 case EL_DC_KEY_WHITE:
15149 RaiseScore(level.score[SC_KEY]);
15152 RaiseScore(element_info[element].collect_score);
15157 void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message)
15159 if (skip_request || Request(message, REQ_ASK | REQ_STAY_CLOSED))
15161 // closing door required in case of envelope style request dialogs
15164 // prevent short reactivation of overlay buttons while closing door
15165 SetOverlayActive(FALSE);
15167 CloseDoor(DOOR_CLOSE_1);
15170 if (network.enabled)
15171 SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
15175 FadeSkipNextFadeIn();
15177 SetGameStatus(GAME_MODE_MAIN);
15182 else // continue playing the game
15184 if (tape.playing && tape.deactivate_display)
15185 TapeDeactivateDisplayOff(TRUE);
15187 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
15189 if (tape.playing && tape.deactivate_display)
15190 TapeDeactivateDisplayOn();
15194 void RequestQuitGame(boolean ask_if_really_quit)
15196 boolean quick_quit = (!ask_if_really_quit || level_editor_test_game);
15197 boolean skip_request = game.all_players_gone || quick_quit;
15199 RequestQuitGameExt(skip_request, quick_quit,
15200 "Do you really want to quit the game?");
15203 void RequestRestartGame(char *message)
15205 game.restart_game_message = NULL;
15207 boolean has_started_game = hasStartedNetworkGame();
15208 int request_mode = (has_started_game ? REQ_ASK : REQ_CONFIRM);
15210 if (Request(message, request_mode | REQ_STAY_CLOSED) && has_started_game)
15212 StartGameActions(network.enabled, setup.autorecord, level.random_seed);
15216 SetGameStatus(GAME_MODE_MAIN);
15222 void CheckGameOver(void)
15224 static boolean last_game_over = FALSE;
15225 static int game_over_delay = 0;
15226 int game_over_delay_value = 50;
15227 boolean game_over = checkGameFailed();
15229 // do not handle game over if request dialog is already active
15230 if (game.request_active)
15233 // do not ask to play again if game was never actually played
15234 if (!game.GamePlayed)
15239 last_game_over = FALSE;
15240 game_over_delay = game_over_delay_value;
15245 if (game_over_delay > 0)
15252 if (last_game_over != game_over)
15253 game.restart_game_message = (hasStartedNetworkGame() ?
15254 "Game over! Play it again?" :
15257 last_game_over = game_over;
15260 boolean checkGameSolved(void)
15262 // set for all game engines if level was solved
15263 return game.LevelSolved_GameEnd;
15266 boolean checkGameFailed(void)
15268 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15269 return (game_em.game_over && !game_em.level_solved);
15270 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15271 return (game_sp.game_over && !game_sp.level_solved);
15272 else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15273 return (game_mm.game_over && !game_mm.level_solved);
15274 else // GAME_ENGINE_TYPE_RND
15275 return (game.GameOver && !game.LevelSolved);
15278 boolean checkGameEnded(void)
15280 return (checkGameSolved() || checkGameFailed());
15284 // ----------------------------------------------------------------------------
15285 // random generator functions
15286 // ----------------------------------------------------------------------------
15288 unsigned int InitEngineRandom_RND(int seed)
15290 game.num_random_calls = 0;
15292 return InitEngineRandom(seed);
15295 unsigned int RND(int max)
15299 game.num_random_calls++;
15301 return GetEngineRandom(max);
15308 // ----------------------------------------------------------------------------
15309 // game engine snapshot handling functions
15310 // ----------------------------------------------------------------------------
15312 struct EngineSnapshotInfo
15314 // runtime values for custom element collect score
15315 int collect_score[NUM_CUSTOM_ELEMENTS];
15317 // runtime values for group element choice position
15318 int choice_pos[NUM_GROUP_ELEMENTS];
15320 // runtime values for belt position animations
15321 int belt_graphic[4][NUM_BELT_PARTS];
15322 int belt_anim_mode[4][NUM_BELT_PARTS];
15325 static struct EngineSnapshotInfo engine_snapshot_rnd;
15326 static char *snapshot_level_identifier = NULL;
15327 static int snapshot_level_nr = -1;
15329 static void SaveEngineSnapshotValues_RND(void)
15331 static int belt_base_active_element[4] =
15333 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
15334 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
15335 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
15336 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
15340 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
15342 int element = EL_CUSTOM_START + i;
15344 engine_snapshot_rnd.collect_score[i] = element_info[element].collect_score;
15347 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15349 int element = EL_GROUP_START + i;
15351 engine_snapshot_rnd.choice_pos[i] = element_info[element].group->choice_pos;
15354 for (i = 0; i < 4; i++)
15356 for (j = 0; j < NUM_BELT_PARTS; j++)
15358 int element = belt_base_active_element[i] + j;
15359 int graphic = el2img(element);
15360 int anim_mode = graphic_info[graphic].anim_mode;
15362 engine_snapshot_rnd.belt_graphic[i][j] = graphic;
15363 engine_snapshot_rnd.belt_anim_mode[i][j] = anim_mode;
15368 static void LoadEngineSnapshotValues_RND(void)
15370 unsigned int num_random_calls = game.num_random_calls;
15373 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
15375 int element = EL_CUSTOM_START + i;
15377 element_info[element].collect_score = engine_snapshot_rnd.collect_score[i];
15380 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15382 int element = EL_GROUP_START + i;
15384 element_info[element].group->choice_pos = engine_snapshot_rnd.choice_pos[i];
15387 for (i = 0; i < 4; i++)
15389 for (j = 0; j < NUM_BELT_PARTS; j++)
15391 int graphic = engine_snapshot_rnd.belt_graphic[i][j];
15392 int anim_mode = engine_snapshot_rnd.belt_anim_mode[i][j];
15394 graphic_info[graphic].anim_mode = anim_mode;
15398 if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15400 InitRND(tape.random_seed);
15401 for (i = 0; i < num_random_calls; i++)
15405 if (game.num_random_calls != num_random_calls)
15407 Error(ERR_INFO, "number of random calls out of sync");
15408 Error(ERR_INFO, "number of random calls should be %d", num_random_calls);
15409 Error(ERR_INFO, "number of random calls is %d", game.num_random_calls);
15410 Error(ERR_EXIT, "this should not happen -- please debug");
15414 void FreeEngineSnapshotSingle(void)
15416 FreeSnapshotSingle();
15418 setString(&snapshot_level_identifier, NULL);
15419 snapshot_level_nr = -1;
15422 void FreeEngineSnapshotList(void)
15424 FreeSnapshotList();
15427 static ListNode *SaveEngineSnapshotBuffers(void)
15429 ListNode *buffers = NULL;
15431 // copy some special values to a structure better suited for the snapshot
15433 if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15434 SaveEngineSnapshotValues_RND();
15435 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15436 SaveEngineSnapshotValues_EM();
15437 if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15438 SaveEngineSnapshotValues_SP(&buffers);
15439 if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15440 SaveEngineSnapshotValues_MM(&buffers);
15442 // save values stored in special snapshot structure
15444 if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15445 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
15446 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15447 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
15448 if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15449 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_sp));
15450 if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15451 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_mm));
15453 // save further RND engine values
15455 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(stored_player));
15456 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(game));
15457 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(tape));
15459 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(FrameCounter));
15460 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeFrames));
15461 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimePlayed));
15462 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeLeft));
15463 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TapeTime));
15465 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovDir));
15466 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovPos));
15467 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenGfxPos));
15469 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScrollStepSize));
15471 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt));
15472 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2));
15474 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Feld));
15475 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovPos));
15476 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDir));
15477 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDelay));
15478 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeDelay));
15479 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangePage));
15480 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CustomValue));
15481 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store));
15482 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store2));
15483 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(StorePlayer));
15484 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Back));
15485 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaNr));
15486 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustMoving));
15487 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustFalling));
15488 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckCollision));
15489 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckImpact));
15490 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Stop));
15491 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Pushed));
15493 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeCount));
15494 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeEvent));
15496 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodePhase));
15497 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeDelay));
15498 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeField));
15500 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(RunnerVisit));
15501 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(PlayerVisit));
15503 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxFrame));
15504 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxRandom));
15505 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxElement));
15506 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxAction));
15507 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxDir));
15509 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_x));
15510 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_y));
15513 ListNode *node = engine_snapshot_list_rnd;
15516 while (node != NULL)
15518 num_bytes += ((struct EngineSnapshotNodeInfo *)node->content)->size;
15523 printf("::: size of engine snapshot: %d bytes\n", num_bytes);
15529 void SaveEngineSnapshotSingle(void)
15531 ListNode *buffers = SaveEngineSnapshotBuffers();
15533 // finally save all snapshot buffers to single snapshot
15534 SaveSnapshotSingle(buffers);
15536 // save level identification information
15537 setString(&snapshot_level_identifier, leveldir_current->identifier);
15538 snapshot_level_nr = level_nr;
15541 boolean CheckSaveEngineSnapshotToList(void)
15543 boolean save_snapshot =
15544 ((game.snapshot.mode == SNAPSHOT_MODE_EVERY_STEP) ||
15545 (game.snapshot.mode == SNAPSHOT_MODE_EVERY_MOVE &&
15546 game.snapshot.changed_action) ||
15547 (game.snapshot.mode == SNAPSHOT_MODE_EVERY_COLLECT &&
15548 game.snapshot.collected_item));
15550 game.snapshot.changed_action = FALSE;
15551 game.snapshot.collected_item = FALSE;
15552 game.snapshot.save_snapshot = save_snapshot;
15554 return save_snapshot;
15557 void SaveEngineSnapshotToList(void)
15559 if (game.snapshot.mode == SNAPSHOT_MODE_OFF ||
15563 ListNode *buffers = SaveEngineSnapshotBuffers();
15565 // finally save all snapshot buffers to snapshot list
15566 SaveSnapshotToList(buffers);
15569 void SaveEngineSnapshotToListInitial(void)
15571 FreeEngineSnapshotList();
15573 SaveEngineSnapshotToList();
15576 static void LoadEngineSnapshotValues(void)
15578 // restore special values from snapshot structure
15580 if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15581 LoadEngineSnapshotValues_RND();
15582 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15583 LoadEngineSnapshotValues_EM();
15584 if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15585 LoadEngineSnapshotValues_SP();
15586 if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15587 LoadEngineSnapshotValues_MM();
15590 void LoadEngineSnapshotSingle(void)
15592 LoadSnapshotSingle();
15594 LoadEngineSnapshotValues();
15597 static void LoadEngineSnapshot_Undo(int steps)
15599 LoadSnapshotFromList_Older(steps);
15601 LoadEngineSnapshotValues();
15604 static void LoadEngineSnapshot_Redo(int steps)
15606 LoadSnapshotFromList_Newer(steps);
15608 LoadEngineSnapshotValues();
15611 boolean CheckEngineSnapshotSingle(void)
15613 return (strEqual(snapshot_level_identifier, leveldir_current->identifier) &&
15614 snapshot_level_nr == level_nr);
15617 boolean CheckEngineSnapshotList(void)
15619 return CheckSnapshotList();
15623 // ---------- new game button stuff -------------------------------------------
15630 boolean *setup_value;
15631 boolean allowed_on_tape;
15632 boolean is_touch_button;
15634 } gamebutton_info[NUM_GAME_BUTTONS] =
15637 IMG_GFX_GAME_BUTTON_STOP, &game.button.stop,
15638 GAME_CTRL_ID_STOP, NULL,
15639 TRUE, FALSE, "stop game"
15642 IMG_GFX_GAME_BUTTON_PAUSE, &game.button.pause,
15643 GAME_CTRL_ID_PAUSE, NULL,
15644 TRUE, FALSE, "pause game"
15647 IMG_GFX_GAME_BUTTON_PLAY, &game.button.play,
15648 GAME_CTRL_ID_PLAY, NULL,
15649 TRUE, FALSE, "play game"
15652 IMG_GFX_GAME_BUTTON_UNDO, &game.button.undo,
15653 GAME_CTRL_ID_UNDO, NULL,
15654 TRUE, FALSE, "undo step"
15657 IMG_GFX_GAME_BUTTON_REDO, &game.button.redo,
15658 GAME_CTRL_ID_REDO, NULL,
15659 TRUE, FALSE, "redo step"
15662 IMG_GFX_GAME_BUTTON_SAVE, &game.button.save,
15663 GAME_CTRL_ID_SAVE, NULL,
15664 TRUE, FALSE, "save game"
15667 IMG_GFX_GAME_BUTTON_PAUSE2, &game.button.pause2,
15668 GAME_CTRL_ID_PAUSE2, NULL,
15669 TRUE, FALSE, "pause game"
15672 IMG_GFX_GAME_BUTTON_LOAD, &game.button.load,
15673 GAME_CTRL_ID_LOAD, NULL,
15674 TRUE, FALSE, "load game"
15677 IMG_GFX_GAME_BUTTON_PANEL_STOP, &game.button.panel_stop,
15678 GAME_CTRL_ID_PANEL_STOP, NULL,
15679 FALSE, FALSE, "stop game"
15682 IMG_GFX_GAME_BUTTON_PANEL_PAUSE, &game.button.panel_pause,
15683 GAME_CTRL_ID_PANEL_PAUSE, NULL,
15684 FALSE, FALSE, "pause game"
15687 IMG_GFX_GAME_BUTTON_PANEL_PLAY, &game.button.panel_play,
15688 GAME_CTRL_ID_PANEL_PLAY, NULL,
15689 FALSE, FALSE, "play game"
15692 IMG_GFX_GAME_BUTTON_TOUCH_STOP, &game.button.touch_stop,
15693 GAME_CTRL_ID_TOUCH_STOP, NULL,
15694 FALSE, TRUE, "stop game"
15697 IMG_GFX_GAME_BUTTON_TOUCH_PAUSE, &game.button.touch_pause,
15698 GAME_CTRL_ID_TOUCH_PAUSE, NULL,
15699 FALSE, TRUE, "pause game"
15702 IMG_GFX_GAME_BUTTON_SOUND_MUSIC, &game.button.sound_music,
15703 SOUND_CTRL_ID_MUSIC, &setup.sound_music,
15704 TRUE, FALSE, "background music on/off"
15707 IMG_GFX_GAME_BUTTON_SOUND_LOOPS, &game.button.sound_loops,
15708 SOUND_CTRL_ID_LOOPS, &setup.sound_loops,
15709 TRUE, FALSE, "sound loops on/off"
15712 IMG_GFX_GAME_BUTTON_SOUND_SIMPLE, &game.button.sound_simple,
15713 SOUND_CTRL_ID_SIMPLE, &setup.sound_simple,
15714 TRUE, FALSE, "normal sounds on/off"
15717 IMG_GFX_GAME_BUTTON_PANEL_SOUND_MUSIC, &game.button.panel_sound_music,
15718 SOUND_CTRL_ID_PANEL_MUSIC, &setup.sound_music,
15719 FALSE, FALSE, "background music on/off"
15722 IMG_GFX_GAME_BUTTON_PANEL_SOUND_LOOPS, &game.button.panel_sound_loops,
15723 SOUND_CTRL_ID_PANEL_LOOPS, &setup.sound_loops,
15724 FALSE, FALSE, "sound loops on/off"
15727 IMG_GFX_GAME_BUTTON_PANEL_SOUND_SIMPLE, &game.button.panel_sound_simple,
15728 SOUND_CTRL_ID_PANEL_SIMPLE, &setup.sound_simple,
15729 FALSE, FALSE, "normal sounds on/off"
15733 void CreateGameButtons(void)
15737 for (i = 0; i < NUM_GAME_BUTTONS; i++)
15739 int graphic = gamebutton_info[i].graphic;
15740 struct GraphicInfo *gfx = &graphic_info[graphic];
15741 struct XY *pos = gamebutton_info[i].pos;
15742 struct GadgetInfo *gi;
15745 unsigned int event_mask;
15746 boolean is_touch_button = gamebutton_info[i].is_touch_button;
15747 boolean allowed_on_tape = gamebutton_info[i].allowed_on_tape;
15748 boolean on_tape = (tape.show_game_buttons && allowed_on_tape);
15749 int base_x = (is_touch_button ? 0 : on_tape ? VX : DX);
15750 int base_y = (is_touch_button ? 0 : on_tape ? VY : DY);
15751 int gd_x = gfx->src_x;
15752 int gd_y = gfx->src_y;
15753 int gd_xp = gfx->src_x + gfx->pressed_xoffset;
15754 int gd_yp = gfx->src_y + gfx->pressed_yoffset;
15755 int gd_xa = gfx->src_x + gfx->active_xoffset;
15756 int gd_ya = gfx->src_y + gfx->active_yoffset;
15757 int gd_xap = gfx->src_x + gfx->active_xoffset + gfx->pressed_xoffset;
15758 int gd_yap = gfx->src_y + gfx->active_yoffset + gfx->pressed_yoffset;
15759 int x = (is_touch_button ? pos->x : GDI_ACTIVE_POS(pos->x));
15760 int y = (is_touch_button ? pos->y : GDI_ACTIVE_POS(pos->y));
15763 if (gfx->bitmap == NULL)
15765 game_gadget[id] = NULL;
15770 if (id == GAME_CTRL_ID_STOP ||
15771 id == GAME_CTRL_ID_PANEL_STOP ||
15772 id == GAME_CTRL_ID_TOUCH_STOP ||
15773 id == GAME_CTRL_ID_PLAY ||
15774 id == GAME_CTRL_ID_PANEL_PLAY ||
15775 id == GAME_CTRL_ID_SAVE ||
15776 id == GAME_CTRL_ID_LOAD)
15778 button_type = GD_TYPE_NORMAL_BUTTON;
15780 event_mask = GD_EVENT_RELEASED;
15782 else if (id == GAME_CTRL_ID_UNDO ||
15783 id == GAME_CTRL_ID_REDO)
15785 button_type = GD_TYPE_NORMAL_BUTTON;
15787 event_mask = GD_EVENT_PRESSED | GD_EVENT_REPEATED;
15791 button_type = GD_TYPE_CHECK_BUTTON;
15792 checked = (gamebutton_info[i].setup_value != NULL ?
15793 *gamebutton_info[i].setup_value : FALSE);
15794 event_mask = GD_EVENT_PRESSED;
15797 gi = CreateGadget(GDI_CUSTOM_ID, id,
15798 GDI_IMAGE_ID, graphic,
15799 GDI_INFO_TEXT, gamebutton_info[i].infotext,
15802 GDI_WIDTH, gfx->width,
15803 GDI_HEIGHT, gfx->height,
15804 GDI_TYPE, button_type,
15805 GDI_STATE, GD_BUTTON_UNPRESSED,
15806 GDI_CHECKED, checked,
15807 GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
15808 GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
15809 GDI_ALT_DESIGN_UNPRESSED, gfx->bitmap, gd_xa, gd_ya,
15810 GDI_ALT_DESIGN_PRESSED, gfx->bitmap, gd_xap, gd_yap,
15811 GDI_DIRECT_DRAW, FALSE,
15812 GDI_OVERLAY_TOUCH_BUTTON, is_touch_button,
15813 GDI_EVENT_MASK, event_mask,
15814 GDI_CALLBACK_ACTION, HandleGameButtons,
15818 Error(ERR_EXIT, "cannot create gadget");
15820 game_gadget[id] = gi;
15824 void FreeGameButtons(void)
15828 for (i = 0; i < NUM_GAME_BUTTONS; i++)
15829 FreeGadget(game_gadget[i]);
15832 static void UnmapGameButtonsAtSamePosition(int id)
15836 for (i = 0; i < NUM_GAME_BUTTONS; i++)
15838 gamebutton_info[i].pos->x == gamebutton_info[id].pos->x &&
15839 gamebutton_info[i].pos->y == gamebutton_info[id].pos->y)
15840 UnmapGadget(game_gadget[i]);
15843 static void UnmapGameButtonsAtSamePosition_All(void)
15845 if (setup.show_snapshot_buttons)
15847 UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_SAVE);
15848 UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE2);
15849 UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_LOAD);
15853 UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_STOP);
15854 UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE);
15855 UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PLAY);
15857 UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_STOP);
15858 UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_PAUSE);
15859 UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_PLAY);
15863 static void MapGameButtonsAtSamePosition(int id)
15867 for (i = 0; i < NUM_GAME_BUTTONS; i++)
15869 gamebutton_info[i].pos->x == gamebutton_info[id].pos->x &&
15870 gamebutton_info[i].pos->y == gamebutton_info[id].pos->y)
15871 MapGadget(game_gadget[i]);
15873 UnmapGameButtonsAtSamePosition_All();
15876 void MapUndoRedoButtons(void)
15878 UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
15879 UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
15881 MapGadget(game_gadget[GAME_CTRL_ID_UNDO]);
15882 MapGadget(game_gadget[GAME_CTRL_ID_REDO]);
15885 void UnmapUndoRedoButtons(void)
15887 UnmapGadget(game_gadget[GAME_CTRL_ID_UNDO]);
15888 UnmapGadget(game_gadget[GAME_CTRL_ID_REDO]);
15890 MapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
15891 MapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
15894 void ModifyPauseButtons(void)
15898 GAME_CTRL_ID_PAUSE,
15899 GAME_CTRL_ID_PAUSE2,
15900 GAME_CTRL_ID_PANEL_PAUSE,
15901 GAME_CTRL_ID_TOUCH_PAUSE,
15906 for (i = 0; ids[i] > -1; i++)
15907 ModifyGadget(game_gadget[ids[i]], GDI_CHECKED, tape.pausing, GDI_END);
15910 static void MapGameButtonsExt(boolean on_tape)
15914 for (i = 0; i < NUM_GAME_BUTTONS; i++)
15915 if ((!on_tape || gamebutton_info[i].allowed_on_tape) &&
15916 i != GAME_CTRL_ID_UNDO &&
15917 i != GAME_CTRL_ID_REDO)
15918 MapGadget(game_gadget[i]);
15920 UnmapGameButtonsAtSamePosition_All();
15922 RedrawGameButtons();
15925 static void UnmapGameButtonsExt(boolean on_tape)
15929 for (i = 0; i < NUM_GAME_BUTTONS; i++)
15930 if (!on_tape || gamebutton_info[i].allowed_on_tape)
15931 UnmapGadget(game_gadget[i]);
15934 static void RedrawGameButtonsExt(boolean on_tape)
15938 for (i = 0; i < NUM_GAME_BUTTONS; i++)
15939 if (!on_tape || gamebutton_info[i].allowed_on_tape)
15940 RedrawGadget(game_gadget[i]);
15943 static void SetGadgetState(struct GadgetInfo *gi, boolean state)
15948 gi->checked = state;
15951 static void RedrawSoundButtonGadget(int id)
15953 int id2 = (id == SOUND_CTRL_ID_MUSIC ? SOUND_CTRL_ID_PANEL_MUSIC :
15954 id == SOUND_CTRL_ID_LOOPS ? SOUND_CTRL_ID_PANEL_LOOPS :
15955 id == SOUND_CTRL_ID_SIMPLE ? SOUND_CTRL_ID_PANEL_SIMPLE :
15956 id == SOUND_CTRL_ID_PANEL_MUSIC ? SOUND_CTRL_ID_MUSIC :
15957 id == SOUND_CTRL_ID_PANEL_LOOPS ? SOUND_CTRL_ID_LOOPS :
15958 id == SOUND_CTRL_ID_PANEL_SIMPLE ? SOUND_CTRL_ID_SIMPLE :
15961 SetGadgetState(game_gadget[id2], *gamebutton_info[id2].setup_value);
15962 RedrawGadget(game_gadget[id2]);
15965 void MapGameButtons(void)
15967 MapGameButtonsExt(FALSE);
15970 void UnmapGameButtons(void)
15972 UnmapGameButtonsExt(FALSE);
15975 void RedrawGameButtons(void)
15977 RedrawGameButtonsExt(FALSE);
15980 void MapGameButtonsOnTape(void)
15982 MapGameButtonsExt(TRUE);
15985 void UnmapGameButtonsOnTape(void)
15987 UnmapGameButtonsExt(TRUE);
15990 void RedrawGameButtonsOnTape(void)
15992 RedrawGameButtonsExt(TRUE);
15995 static void GameUndoRedoExt(void)
15997 ClearPlayerAction();
15999 tape.pausing = TRUE;
16002 UpdateAndDisplayGameControlValues();
16004 DrawCompleteVideoDisplay();
16005 DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
16006 DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
16007 DrawVideoDisplay(VIDEO_STATE_1STEP(tape.single_step), 0);
16012 static void GameUndo(int steps)
16014 if (!CheckEngineSnapshotList())
16017 LoadEngineSnapshot_Undo(steps);
16022 static void GameRedo(int steps)
16024 if (!CheckEngineSnapshotList())
16027 LoadEngineSnapshot_Redo(steps);
16032 static void HandleGameButtonsExt(int id, int button)
16034 static boolean game_undo_executed = FALSE;
16035 int steps = BUTTON_STEPSIZE(button);
16036 boolean handle_game_buttons =
16037 (game_status == GAME_MODE_PLAYING ||
16038 (game_status == GAME_MODE_MAIN && tape.show_game_buttons));
16040 if (!handle_game_buttons)
16045 case GAME_CTRL_ID_STOP:
16046 case GAME_CTRL_ID_PANEL_STOP:
16047 case GAME_CTRL_ID_TOUCH_STOP:
16048 if (game_status == GAME_MODE_MAIN)
16054 RequestQuitGame(TRUE);
16058 case GAME_CTRL_ID_PAUSE:
16059 case GAME_CTRL_ID_PAUSE2:
16060 case GAME_CTRL_ID_PANEL_PAUSE:
16061 case GAME_CTRL_ID_TOUCH_PAUSE:
16062 if (network.enabled && game_status == GAME_MODE_PLAYING)
16065 SendToServer_ContinuePlaying();
16067 SendToServer_PausePlaying();
16070 TapeTogglePause(TAPE_TOGGLE_MANUAL);
16072 game_undo_executed = FALSE;
16076 case GAME_CTRL_ID_PLAY:
16077 case GAME_CTRL_ID_PANEL_PLAY:
16078 if (game_status == GAME_MODE_MAIN)
16080 StartGameActions(network.enabled, setup.autorecord, level.random_seed);
16082 else if (tape.pausing)
16084 if (network.enabled)
16085 SendToServer_ContinuePlaying();
16087 TapeTogglePause(TAPE_TOGGLE_MANUAL | TAPE_TOGGLE_PLAY_PAUSE);
16091 case GAME_CTRL_ID_UNDO:
16092 // Important: When using "save snapshot when collecting an item" mode,
16093 // load last (current) snapshot for first "undo" after pressing "pause"
16094 // (else the last-but-one snapshot would be loaded, because the snapshot
16095 // pointer already points to the last snapshot when pressing "pause",
16096 // which is fine for "every step/move" mode, but not for "every collect")
16097 if (game.snapshot.mode == SNAPSHOT_MODE_EVERY_COLLECT &&
16098 !game_undo_executed)
16101 game_undo_executed = TRUE;
16106 case GAME_CTRL_ID_REDO:
16110 case GAME_CTRL_ID_SAVE:
16114 case GAME_CTRL_ID_LOAD:
16118 case SOUND_CTRL_ID_MUSIC:
16119 case SOUND_CTRL_ID_PANEL_MUSIC:
16120 if (setup.sound_music)
16122 setup.sound_music = FALSE;
16126 else if (audio.music_available)
16128 setup.sound = setup.sound_music = TRUE;
16130 SetAudioMode(setup.sound);
16132 if (game_status == GAME_MODE_PLAYING)
16136 RedrawSoundButtonGadget(id);
16140 case SOUND_CTRL_ID_LOOPS:
16141 case SOUND_CTRL_ID_PANEL_LOOPS:
16142 if (setup.sound_loops)
16143 setup.sound_loops = FALSE;
16144 else if (audio.loops_available)
16146 setup.sound = setup.sound_loops = TRUE;
16148 SetAudioMode(setup.sound);
16151 RedrawSoundButtonGadget(id);
16155 case SOUND_CTRL_ID_SIMPLE:
16156 case SOUND_CTRL_ID_PANEL_SIMPLE:
16157 if (setup.sound_simple)
16158 setup.sound_simple = FALSE;
16159 else if (audio.sound_available)
16161 setup.sound = setup.sound_simple = TRUE;
16163 SetAudioMode(setup.sound);
16166 RedrawSoundButtonGadget(id);
16175 static void HandleGameButtons(struct GadgetInfo *gi)
16177 HandleGameButtonsExt(gi->custom_id, gi->event.button);
16180 void HandleSoundButtonKeys(Key key)
16182 if (key == setup.shortcut.sound_simple)
16183 ClickOnGadget(game_gadget[SOUND_CTRL_ID_SIMPLE], MB_LEFTBUTTON);
16184 else if (key == setup.shortcut.sound_loops)
16185 ClickOnGadget(game_gadget[SOUND_CTRL_ID_LOOPS], MB_LEFTBUTTON);
16186 else if (key == setup.shortcut.sound_music)
16187 ClickOnGadget(game_gadget[SOUND_CTRL_ID_MUSIC], MB_LEFTBUTTON);